File indexing completed on 2025-03-23 13:47:58
0001 /* 0002 KWin - the KDE window manager 0003 This file is part of the KDE project. 0004 0005 SPDX-FileCopyrightText: 2017 Martin Flöser <mgraesslin@kde.org> 0006 0007 SPDX-License-Identifier: GPL-2.0-or-later 0008 */ 0009 #include "kwin_wayland_test.h" 0010 0011 #include "core/outputbackend.h" 0012 #include "cursor.h" 0013 #include "input.h" 0014 #include "scripting/scripting.h" 0015 #include "useractions.h" 0016 #include "virtualdesktops.h" 0017 #include "wayland_server.h" 0018 #include "window.h" 0019 #include "workspace.h" 0020 0021 #include <KWayland/Client/surface.h> 0022 0023 #include <QDBusConnection> 0024 #include <QDBusMessage> 0025 #include <QDBusPendingReply> 0026 0027 using namespace KWin; 0028 0029 static const QString s_socketName = QStringLiteral("wayland_test_kwin_kwinbindings-0"); 0030 0031 class KWinBindingsTest : public QObject 0032 { 0033 Q_OBJECT 0034 private Q_SLOTS: 0035 void initTestCase(); 0036 void init(); 0037 void cleanup(); 0038 0039 void testSwitchWindow(); 0040 void testSwitchWindowScript(); 0041 void testWindowToDesktop_data(); 0042 void testWindowToDesktop(); 0043 }; 0044 0045 void KWinBindingsTest::initTestCase() 0046 { 0047 qRegisterMetaType<KWin::Window *>(); 0048 QSignalSpy applicationStartedSpy(kwinApp(), &Application::started); 0049 QVERIFY(waylandServer()->init(s_socketName)); 0050 QMetaObject::invokeMethod(kwinApp()->outputBackend(), "setVirtualOutputs", Qt::DirectConnection, Q_ARG(QVector<QRect>, QVector<QRect>() << QRect(0, 0, 1280, 1024) << QRect(1280, 0, 1280, 1024))); 0051 0052 kwinApp()->setConfig(KSharedConfig::openConfig(QString(), KConfig::SimpleConfig)); 0053 0054 kwinApp()->start(); 0055 QVERIFY(applicationStartedSpy.wait()); 0056 } 0057 0058 void KWinBindingsTest::init() 0059 { 0060 QVERIFY(Test::setupWaylandConnection()); 0061 workspace()->setActiveOutput(QPoint(640, 512)); 0062 KWin::Cursors::self()->mouse()->setPos(QPoint(640, 512)); 0063 } 0064 0065 void KWinBindingsTest::cleanup() 0066 { 0067 Test::destroyWaylandConnection(); 0068 } 0069 0070 void KWinBindingsTest::testSwitchWindow() 0071 { 0072 // first create windows 0073 std::unique_ptr<KWayland::Client::Surface> surface1(Test::createSurface()); 0074 std::unique_ptr<Test::XdgToplevel> shellSurface1(Test::createXdgToplevelSurface(surface1.get())); 0075 auto c1 = Test::renderAndWaitForShown(surface1.get(), QSize(100, 50), Qt::blue); 0076 std::unique_ptr<KWayland::Client::Surface> surface2(Test::createSurface()); 0077 std::unique_ptr<Test::XdgToplevel> shellSurface2(Test::createXdgToplevelSurface(surface2.get())); 0078 auto c2 = Test::renderAndWaitForShown(surface2.get(), QSize(100, 50), Qt::blue); 0079 std::unique_ptr<KWayland::Client::Surface> surface3(Test::createSurface()); 0080 std::unique_ptr<Test::XdgToplevel> shellSurface3(Test::createXdgToplevelSurface(surface3.get())); 0081 auto c3 = Test::renderAndWaitForShown(surface3.get(), QSize(100, 50), Qt::blue); 0082 std::unique_ptr<KWayland::Client::Surface> surface4(Test::createSurface()); 0083 std::unique_ptr<Test::XdgToplevel> shellSurface4(Test::createXdgToplevelSurface(surface4.get())); 0084 auto c4 = Test::renderAndWaitForShown(surface4.get(), QSize(100, 50), Qt::blue); 0085 0086 QVERIFY(c4->isActive()); 0087 QVERIFY(c4 != c3); 0088 QVERIFY(c3 != c2); 0089 QVERIFY(c2 != c1); 0090 0091 // let's position all windows 0092 c1->move(QPoint(0, 0)); 0093 c2->move(QPoint(200, 0)); 0094 c3->move(QPoint(200, 200)); 0095 c4->move(QPoint(0, 200)); 0096 0097 // now let's trigger the shortcuts 0098 0099 // invoke global shortcut through dbus 0100 auto invokeShortcut = [](const QString &shortcut) { 0101 auto msg = QDBusMessage::createMethodCall( 0102 QStringLiteral("org.kde.kglobalaccel"), 0103 QStringLiteral("/component/kwin"), 0104 QStringLiteral("org.kde.kglobalaccel.Component"), 0105 QStringLiteral("invokeShortcut")); 0106 msg.setArguments(QList<QVariant>{shortcut}); 0107 QDBusConnection::sessionBus().asyncCall(msg); 0108 }; 0109 invokeShortcut(QStringLiteral("Switch Window Up")); 0110 QTRY_COMPARE(workspace()->activeWindow(), c1); 0111 invokeShortcut(QStringLiteral("Switch Window Right")); 0112 QTRY_COMPARE(workspace()->activeWindow(), c2); 0113 invokeShortcut(QStringLiteral("Switch Window Down")); 0114 QTRY_COMPARE(workspace()->activeWindow(), c3); 0115 invokeShortcut(QStringLiteral("Switch Window Left")); 0116 QTRY_COMPARE(workspace()->activeWindow(), c4); 0117 // test opposite direction 0118 invokeShortcut(QStringLiteral("Switch Window Left")); 0119 QTRY_COMPARE(workspace()->activeWindow(), c3); 0120 invokeShortcut(QStringLiteral("Switch Window Down")); 0121 QTRY_COMPARE(workspace()->activeWindow(), c2); 0122 invokeShortcut(QStringLiteral("Switch Window Right")); 0123 QTRY_COMPARE(workspace()->activeWindow(), c1); 0124 invokeShortcut(QStringLiteral("Switch Window Up")); 0125 QTRY_COMPARE(workspace()->activeWindow(), c4); 0126 } 0127 0128 void KWinBindingsTest::testSwitchWindowScript() 0129 { 0130 QVERIFY(Scripting::self()); 0131 0132 // first create windows 0133 std::unique_ptr<KWayland::Client::Surface> surface1(Test::createSurface()); 0134 std::unique_ptr<Test::XdgToplevel> shellSurface1(Test::createXdgToplevelSurface(surface1.get())); 0135 auto c1 = Test::renderAndWaitForShown(surface1.get(), QSize(100, 50), Qt::blue); 0136 std::unique_ptr<KWayland::Client::Surface> surface2(Test::createSurface()); 0137 std::unique_ptr<Test::XdgToplevel> shellSurface2(Test::createXdgToplevelSurface(surface2.get())); 0138 auto c2 = Test::renderAndWaitForShown(surface2.get(), QSize(100, 50), Qt::blue); 0139 std::unique_ptr<KWayland::Client::Surface> surface3(Test::createSurface()); 0140 std::unique_ptr<Test::XdgToplevel> shellSurface3(Test::createXdgToplevelSurface(surface3.get())); 0141 auto c3 = Test::renderAndWaitForShown(surface3.get(), QSize(100, 50), Qt::blue); 0142 std::unique_ptr<KWayland::Client::Surface> surface4(Test::createSurface()); 0143 std::unique_ptr<Test::XdgToplevel> shellSurface4(Test::createXdgToplevelSurface(surface4.get())); 0144 auto c4 = Test::renderAndWaitForShown(surface4.get(), QSize(100, 50), Qt::blue); 0145 0146 QVERIFY(c4->isActive()); 0147 QVERIFY(c4 != c3); 0148 QVERIFY(c3 != c2); 0149 QVERIFY(c2 != c1); 0150 0151 // let's position all windows 0152 c1->move(QPoint(0, 0)); 0153 c2->move(QPoint(200, 0)); 0154 c3->move(QPoint(200, 200)); 0155 c4->move(QPoint(0, 200)); 0156 0157 auto runScript = [](const QString &slot) { 0158 QTemporaryFile tmpFile; 0159 QVERIFY(tmpFile.open()); 0160 QTextStream out(&tmpFile); 0161 out << "workspace." << slot << "()"; 0162 out.flush(); 0163 0164 const int id = Scripting::self()->loadScript(tmpFile.fileName()); 0165 QVERIFY(id != -1); 0166 QVERIFY(Scripting::self()->isScriptLoaded(tmpFile.fileName())); 0167 auto s = Scripting::self()->findScript(tmpFile.fileName()); 0168 QVERIFY(s); 0169 QSignalSpy runningChangedSpy(s, &AbstractScript::runningChanged); 0170 s->run(); 0171 QTRY_COMPARE(runningChangedSpy.count(), 1); 0172 }; 0173 0174 runScript(QStringLiteral("slotSwitchWindowUp")); 0175 QTRY_COMPARE(workspace()->activeWindow(), c1); 0176 runScript(QStringLiteral("slotSwitchWindowRight")); 0177 QTRY_COMPARE(workspace()->activeWindow(), c2); 0178 runScript(QStringLiteral("slotSwitchWindowDown")); 0179 QTRY_COMPARE(workspace()->activeWindow(), c3); 0180 runScript(QStringLiteral("slotSwitchWindowLeft")); 0181 QTRY_COMPARE(workspace()->activeWindow(), c4); 0182 } 0183 0184 void KWinBindingsTest::testWindowToDesktop_data() 0185 { 0186 QTest::addColumn<int>("desktop"); 0187 0188 QTest::newRow("2") << 2; 0189 QTest::newRow("3") << 3; 0190 QTest::newRow("4") << 4; 0191 QTest::newRow("5") << 5; 0192 QTest::newRow("6") << 6; 0193 QTest::newRow("7") << 7; 0194 QTest::newRow("8") << 8; 0195 QTest::newRow("9") << 9; 0196 QTest::newRow("10") << 10; 0197 QTest::newRow("11") << 11; 0198 QTest::newRow("12") << 12; 0199 QTest::newRow("13") << 13; 0200 QTest::newRow("14") << 14; 0201 QTest::newRow("15") << 15; 0202 QTest::newRow("16") << 16; 0203 QTest::newRow("17") << 17; 0204 QTest::newRow("18") << 18; 0205 QTest::newRow("19") << 19; 0206 QTest::newRow("20") << 20; 0207 } 0208 0209 void KWinBindingsTest::testWindowToDesktop() 0210 { 0211 // first go to desktop one 0212 VirtualDesktopManager::self()->setCurrent(VirtualDesktopManager::self()->desktops().first()); 0213 0214 // now create a window 0215 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface()); 0216 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get())); 0217 auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue); 0218 QSignalSpy desktopChangedSpy(window, &Window::desktopChanged); 0219 QCOMPARE(workspace()->activeWindow(), window); 0220 0221 QFETCH(int, desktop); 0222 VirtualDesktopManager::self()->setCount(desktop); 0223 0224 // now trigger the shortcut 0225 auto invokeShortcut = [](int desktop) { 0226 auto msg = QDBusMessage::createMethodCall( 0227 QStringLiteral("org.kde.kglobalaccel"), 0228 QStringLiteral("/component/kwin"), 0229 QStringLiteral("org.kde.kglobalaccel.Component"), 0230 QStringLiteral("invokeShortcut")); 0231 msg.setArguments(QList<QVariant>{QStringLiteral("Window to Desktop %1").arg(desktop)}); 0232 QDBusConnection::sessionBus().asyncCall(msg); 0233 }; 0234 invokeShortcut(desktop); 0235 QVERIFY(desktopChangedSpy.wait()); 0236 QCOMPARE(window->desktop(), desktop); 0237 // back to desktop 1 0238 invokeShortcut(1); 0239 QVERIFY(desktopChangedSpy.wait()); 0240 QCOMPARE(window->desktop(), 1); 0241 // invoke with one desktop too many 0242 invokeShortcut(desktop + 1); 0243 // that should fail 0244 QVERIFY(!desktopChangedSpy.wait(100)); 0245 } 0246 0247 WAYLANDTEST_MAIN(KWinBindingsTest) 0248 #include "kwinbindings_test.moc"