KWin
Loading...
Searching...
No Matches
no_global_shortcuts_test.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2018 Martin Flöser <mgraesslin@kde.org>
6
7 SPDX-License-Identifier: GPL-2.0-or-later
8*/
9#include "kwin_wayland_test.h"
10
11#include "input.h"
12#include "keyboard_input.h"
13#include "pointer_input.h"
14#include "screenedge.h"
15#include "wayland_server.h"
16#include "workspace.h"
17
18#include <KConfigGroup>
19#include <KGlobalAccel>
20
21#include <QAction>
22#include <QDBusConnection>
23
24#include <linux/input.h>
25
26using namespace KWin;
27
28static const QString s_socketName = QStringLiteral("wayland_test_kwin_no_global_shortcuts-0");
29static const QString s_serviceName = QStringLiteral("org.kde.KWin.Test.ModifierOnlyShortcut");
30static const QString s_path = QStringLiteral("/Test");
31
33
34
37class NoGlobalShortcutsTest : public QObject
38{
39 Q_OBJECT
40private Q_SLOTS:
41 void initTestCase();
42 void init();
43 void cleanup();
44
45 void testTrigger_data();
46 void testTrigger();
47 void testKGlobalAccel();
48 void testPointerShortcut();
49 void testAxisShortcut_data();
50 void testAxisShortcut();
51 void testScreenEdge();
52};
53
54class Target : public QObject
55{
56 Q_OBJECT
57 Q_CLASSINFO("D-Bus Interface", "org.kde.KWin.Test.ModifierOnlyShortcut")
58
59public:
61 ~Target() override;
62
63public Q_SLOTS:
64 Q_SCRIPTABLE void shortcut();
65
66Q_SIGNALS:
68};
69
71 : QObject()
72{
73 QDBusConnection::sessionBus().registerService(s_serviceName);
74 QDBusConnection::sessionBus().registerObject(s_path, s_serviceName, this, QDBusConnection::ExportScriptableSlots);
75}
76
78{
79 QDBusConnection::sessionBus().unregisterObject(s_path);
80 QDBusConnection::sessionBus().unregisterService(s_serviceName);
81}
82
84{
85 Q_EMIT shortcutTriggered();
86}
87
88void NoGlobalShortcutsTest::initTestCase()
89{
90 qRegisterMetaType<KWin::ElectricBorder>("ElectricBorder");
91 QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
94 QRect(0, 0, 1280, 1024),
95 QRect(1280, 0, 1280, 1024),
96 });
97
98 kwinApp()->setConfig(KSharedConfig::openConfig(QString(), KConfig::SimpleConfig));
99 qputenv("KWIN_XKB_DEFAULT_KEYMAP", "1");
100 qputenv("XKB_DEFAULT_RULES", "evdev");
101
102 kwinApp()->start();
103 QVERIFY(applicationStartedSpy.wait());
104}
105
106void NoGlobalShortcutsTest::init()
107{
108 workspace()->setActiveOutput(QPoint(640, 512));
109 KWin::input()->pointer()->warp(QPoint(640, 512));
110}
111
112void NoGlobalShortcutsTest::cleanup()
113{
114}
115
116void NoGlobalShortcutsTest::testTrigger_data()
117{
118 QTest::addColumn<QStringList>("metaConfig");
119 QTest::addColumn<QStringList>("altConfig");
120 QTest::addColumn<QStringList>("controlConfig");
121 QTest::addColumn<QStringList>("shiftConfig");
122 QTest::addColumn<int>("modifier");
123 QTest::addColumn<QList<int>>("nonTriggeringMods");
124
125 const QStringList trigger = QStringList{s_serviceName, s_path, s_serviceName, QStringLiteral("shortcut")};
126 const QStringList e = QStringList();
127
128 QTest::newRow("leftMeta") << trigger << e << e << e << KEY_LEFTMETA << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
129 QTest::newRow("rightMeta") << trigger << e << e << e << KEY_RIGHTMETA << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
130 QTest::newRow("leftAlt") << e << trigger << e << e << KEY_LEFTALT << QList<int>{KEY_LEFTMETA, KEY_RIGHTMETA, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
131 QTest::newRow("rightAlt") << e << trigger << e << e << KEY_RIGHTALT << QList<int>{KEY_LEFTMETA, KEY_RIGHTMETA, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
132 QTest::newRow("leftControl") << e << e << trigger << e << KEY_LEFTCTRL << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTMETA, KEY_RIGHTMETA, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
133 QTest::newRow("rightControl") << e << e << trigger << e << KEY_RIGHTCTRL << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTMETA, KEY_RIGHTMETA, KEY_LEFTSHIFT, KEY_RIGHTSHIFT};
134 QTest::newRow("leftShift") << e << e << e << trigger << KEY_LEFTSHIFT << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTMETA, KEY_RIGHTMETA};
135 QTest::newRow("rightShift") << e << e << e << trigger << KEY_RIGHTSHIFT << QList<int>{KEY_LEFTALT, KEY_RIGHTALT, KEY_LEFTCTRL, KEY_RIGHTCTRL, KEY_LEFTMETA, KEY_RIGHTMETA};
136}
137
138void NoGlobalShortcutsTest::testTrigger()
139{
140 // test based on ModifierOnlyShortcutTest::testTrigger
141 Target target;
142 QSignalSpy triggeredSpy(&target, &Target::shortcutTriggered);
143
144 KConfigGroup group = kwinApp()->config()->group(QStringLiteral("ModifierOnlyShortcuts"));
145 QFETCH(QStringList, metaConfig);
146 QFETCH(QStringList, altConfig);
147 QFETCH(QStringList, shiftConfig);
148 QFETCH(QStringList, controlConfig);
149 group.writeEntry("Meta", metaConfig);
150 group.writeEntry("Alt", altConfig);
151 group.writeEntry("Shift", shiftConfig);
152 group.writeEntry("Control", controlConfig);
153 group.sync();
155
156 // configured shortcut should trigger
157 quint32 timestamp = 1;
158 QFETCH(int, modifier);
159 Test::keyboardKeyPressed(modifier, timestamp++);
160 Test::keyboardKeyReleased(modifier, timestamp++);
161 QCOMPARE(triggeredSpy.count(), 0);
162
163 // the other shortcuts should not trigger
164 QFETCH(QList<int>, nonTriggeringMods);
165 for (auto it = nonTriggeringMods.constBegin(), end = nonTriggeringMods.constEnd(); it != end; it++) {
166 Test::keyboardKeyPressed(*it, timestamp++);
167 Test::keyboardKeyReleased(*it, timestamp++);
168 QCOMPARE(triggeredSpy.count(), 0);
169 }
170}
171
172void NoGlobalShortcutsTest::testKGlobalAccel()
173{
174 std::unique_ptr<QAction> action(new QAction(nullptr));
175 action->setProperty("componentName", QStringLiteral("kwin"));
176 action->setObjectName(QStringLiteral("globalshortcuts-test-meta-shift-w"));
177 QSignalSpy triggeredSpy(action.get(), &QAction::triggered);
178 KGlobalAccel::self()->setShortcut(action.get(), QList<QKeySequence>{Qt::META | Qt::SHIFT | Qt::Key_W}, KGlobalAccel::NoAutoloading);
179
180 // press meta+shift+w
181 quint32 timestamp = 0;
182 Test::keyboardKeyPressed(KEY_LEFTMETA, timestamp++);
183 QCOMPARE(input()->keyboardModifiers(), Qt::MetaModifier);
184 Test::keyboardKeyPressed(KEY_LEFTSHIFT, timestamp++);
185 QCOMPARE(input()->keyboardModifiers(), Qt::ShiftModifier | Qt::MetaModifier);
186 Test::keyboardKeyPressed(KEY_W, timestamp++);
187 Test::keyboardKeyReleased(KEY_W, timestamp++);
188
189 // release meta+shift
190 Test::keyboardKeyReleased(KEY_LEFTSHIFT, timestamp++);
191 Test::keyboardKeyReleased(KEY_LEFTMETA, timestamp++);
192
193 QVERIFY(!triggeredSpy.wait(100));
194 QCOMPARE(triggeredSpy.count(), 0);
195}
196
197void NoGlobalShortcutsTest::testPointerShortcut()
198{
199 // based on LockScreenTest::testPointerShortcut
200 std::unique_ptr<QAction> action(new QAction(nullptr));
201 QSignalSpy actionSpy(action.get(), &QAction::triggered);
202 input()->registerPointerShortcut(Qt::MetaModifier, Qt::LeftButton, action.get());
203
204 // try to trigger the shortcut
205 quint32 timestamp = 1;
206 Test::keyboardKeyPressed(KEY_LEFTMETA, timestamp++);
207 Test::pointerButtonPressed(BTN_LEFT, timestamp++);
208 QCoreApplication::instance()->processEvents();
209 QCOMPARE(actionSpy.count(), 0);
210 Test::pointerButtonReleased(BTN_LEFT, timestamp++);
211 Test::keyboardKeyReleased(KEY_LEFTMETA, timestamp++);
212 QCoreApplication::instance()->processEvents();
213 QCOMPARE(actionSpy.count(), 0);
214}
215
216void NoGlobalShortcutsTest::testAxisShortcut_data()
217{
218 QTest::addColumn<Qt::Orientation>("direction");
219 QTest::addColumn<int>("sign");
220
221 QTest::newRow("up") << Qt::Vertical << 1;
222 QTest::newRow("down") << Qt::Vertical << -1;
223 QTest::newRow("left") << Qt::Horizontal << 1;
224 QTest::newRow("right") << Qt::Horizontal << -1;
225}
226
227void NoGlobalShortcutsTest::testAxisShortcut()
228{
229 // based on LockScreenTest::testAxisShortcut
230 std::unique_ptr<QAction> action(new QAction(nullptr));
231 QSignalSpy actionSpy(action.get(), &QAction::triggered);
232 QFETCH(Qt::Orientation, direction);
233 QFETCH(int, sign);
234 PointerAxisDirection axisDirection = PointerAxisUp;
235 if (direction == Qt::Vertical) {
236 axisDirection = sign > 0 ? PointerAxisUp : PointerAxisDown;
237 } else {
238 axisDirection = sign > 0 ? PointerAxisLeft : PointerAxisRight;
239 }
240 input()->registerAxisShortcut(Qt::MetaModifier, axisDirection, action.get());
241
242 // try to trigger the shortcut
243 quint32 timestamp = 1;
244 Test::keyboardKeyPressed(KEY_LEFTMETA, timestamp++);
245 if (direction == Qt::Vertical) {
246 Test::pointerAxisVertical(sign * 5.0, timestamp++);
247 } else {
248 Test::pointerAxisHorizontal(sign * 5.0, timestamp++);
249 }
250 QCoreApplication::instance()->processEvents();
251 QCOMPARE(actionSpy.count(), 0);
252 Test::keyboardKeyReleased(KEY_LEFTMETA, timestamp++);
253 QCoreApplication::instance()->processEvents();
254 QCOMPARE(actionSpy.count(), 0);
255}
256
257void NoGlobalShortcutsTest::testScreenEdge()
258{
259 // based on LockScreenTest::testScreenEdge
260 QSignalSpy screenEdgeSpy(workspace()->screenEdges(), &ScreenEdges::approaching);
261 QCOMPARE(screenEdgeSpy.count(), 0);
262
263 quint32 timestamp = 1;
264 Test::pointerMotion({5, 5}, timestamp++);
265 QCOMPARE(screenEdgeSpy.count(), 0);
266}
267
269#include "no_global_shortcuts_test.moc"
void registerPointerShortcut(Qt::KeyboardModifiers modifiers, Qt::MouseButton pointerButtons, QAction *action)
Definition input.cpp:3314
void registerAxisShortcut(Qt::KeyboardModifiers modifiers, PointerAxisDirection axis, QAction *action)
PointerInputRedirection * pointer() const
Definition input.h:220
void warp(const QPointF &pos)
void approaching(ElectricBorder border, qreal factor, const QRect &geometry)
void setActiveOutput(Output *output)
void slotReconfigure()
void shortcutTriggered()
Q_SCRIPTABLE void shortcut()
const QString s_path
Q_DECLARE_METATYPE(KWin::SwitchEvent::State)
#define WAYLANDTEST_MAIN(TestObject)
void keyboardKeyReleased(quint32 key, quint32 time)
void setOutputConfig(const QList< QRect > &geometries)
void pointerAxisVertical(qreal delta, quint32 time, qint32 discreteDelta=0, InputRedirection::PointerAxisSource source=InputRedirection::PointerAxisSourceUnknown)
void keyboardKeyPressed(quint32 key, quint32 time)
void pointerAxisHorizontal(qreal delta, quint32 time, qint32 discreteDelta=0, InputRedirection::PointerAxisSource source=InputRedirection::PointerAxisSourceUnknown)
void pointerButtonPressed(quint32 button, quint32 time)
void pointerMotion(const QPointF &position, quint32 time)
void pointerButtonReleased(quint32 button, quint32 time)
void init(xcb_connection_t *connection, xcb_window_t rootWindow)
PointerAxisDirection
The direction in which a pointer axis is moved.
Definition globals.h:104
@ PointerAxisUp
Definition globals.h:105
@ PointerAxisLeft
Definition globals.h:107
@ PointerAxisDown
Definition globals.h:106
@ PointerAxisRight
Definition globals.h:108
ElectricBorder
Definition globals.h:60
WaylandServer * waylandServer()
Workspace * workspace()
Definition workspace.h:830
InputRedirection * input()
Definition input.h:549