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
This commit is contained in:
Martin Flöser 2017-07-22 12:49:57 +02:00
parent f88c322a3b
commit 70bc9524d9
3 changed files with 57 additions and 2 deletions

View file

@ -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> surface(Test::createSurface());
QScopedPointer<ShellSurface> 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<ShellClient*>();
QVERIFY(dialog);
QVERIFY(dialog->isInternal());
auto sequenceEdit = workspace()->shortcutDialog()->findChild<QKeySequenceEdit*>();
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"

View file

@ -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;

View file

@ -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