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