File indexing completed on 2024-05-05 17:35:49

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"