From 70bc9524d95b56c8ab569b26f596640214839070 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Fl=C3=B6ser?= Date: Sat, 22 Jul 2017 12:49:57 +0200 Subject: [PATCH] Send QKeyEvent with Qt::Key as expected by Qt to internal windows Summary: KWin passes the current keysym converted to a Qt::Key in the QKeyEvent. The current keysym does not always change when a key gets released, so when pressing a shortcut the release carry a Qt::Key which could be considered as wrong. QtWayland transforms the actual pressed/released key into a keysym and passes that through the QKeyEvent. This change does the same for the internal windows. A new QKeyEvent is created and adjusted in a way that it matches what Qt expects. Why not change everything to how Qt expects it? The key is used at various places and in KWin internally we expect the behavior how it is currently implemented. So it's better to use Qt's expectation only when interacting with Qt. Also the change carries a workaround for a bug in QKeySequenceEdit (see QTBUG-62102) and transforms Super to Meta. As this adjustment only makes sense for the internal windows we need to send in an adjusted QKeyEvent anyway, so another argument for using the Qt behavior only in this place. Test Plan: Can set a shortcut on Wayland and it can be used to activate the window. Reviewers: #kwin, #plasma Subscribers: plasma-devel Tags: #plasma Differential Revision: https://phabricator.kde.org/D6828 --- .../integration/globalshortcuts_test.cpp | 42 +++++++++++++++++++ input.cpp | 13 +++++- workspace.h | 4 ++ 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/autotests/integration/globalshortcuts_test.cpp b/autotests/integration/globalshortcuts_test.cpp index 14a924a852..11a7199c3a 100644 --- a/autotests/integration/globalshortcuts_test.cpp +++ b/autotests/integration/globalshortcuts_test.cpp @@ -57,6 +57,7 @@ private Q_SLOTS: void testMetaShiftW(); void testX11ClientShortcut(); void testWaylandClientShortcut(); + void testSetupWindowShortcut(); }; void GlobalShortcutsTest::initTestCase() @@ -314,6 +315,47 @@ void GlobalShortcutsTest::testWaylandClientShortcut() QVERIFY(workspace()->shortcutAvailable(seq)); } +void GlobalShortcutsTest::testSetupWindowShortcut() +{ + QScopedPointer surface(Test::createSurface()); + QScopedPointer shellSurface(Test::createShellSurface(surface.data())); + auto client = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue); + + QCOMPARE(workspace()->activeClient(), client); + QVERIFY(client->isActive()); + QCOMPARE(client->shortcut(), QKeySequence()); + + QSignalSpy shortcutDialogAddedSpy(waylandServer(), &WaylandServer::shellClientAdded); + QVERIFY(shortcutDialogAddedSpy.isValid()); + workspace()->slotSetupWindowShortcut(); + QTRY_COMPARE(shortcutDialogAddedSpy.count(), 1); + auto dialog = shortcutDialogAddedSpy.first().first().value(); + QVERIFY(dialog); + QVERIFY(dialog->isInternal()); + auto sequenceEdit = workspace()->shortcutDialog()->findChild(); + QVERIFY(sequenceEdit); + + // the QKeySequenceEdit field does not get focus, we need to pass it focus manually + QEXPECT_FAIL("", "Edit does not have focus", Continue); + QVERIFY(sequenceEdit->hasFocus()); + sequenceEdit->setFocus(); + QTRY_VERIFY(sequenceEdit->hasFocus()); + + quint32 timestamp = 0; + kwinApp()->platform()->keyboardKeyPressed(KEY_LEFTMETA, timestamp++); + kwinApp()->platform()->keyboardKeyPressed(KEY_LEFTSHIFT, timestamp++); + kwinApp()->platform()->keyboardKeyPressed(KEY_Y, timestamp++); + kwinApp()->platform()->keyboardKeyReleased(KEY_Y, timestamp++); + kwinApp()->platform()->keyboardKeyReleased(KEY_LEFTSHIFT, timestamp++); + kwinApp()->platform()->keyboardKeyReleased(KEY_LEFTMETA, timestamp++); + + // the sequence gets accepted after one second, so wait a bit longer + QTest::qWait(2000); + // now send in enter + kwinApp()->platform()->keyboardKeyPressed(KEY_ENTER, timestamp++); + kwinApp()->platform()->keyboardKeyReleased(KEY_ENTER, timestamp++); + QTRY_COMPARE(client->shortcut(), QKeySequence(Qt::META + Qt::SHIFT + Qt::Key_Y)); +} WAYLANDTEST_MAIN(GlobalShortcutsTest) #include "globalshortcuts_test.moc" diff --git a/input.cpp b/input.cpp index 11b0a4569a..2199acd6a5 100644 --- a/input.cpp +++ b/input.cpp @@ -826,8 +826,17 @@ class InternalWindowEventFilter : public InputEventFilter { if (!found) { return false; } - event->setAccepted(false); - if (QCoreApplication::sendEvent(found, event)) { + auto xkb = input()->keyboard()->xkb(); + Qt::Key key = xkb->toQtKey(xkb->toKeysym(event->nativeScanCode())); + if (key == Qt::Key_Super_L || key == Qt::Key_Super_R) { + // workaround for QTBUG-62102 + key = Qt::Key_Meta; + } + QKeyEvent internalEvent(event->type(), key, + event->modifiers(), event->nativeScanCode(), event->nativeVirtualKey(), + event->nativeModifiers(), event->text()); + internalEvent.setAccepted(false); + if (QCoreApplication::sendEvent(found, &internalEvent)) { waylandServer()->seat()->setFocusedKeyboardSurface(nullptr); passToWaylandServer(event); return true; diff --git a/workspace.h b/workspace.h index c883f74b32..995cd83330 100644 --- a/workspace.h +++ b/workspace.h @@ -379,6 +379,10 @@ public: }; void switchWindow(Direction direction); + ShortcutDialog *shortcutDialog() const { + return client_keys_dialog; + } + public Q_SLOTS: void performWindowOperation(KWin::AbstractClient* c, Options::WindowOperation op); // Keybindings