23#include <KWayland/Client/surface.h>
25#include <KGlobalAccel>
29#include <linux/input.h>
31#include <xcb/xcb_icccm.h>
35static const QString s_socketName = QStringLiteral(
"wayland_test_kwin_globalshortcuts-0");
45 void testNonLatinLayout_data();
46 void testNonLatinLayout();
47 void testConsumedShift();
48 void testRepeatedTrigger();
49 void testUserActionsMenu();
50 void testMetaShiftW();
51 void testComponseKey();
53 void testX11WindowShortcut();
54 void testWaylandWindowShortcut();
55 void testSetupWindowShortcut();
58void GlobalShortcutsTest::initTestCase()
60 qRegisterMetaType<KWin::Window *>();
61 qRegisterMetaType<KWin::InternalWindow *>();
65 QRect(0, 0, 1280, 1024),
66 QRect(1280, 0, 1280, 1024),
69 kwinApp()->setConfig(KSharedConfig::openConfig(QString(), KConfig::SimpleConfig));
70 qputenv(
"KWIN_XKB_DEFAULT_KEYMAP",
"1");
71 qputenv(
"XKB_DEFAULT_RULES",
"evdev");
72 qputenv(
"XKB_DEFAULT_LAYOUT",
"us,ru");
75 QVERIFY(applicationStartedSpy.wait());
78void GlobalShortcutsTest::init()
88void GlobalShortcutsTest::cleanup()
95void GlobalShortcutsTest::testNonLatinLayout_data()
97 QTest::addColumn<int>(
"modifierKey");
98 QTest::addColumn<Qt::Modifier>(
"qtModifier");
99 QTest::addColumn<int>(
"key");
100 QTest::addColumn<Qt::Key>(
"qtKey");
106 QTest::newRow(
"Left Ctrl + Tab") << KEY_LEFTCTRL << Qt::CTRL << KEY_TAB << Qt::Key_Tab;
107 QTest::newRow(
"Left Ctrl + W") << KEY_LEFTCTRL << Qt::CTRL << KEY_W << Qt::Key_W;
108 QTest::newRow(
"Left Ctrl + `") << KEY_LEFTCTRL << Qt::CTRL << KEY_GRAVE << Qt::Key_QuoteLeft;
110 QTest::newRow(
"Left Alt + Tab") << KEY_LEFTALT << Qt::ALT << KEY_TAB << Qt::Key_Tab;
111 QTest::newRow(
"Left Alt + W") << KEY_LEFTALT << Qt::ALT << KEY_W << Qt::Key_W;
112 QTest::newRow(
"Left Alt + `") << KEY_LEFTALT << Qt::ALT << KEY_GRAVE << Qt::Key_QuoteLeft;
114 QTest::newRow(
"Left Shift + Tab") << KEY_LEFTSHIFT << Qt::SHIFT << KEY_TAB << Qt::Key_Tab;
116 QTest::newRow(
"Left Meta + Tab") << KEY_LEFTMETA << Qt::META << KEY_TAB << Qt::Key_Tab;
117 QTest::newRow(
"Left Meta + W") << KEY_LEFTMETA << Qt::META << KEY_W << Qt::Key_W;
118 QTest::newRow(
"Left Meta + `") << KEY_LEFTMETA << Qt::META << KEY_GRAVE << Qt::Key_QuoteLeft;
121void GlobalShortcutsTest::testNonLatinLayout()
126 QCOMPARE(xkb->layoutName(), QStringLiteral(
"Russian"));
128 QFETCH(
int, modifierKey);
129 QFETCH(Qt::Modifier, qtModifier);
131 QFETCH(Qt::Key, qtKey);
133 const QKeySequence seq(qtModifier + qtKey);
135 std::unique_ptr<QAction> action(
new QAction(
nullptr));
136 action->setProperty(
"componentName", QStringLiteral(
"kwin"));
137 action->setObjectName(
"globalshortcuts-test-non-latin-layout");
139 QSignalSpy triggeredSpy(action.get(), &QAction::triggered);
141 KGlobalAccel::self()->stealShortcutSystemwide(seq);
142 KGlobalAccel::self()->setShortcut(action.get(), {seq}, KGlobalAccel::NoAutoloading);
144 quint32 timestamp = 0;
146 QCOMPARE(
input()->keyboardModifiers(), qtModifier);
152 QTRY_COMPARE_WITH_TIMEOUT(triggeredSpy.count(), 1, 100);
155void GlobalShortcutsTest::testConsumedShift()
159 std::unique_ptr<QAction> action(
new QAction(
nullptr));
160 action->setProperty(
"componentName", QStringLiteral(
"kwin"));
161 action->setObjectName(QStringLiteral(
"globalshortcuts-test-consumed-shift"));
162 QSignalSpy triggeredSpy(action.get(), &QAction::triggered);
163 KGlobalAccel::self()->setShortcut(action.get(), QList<QKeySequence>{Qt::Key_Percent}, KGlobalAccel::NoAutoloading);
166 quint32 timestamp = 0;
168 QCOMPARE(
input()->keyboardModifiers(), Qt::ShiftModifier);
170 QTRY_COMPARE(triggeredSpy.count(), 1);
177void GlobalShortcutsTest::testRepeatedTrigger()
182 std::unique_ptr<QAction> action(
new QAction(
nullptr));
183 action->setProperty(
"componentName", QStringLiteral(
"kwin"));
184 action->setObjectName(QStringLiteral(
"globalshortcuts-test-consumed-shift"));
185 QSignalSpy triggeredSpy(action.get(), &QAction::triggered);
186 KGlobalAccel::self()->setShortcut(action.get(), QList<QKeySequence>{Qt::Key_Percent}, KGlobalAccel::NoAutoloading);
192 quint32 timestamp = 0;
195 QCOMPARE(
input()->keyboardModifiers(), Qt::ShiftModifier);
197 QTRY_COMPARE(triggeredSpy.count(), 1);
199 QVERIFY(triggeredSpy.wait());
200 QVERIFY(triggeredSpy.wait());
203 QVERIFY(!triggeredSpy.wait(50));
206 QVERIFY(!triggeredSpy.wait(50));
212void GlobalShortcutsTest::testUserActionsMenu()
227 QVERIFY(window->isActive());
229 quint32 timestamp = 0;
230 QVERIFY(!
workspace()->userActionsMenu()->isShown());
234 QTRY_VERIFY(
workspace()->userActionsMenu()->isShown());
238void GlobalShortcutsTest::testMetaShiftW()
241 std::unique_ptr<QAction> action(
new QAction(
nullptr));
242 action->setProperty(
"componentName", QStringLiteral(
"kwin"));
243 action->setObjectName(QStringLiteral(
"globalshortcuts-test-meta-shift-w"));
244 QSignalSpy triggeredSpy(action.get(), &QAction::triggered);
245 KGlobalAccel::self()->setShortcut(action.get(), QList<QKeySequence>{Qt::META | Qt::SHIFT | Qt::Key_W}, KGlobalAccel::NoAutoloading);
248 quint32 timestamp = 0;
250 QCOMPARE(
input()->keyboardModifiers(), Qt::MetaModifier);
252 QCOMPARE(
input()->keyboardModifiers(), Qt::ShiftModifier | Qt::MetaModifier);
254 QTRY_COMPARE(triggeredSpy.count(), 1);
262void GlobalShortcutsTest::testComponseKey()
265 std::unique_ptr<QAction> action(
new QAction(
nullptr));
266 action->setProperty(
"componentName", QStringLiteral(
"kwin"));
267 action->setObjectName(QStringLiteral(
"globalshortcuts-accent"));
268 QSignalSpy triggeredSpy(action.get(), &QAction::triggered);
269 KGlobalAccel::self()->setShortcut(action.get(), QList<QKeySequence>{Qt::NoModifier}, KGlobalAccel::NoAutoloading);
272 quint32 timestamp = 0;
276 QTRY_COMPARE(triggeredSpy.count(), 0);
279void GlobalShortcutsTest::testKeypad()
281 auto zeroAction = std::make_unique<QAction>();
282 zeroAction->setProperty(
"componentName", QStringLiteral(
"kwin"));
283 zeroAction->setObjectName(QStringLiteral(
"globalshortcuts-test-keypad-0"));
284 QSignalSpy zeroActionTriggeredSpy(zeroAction.get(), &QAction::triggered);
285 KGlobalAccel::self()->setShortcut(zeroAction.get(), QList<QKeySequence>{Qt::MetaModifier | Qt::KeypadModifier | Qt::Key_0}, KGlobalAccel::NoAutoloading);
287 auto insertAction = std::make_unique<QAction>();
288 insertAction->setProperty(
"componentName", QStringLiteral(
"kwin"));
289 insertAction->setObjectName(QStringLiteral(
"globalshortcuts-test-keypad-ins"));
290 QSignalSpy insertActionTriggeredSpy(insertAction.get(), &QAction::triggered);
291 KGlobalAccel::self()->setShortcut(insertAction.get(), QList<QKeySequence>{Qt::MetaModifier | Qt::KeypadModifier | Qt::Key_Insert}, KGlobalAccel::NoAutoloading);
294 quint32 timestamp = 0;
302 QTRY_COMPARE(zeroActionTriggeredSpy.count(), 1);
303 QCOMPARE(insertActionTriggeredSpy.count(), 0);
313 QTRY_COMPARE(insertActionTriggeredSpy.count(), 1);
314 QCOMPARE(zeroActionTriggeredSpy.count(), 1);
317void GlobalShortcutsTest::testX11WindowShortcut()
321 QVERIFY(!xcb_connection_has_error(c.get()));
322 xcb_window_t windowId = xcb_generate_id(c.get());
323 const QRect windowGeometry = QRect(0, 0, 10, 20);
324 const uint32_t values[] = {
325 XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW};
326 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId,
rootWindow(),
329 windowGeometry.width(),
330 windowGeometry.height(),
331 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, XCB_CW_EVENT_MASK, values);
332 xcb_size_hints_t hints;
333 memset(&hints, 0,
sizeof(hints));
334 xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y());
335 xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height());
336 xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
337 NETWinInfo info(c.get(), windowId,
rootWindow(), NET::WMAllProperties, NET::WM2AllProperties);
338 info.setWindowType(NET::Normal);
339 xcb_map_window(c.get(), windowId);
343 QVERIFY(windowCreatedSpy.wait());
347 QCOMPARE(
workspace()->activeWindow(), window);
349 QCOMPARE(window->
shortcut(), QKeySequence());
350 const QKeySequence seq(Qt::META | Qt::SHIFT | Qt::Key_Y);
351 QVERIFY(
workspace()->shortcutAvailable(seq));
354 QVERIFY(!
workspace()->shortcutAvailable(seq));
355 QCOMPARE(window->
caption(), QStringLiteral(
" {Meta+Shift+Y}"));
358 QCoreApplication::processEvents();
365 quint32 timestamp = 0;
369 QTRY_COMPARE(
workspace()->activeWindow(), window);
375 QSignalSpy windowClosedSpy(window, &X11Window::closed);
376 xcb_unmap_window(c.get(), windowId);
377 xcb_destroy_window(c.get(), windowId);
379 QVERIFY(windowClosedSpy.wait());
382void GlobalShortcutsTest::testWaylandWindowShortcut()
388 QCOMPARE(
workspace()->activeWindow(), window);
390 QCOMPARE(window->
shortcut(), QKeySequence());
391 const QKeySequence seq(Qt::META | Qt::SHIFT | Qt::Key_Y);
392 QVERIFY(
workspace()->shortcutAvailable(seq));
395 QVERIFY(!
workspace()->shortcutAvailable(seq));
396 QCOMPARE(window->
caption(), QStringLiteral(
" {Meta+Shift+Y}"));
403 quint32 timestamp = 0;
407 QTRY_COMPARE(
workspace()->activeWindow(), window);
412 shellSurface.reset();
415 QTRY_VERIFY_WITH_TIMEOUT(
workspace()->shortcutAvailable(seq), 500);
418void GlobalShortcutsTest::testSetupWindowShortcut()
426 QCOMPARE(
workspace()->activeWindow(), window);
428 QCOMPARE(window->
shortcut(), QKeySequence());
432 QTRY_COMPARE(shortcutDialogAddedSpy.count(), 1);
433 auto dialog = shortcutDialogAddedSpy.first().first().value<
InternalWindow *>();
435 QVERIFY(dialog->isInternal());
437 QVERIFY(sequenceEdit);
438 QTRY_VERIFY(sequenceEdit->hasFocus());
440 quint32 timestamp = 0;
453 QTRY_COMPARE(window->
shortcut(), QKeySequence(Qt::META | Qt::SHIFT | Qt::Key_Y));
457#include "globalshortcuts_test.moc"
void setRepeatInfo(qint32 charactersPerSecond, qint32 delay)
KeyboardInterface * keyboard() const
SeatInterface * seat() const
const QKeySequence & shortcut() const
void setShortcut(const QString &cut)
void activateWindow(Window *window, bool force=false)
void windowAdded(KWin::Window *)
ShortcutDialog * shortcutDialog() const
void setActiveOutput(Output *output)
void slotSetupWindowShortcut()
bool switchToLayout(xkb_layout_index_t layout)
#define WAYLANDTEST_MAIN(TestObject)
Window * renderAndWaitForShown(KWayland::Client::Surface *surface, const QSize &size, const QColor &color, const QImage::Format &format=QImage::Format_ARGB32, int timeout=5000)
void keyboardKeyReleased(quint32 key, quint32 time)
void destroyWaylandConnection()
void setOutputConfig(const QList< QRect > &geometries)
void keyboardKeyPressed(quint32 key, quint32 time)
bool setupWaylandConnection(AdditionalWaylandInterfaces flags=AdditionalWaylandInterfaces())
XcbConnectionPtr createX11Connection()
std::unique_ptr< KWayland::Client::Surface > createSurface()
XdgToplevel * createXdgToplevelSurface(KWayland::Client::Surface *surface, QObject *parent=nullptr)
std::unique_ptr< xcb_connection_t, XcbConnectionDeleter > XcbConnectionPtr
bool waitForWindowClosed(Window *window)
KWIN_EXPORT xcb_window_t rootWindow()
WaylandServer * waylandServer()
InputRedirection * input()