File indexing completed on 2025-03-23 13:48:12
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org> 0006 SPDX-FileCopyrightText: 2019 Roman Gilg <subdiff@gmail.com> 0007 0008 SPDX-License-Identifier: GPL-2.0-or-later 0009 */ 0010 #include "kwin_wayland_test.h" 0011 0012 #include "core/output.h" 0013 #include "core/outputbackend.h" 0014 #include "wayland/seat_interface.h" 0015 #include "wayland_server.h" 0016 #include "window.h" 0017 #include "workspace.h" 0018 #include "xwayland/databridge.h" 0019 0020 #include <QProcess> 0021 #include <QProcessEnvironment> 0022 0023 using namespace KWin; 0024 0025 static const QString s_socketName = QStringLiteral("wayland_test_kwin_xwayland_selections-0"); 0026 0027 struct ProcessKillBeforeDeleter 0028 { 0029 void operator()(QProcess *pointer) 0030 { 0031 if (pointer) { 0032 pointer->kill(); 0033 } 0034 delete pointer; 0035 } 0036 }; 0037 0038 class XwaylandSelectionsTest : public QObject 0039 { 0040 Q_OBJECT 0041 private Q_SLOTS: 0042 void initTestCase(); 0043 void testSync_data(); 0044 void testSync(); 0045 }; 0046 0047 void XwaylandSelectionsTest::initTestCase() 0048 { 0049 // QSKIP("Skipped as it fails for unknown reasons on build.kde.org"); 0050 qRegisterMetaType<KWin::Window *>(); 0051 qRegisterMetaType<QProcess::ExitStatus>(); 0052 QSignalSpy applicationStartedSpy(kwinApp(), &Application::started); 0053 // QSignalSpy clipboardSyncDevicedCreated{waylandServer(), &WaylandServer::xclipboardSyncDataDeviceCreated}; 0054 // QVERIFY(clipboardSyncDevicedCreated.isValid()); 0055 QVERIFY(waylandServer()->init(s_socketName)); 0056 QMetaObject::invokeMethod(kwinApp()->outputBackend(), "setVirtualOutputs", Qt::DirectConnection, Q_ARG(QVector<QRect>, QVector<QRect>() << QRect(0, 0, 1280, 1024) << QRect(1280, 0, 1280, 1024))); 0057 0058 kwinApp()->start(); 0059 QVERIFY(applicationStartedSpy.wait()); 0060 const auto outputs = workspace()->outputs(); 0061 QCOMPARE(outputs.count(), 2); 0062 QCOMPARE(outputs[0]->geometry(), QRect(0, 0, 1280, 1024)); 0063 QCOMPARE(outputs[1]->geometry(), QRect(1280, 0, 1280, 1024)); 0064 // // wait till the xclipboard sync data device is created 0065 // if (clipboardSyncDevicedCreated.empty()) { 0066 // QVERIFY(clipboardSyncDevicedCreated.wait()); 0067 // } 0068 } 0069 0070 void XwaylandSelectionsTest::testSync_data() 0071 { 0072 QTest::addColumn<QString>("copyPlatform"); 0073 QTest::addColumn<QString>("pastePlatform"); 0074 0075 QTest::newRow("x11->wayland") << QStringLiteral("xcb") << QStringLiteral("wayland"); 0076 QTest::newRow("wayland->x11") << QStringLiteral("wayland") << QStringLiteral("xcb"); 0077 } 0078 0079 void XwaylandSelectionsTest::testSync() 0080 { 0081 // this test verifies the syncing of X11 to Wayland clipboard 0082 const QString copy = QFINDTESTDATA(QStringLiteral("copy")); 0083 QVERIFY(!copy.isEmpty()); 0084 const QString paste = QFINDTESTDATA(QStringLiteral("paste")); 0085 QVERIFY(!paste.isEmpty()); 0086 0087 QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded); 0088 QSignalSpy clipboardChangedSpy(waylandServer()->seat(), &KWaylandServer::SeatInterface::selectionChanged); 0089 0090 QProcessEnvironment environment = QProcessEnvironment::systemEnvironment(); 0091 0092 // start the copy process 0093 QFETCH(QString, copyPlatform); 0094 environment.insert(QStringLiteral("QT_QPA_PLATFORM"), copyPlatform); 0095 environment.insert(QStringLiteral("WAYLAND_DISPLAY"), s_socketName); 0096 std::unique_ptr<QProcess, ProcessKillBeforeDeleter> copyProcess(new QProcess()); 0097 copyProcess->setProcessEnvironment(environment); 0098 copyProcess->setProcessChannelMode(QProcess::ForwardedChannels); 0099 copyProcess->setProgram(copy); 0100 copyProcess->start(); 0101 QVERIFY(copyProcess->waitForStarted()); 0102 0103 Window *copyWindow = nullptr; 0104 QVERIFY(windowAddedSpy.wait()); 0105 copyWindow = windowAddedSpy.first().first().value<Window *>(); 0106 QVERIFY(copyWindow); 0107 if (workspace()->activeWindow() != copyWindow) { 0108 workspace()->activateWindow(copyWindow); 0109 } 0110 QCOMPARE(workspace()->activeWindow(), copyWindow); 0111 clipboardChangedSpy.wait(); 0112 0113 // start the paste process 0114 std::unique_ptr<QProcess, ProcessKillBeforeDeleter> pasteProcess(new QProcess()); 0115 QSignalSpy finishedSpy(pasteProcess.get(), static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished)); 0116 QFETCH(QString, pastePlatform); 0117 environment.insert(QStringLiteral("QT_QPA_PLATFORM"), pastePlatform); 0118 pasteProcess->setProcessEnvironment(environment); 0119 pasteProcess->setProcessChannelMode(QProcess::ForwardedChannels); 0120 pasteProcess->setProgram(paste); 0121 pasteProcess->start(); 0122 QVERIFY(pasteProcess->waitForStarted()); 0123 0124 windowAddedSpy.clear(); 0125 Window *pasteWindow = nullptr; 0126 QVERIFY(windowAddedSpy.wait()); 0127 pasteWindow = windowAddedSpy.last().first().value<Window *>(); 0128 QCOMPARE(windowAddedSpy.count(), 1); 0129 QVERIFY(pasteWindow); 0130 0131 if (workspace()->activeWindow() != pasteWindow) { 0132 QSignalSpy windowActivatedSpy(workspace(), &Workspace::windowActivated); 0133 workspace()->activateWindow(pasteWindow); 0134 QVERIFY(windowActivatedSpy.wait()); 0135 } 0136 QTRY_COMPARE(workspace()->activeWindow(), pasteWindow); 0137 QVERIFY(finishedSpy.wait()); 0138 QCOMPARE(finishedSpy.first().first().toInt(), 0); 0139 } 0140 0141 WAYLANDTEST_MAIN(XwaylandSelectionsTest) 0142 #include "xwayland_selections_test.moc"