File indexing completed on 2024-11-10 04:56:02

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2018 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 "input.h"
0012 #include "keyboard_input.h"
0013 #include "pointer_input.h"
0014 #include "screenedge.h"
0015 #include "wayland_server.h"
0016 #include "workspace.h"
0017 
0018 #include <KConfigGroup>
0019 #include <KGlobalAccel>
0020 
0021 #include <QAction>
0022 #include <QDBusConnection>
0023 
0024 #include <linux/input.h>
0025 
0026 using namespace KWin;
0027 
0028 static const QString s_socketName = QStringLiteral("wayland_test_kwin_no_global_shortcuts-0");
0029 static const QString s_serviceName = QStringLiteral("org.kde.KWin.Test.ModifierOnlyShortcut");
0030 static const QString s_path = QStringLiteral("/Test");
0031 
0032 Q_DECLARE_METATYPE(KWin::ElectricBorder)
0033 
0034 /**
0035  * This test verifies the NoGlobalShortcuts initialization flag
0036  */
0037 class NoGlobalShortcutsTest : public QObject
0038 {
0039     Q_OBJECT
0040 private Q_SLOTS:
0041     void initTestCase();
0042     void init();
0043     void cleanup();
0044 
0045     void testTrigger_data();
0046     void testTrigger();
0047     void testKGlobalAccel();
0048     void testPointerShortcut();
0049     void testAxisShortcut_data();
0050     void testAxisShortcut();
0051     void testScreenEdge();
0052 };
0053 
0054 class Target : public QObject
0055 {
0056     Q_OBJECT
0057     Q_CLASSINFO("D-Bus Interface", "org.kde.KWin.Test.ModifierOnlyShortcut")
0058 
0059 public:
0060     Target();
0061     ~Target() override;
0062 
0063 public Q_SLOTS:
0064     Q_SCRIPTABLE void shortcut();
0065 
0066 Q_SIGNALS:
0067     void shortcutTriggered();
0068 };
0069 
0070 Target::Target()
0071     : QObject()
0072 {
0073     QDBusConnection::sessionBus().registerService(s_serviceName);
0074     QDBusConnection::sessionBus().registerObject(s_path, s_serviceName, this, QDBusConnection::ExportScriptableSlots);
0075 }
0076 
0077 Target::~Target()
0078 {
0079     QDBusConnection::sessionBus().unregisterObject(s_path);
0080     QDBusConnection::sessionBus().unregisterService(s_serviceName);
0081 }
0082 
0083 void Target::shortcut()
0084 {
0085     Q_EMIT shortcutTriggered();
0086 }
0087 
0088 void NoGlobalShortcutsTest::initTestCase()
0089 {
0090     qRegisterMetaType<KWin::ElectricBorder>("ElectricBorder");
0091     QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
0092     QVERIFY(waylandServer()->init(s_socketName, KWin::WaylandServer::InitializationFlag::NoGlobalShortcuts));
0093     Test::setOutputConfig({
0094         QRect(0, 0, 1280, 1024),
0095         QRect(1280, 0, 1280, 1024),
0096     });
0097 
0098     kwinApp()->setConfig(KSharedConfig::openConfig(QString(), KConfig::SimpleConfig));
0099     qputenv("KWIN_XKB_DEFAULT_KEYMAP", "1");
0100     qputenv("XKB_DEFAULT_RULES", "evdev");
0101 
0102     kwinApp()->start();
0103     QVERIFY(applicationStartedSpy.wait());
0104 }
0105 
0106 void NoGlobalShortcutsTest::init()
0107 {
0108     workspace()->setActiveOutput(QPoint(640, 512));
0109     KWin::input()->pointer()->warp(QPoint(640, 512));
0110 }
0111 
0112 void NoGlobalShortcutsTest::cleanup()
0113 {
0114 }
0115 
0116 void NoGlobalShortcutsTest::testTrigger_data()
0117 {
0118     QTest::addColumn<QStringList>("metaConfig");
0119     QTest::addColumn<QStringList>("altConfig");
0120     QTest::addColumn<QStringList>("controlConfig");
0121     QTest::addColumn<QStringList>("shiftConfig");
0122     QTest::addColumn<int>("modifier");
0123     QTest::addColumn<QList<int>>("nonTriggeringMods");
0124 
0125     const QStringList trigger = QStringList{s_serviceName, s_path, s_serviceName, QStringLiteral("shortcut")};
0126     const QStringList e = QStringList();
0127 
0128     QTest::newRow("leftMeta") << trigger << e << e << e << KEY_LEFTMETA << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
0129     QTest::newRow("rightMeta") << trigger << e << e << e << KEY_RIGHTMETA << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
0130     QTest::newRow("leftAlt") << e << trigger << e << e << KEY_LEFTALT << QList<int>{KEY_LEFTMETA, KEY_RIGHTMETA, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
0131     QTest::newRow("rightAlt") << e << trigger << e << e << KEY_RIGHTALT << QList<int>{KEY_LEFTMETA, KEY_RIGHTMETA, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
0132     QTest::newRow("leftControl") << e << e << trigger << e << KEY_LEFTCTRL << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTMETA, KEY_RIGHTMETA, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
0133     QTest::newRow("rightControl") << e << e << trigger << e << KEY_RIGHTCTRL << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTMETA, KEY_RIGHTMETA, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
0134     QTest::newRow("leftShift") << e << e << e << trigger << KEY_LEFTSHIFT << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTMETA, KEY_RIGHTMETA};
0135     QTest::newRow("rightShift") << e << e << e << trigger << KEY_RIGHTSHIFT << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTMETA, KEY_RIGHTMETA};
0136 }
0137 
0138 void NoGlobalShortcutsTest::testTrigger()
0139 {
0140     // test based on ModifierOnlyShortcutTest::testTrigger
0141     Target target;
0142     QSignalSpy triggeredSpy(&target, &Target::shortcutTriggered);
0143 
0144     KConfigGroup group = kwinApp()->config()->group(QStringLiteral("ModifierOnlyShortcuts"));
0145     QFETCH(QStringList, metaConfig);
0146     QFETCH(QStringList, altConfig);
0147     QFETCH(QStringList, shiftConfig);
0148     QFETCH(QStringList, controlConfig);
0149     group.writeEntry("Meta", metaConfig);
0150     group.writeEntry("Alt", altConfig);
0151     group.writeEntry("Shift", shiftConfig);
0152     group.writeEntry("Control", controlConfig);
0153     group.sync();
0154     workspace()->slotReconfigure();
0155 
0156     // configured shortcut should trigger
0157     quint32 timestamp = 1;
0158     QFETCH(int, modifier);
0159     Test::keyboardKeyPressed(modifier, timestamp++);
0160     Test::keyboardKeyReleased(modifier, timestamp++);
0161     QCOMPARE(triggeredSpy.count(), 0);
0162 
0163     // the other shortcuts should not trigger
0164     QFETCH(QList<int>, nonTriggeringMods);
0165     for (auto it = nonTriggeringMods.constBegin(), end = nonTriggeringMods.constEnd(); it != end; it++) {
0166         Test::keyboardKeyPressed(*it, timestamp++);
0167         Test::keyboardKeyReleased(*it, timestamp++);
0168         QCOMPARE(triggeredSpy.count(), 0);
0169     }
0170 }
0171 
0172 void NoGlobalShortcutsTest::testKGlobalAccel()
0173 {
0174     std::unique_ptr<QAction> action(new QAction(nullptr));
0175     action->setProperty("componentName", QStringLiteral("kwin"));
0176     action->setObjectName(QStringLiteral("globalshortcuts-test-meta-shift-w"));
0177     QSignalSpy triggeredSpy(action.get(), &QAction::triggered);
0178     KGlobalAccel::self()->setShortcut(action.get(), QList<QKeySequence>{Qt::META | Qt::SHIFT | Qt::Key_W}, KGlobalAccel::NoAutoloading);
0179 
0180     // press meta+shift+w
0181     quint32 timestamp = 0;
0182     Test::keyboardKeyPressed(KEY_LEFTMETA, timestamp++);
0183     QCOMPARE(input()->keyboardModifiers(), Qt::MetaModifier);
0184     Test::keyboardKeyPressed(KEY_LEFTSHIFT, timestamp++);
0185     QCOMPARE(input()->keyboardModifiers(), Qt::ShiftModifier | Qt::MetaModifier);
0186     Test::keyboardKeyPressed(KEY_W, timestamp++);
0187     Test::keyboardKeyReleased(KEY_W, timestamp++);
0188 
0189     // release meta+shift
0190     Test::keyboardKeyReleased(KEY_LEFTSHIFT, timestamp++);
0191     Test::keyboardKeyReleased(KEY_LEFTMETA, timestamp++);
0192 
0193     QVERIFY(!triggeredSpy.wait(100));
0194     QCOMPARE(triggeredSpy.count(), 0);
0195 }
0196 
0197 void NoGlobalShortcutsTest::testPointerShortcut()
0198 {
0199     // based on LockScreenTest::testPointerShortcut
0200     std::unique_ptr<QAction> action(new QAction(nullptr));
0201     QSignalSpy actionSpy(action.get(), &QAction::triggered);
0202     input()->registerPointerShortcut(Qt::MetaModifier, Qt::LeftButton, action.get());
0203 
0204     // try to trigger the shortcut
0205     quint32 timestamp = 1;
0206     Test::keyboardKeyPressed(KEY_LEFTMETA, timestamp++);
0207     Test::pointerButtonPressed(BTN_LEFT, timestamp++);
0208     QCoreApplication::instance()->processEvents();
0209     QCOMPARE(actionSpy.count(), 0);
0210     Test::pointerButtonReleased(BTN_LEFT, timestamp++);
0211     Test::keyboardKeyReleased(KEY_LEFTMETA, timestamp++);
0212     QCoreApplication::instance()->processEvents();
0213     QCOMPARE(actionSpy.count(), 0);
0214 }
0215 
0216 void NoGlobalShortcutsTest::testAxisShortcut_data()
0217 {
0218     QTest::addColumn<Qt::Orientation>("direction");
0219     QTest::addColumn<int>("sign");
0220 
0221     QTest::newRow("up") << Qt::Vertical << 1;
0222     QTest::newRow("down") << Qt::Vertical << -1;
0223     QTest::newRow("left") << Qt::Horizontal << 1;
0224     QTest::newRow("right") << Qt::Horizontal << -1;
0225 }
0226 
0227 void NoGlobalShortcutsTest::testAxisShortcut()
0228 {
0229     // based on LockScreenTest::testAxisShortcut
0230     std::unique_ptr<QAction> action(new QAction(nullptr));
0231     QSignalSpy actionSpy(action.get(), &QAction::triggered);
0232     QFETCH(Qt::Orientation, direction);
0233     QFETCH(int, sign);
0234     PointerAxisDirection axisDirection = PointerAxisUp;
0235     if (direction == Qt::Vertical) {
0236         axisDirection = sign > 0 ? PointerAxisUp : PointerAxisDown;
0237     } else {
0238         axisDirection = sign > 0 ? PointerAxisLeft : PointerAxisRight;
0239     }
0240     input()->registerAxisShortcut(Qt::MetaModifier, axisDirection, action.get());
0241 
0242     // try to trigger the shortcut
0243     quint32 timestamp = 1;
0244     Test::keyboardKeyPressed(KEY_LEFTMETA, timestamp++);
0245     if (direction == Qt::Vertical) {
0246         Test::pointerAxisVertical(sign * 5.0, timestamp++);
0247     } else {
0248         Test::pointerAxisHorizontal(sign * 5.0, timestamp++);
0249     }
0250     QCoreApplication::instance()->processEvents();
0251     QCOMPARE(actionSpy.count(), 0);
0252     Test::keyboardKeyReleased(KEY_LEFTMETA, timestamp++);
0253     QCoreApplication::instance()->processEvents();
0254     QCOMPARE(actionSpy.count(), 0);
0255 }
0256 
0257 void NoGlobalShortcutsTest::testScreenEdge()
0258 {
0259     // based on LockScreenTest::testScreenEdge
0260     QSignalSpy screenEdgeSpy(workspace()->screenEdges(), &ScreenEdges::approaching);
0261     QCOMPARE(screenEdgeSpy.count(), 0);
0262 
0263     quint32 timestamp = 1;
0264     Test::pointerMotion({5, 5}, timestamp++);
0265     QCOMPARE(screenEdgeSpy.count(), 0);
0266 }
0267 
0268 WAYLANDTEST_MAIN(NoGlobalShortcutsTest)
0269 #include "no_global_shortcuts_test.moc"