File: upstream_801d31a9_plugins-stickykeys-Unlatch-keys-after-mouse-click.patch

package info (click to toggle)
kwin 4%3A6.3.6-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 56,600 kB
  • sloc: cpp: 241,670; xml: 3,228; javascript: 2,214; ansic: 775; sh: 67; python: 15; makefile: 8
file content (136 lines) | stat: -rw-r--r-- 4,956 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
From 801d31a926a4ad7ceeeecc64b273cd514550d613 Mon Sep 17 00:00:00 2001
From: Nicolas Fella <nicolas.fella@gmx.de>
Date: Tue, 9 Jul 2024 12:41:14 +0200
Subject: [PATCH] plugins/stickykeys: Unlatch keys after mouse click

This is how X11 behaves and it allows things like Ctrl+Click to work as expected with sticky keys
---
 autotests/integration/sticky_keys_test.cpp | 57 ++++++++++++++++++++++
 src/plugins/stickykeys/stickykeys.cpp      | 27 ++++++++++
 src/plugins/stickykeys/stickykeys.h        |  1 +
 3 files changed, 85 insertions(+)

--- a/autotests/integration/sticky_keys_test.cpp
+++ b/autotests/integration/sticky_keys_test.cpp
@@ -37,6 +37,8 @@ private Q_SLOTS:
     void testStick_data();
     void testLock();
     void testLock_data();
+    void testMouse();
+    void testMouse_data();
     void testDisableTwoKeys();
 };
 
@@ -280,6 +282,61 @@ void StickyKeysTest::testDisableTwoKeys(
     Test::keyboardKeyReleased(KEY_A, ++timestamp);
     QVERIFY(!modifierSpy.wait(10));
 }
+
+void StickyKeysTest::testMouse_data()
+{
+    QTest::addColumn<int>("modifierKey");
+    QTest::addColumn<int>("expectedMods");
+
+    QTest::addRow("Shift") << KEY_LEFTSHIFT << 1;
+    QTest::addRow("Ctrl") << KEY_LEFTCTRL << 4;
+    QTest::addRow("Alt") << KEY_LEFTALT << 8;
+    QTest::addRow("AltGr") << KEY_RIGHTALT << 128;
+}
+
+void StickyKeysTest::testMouse()
+{
+    QFETCH(int, modifierKey);
+    QFETCH(int, expectedMods);
+
+    std::unique_ptr<KWayland::Client::Keyboard> keyboard(Test::waylandSeat()->createKeyboard());
+
+    std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
+    QVERIFY(surface != nullptr);
+    std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
+    QVERIFY(shellSurface != nullptr);
+    Window *waylandWindow = Test::renderAndWaitForShown(surface.get(), QSize(10, 10), Qt::blue);
+    QVERIFY(waylandWindow);
+
+    QSignalSpy modifierSpy(keyboard.get(), &KWayland::Client::Keyboard::modifiersChanged);
+    QVERIFY(modifierSpy.wait());
+    modifierSpy.clear();
+
+    quint32 timestamp = 0;
+
+    // press mod to latch it
+    Test::keyboardKeyPressed(modifierKey, ++timestamp);
+    QVERIFY(modifierSpy.wait());
+    // arguments are: quint32 depressed, quint32 latched, quint32 locked, quint32 group
+    QCOMPARE(modifierSpy.first()[0], expectedMods); // verify that mod is depressed
+    QCOMPARE(modifierSpy.first()[1], expectedMods); // verify that mod is latched
+
+    modifierSpy.clear();
+    // release mod, the modifier should still be latched
+    Test::keyboardKeyReleased(modifierKey, ++timestamp);
+    QVERIFY(modifierSpy.wait());
+    QCOMPARE(modifierSpy.first()[0], 0); // verify that mod is not depressed
+    QCOMPARE(modifierSpy.first()[1], expectedMods); // verify that mod is still latched
+
+    // press and release a mouse button, this unlatches the modifier
+    modifierSpy.clear();
+    Test::pointerButtonPressed(BTN_LEFT, ++timestamp);
+    QVERIFY(!modifierSpy.wait(10));
+    Test::pointerButtonReleased(BTN_LEFT, ++timestamp);
+    QVERIFY(modifierSpy.wait());
+    QCOMPARE(modifierSpy.first()[0], 0); // verify that mod is not depressed
+    QCOMPARE(modifierSpy.first()[1], 0); // verify that mod is not latched any more
+}
 }
 
 WAYLANDTEST_MAIN(KWin::StickyKeysTest)
--- a/src/plugins/stickykeys/stickykeys.cpp
+++ b/src/plugins/stickykeys/stickykeys.cpp
@@ -9,6 +9,8 @@
 #include "keyboard_input.h"
 #include "xkb.h"
 
+#include <QTimer>
+
 #include <KLazyLocalizedString>
 #if KWIN_BUILD_NOTIFICATIONS
 #include <KNotification>
@@ -183,4 +185,29 @@ void StickyKeysFilter::disableStickyKeys
     KWin::input()->uninstallInputEventFilter(this);
 }
 
+bool StickyKeysFilter::pointerButton(KWin::PointerButtonEvent *event)
+{
+    if (event->state == KWin::PointerButtonState::Released) {
+        // unlatch all unlocked modifiers
+        for (auto it = m_keyStates.keyValueBegin(); it != m_keyStates.keyValueEnd(); ++it) {
+
+            if (it->second == Locked) {
+                continue;
+            }
+
+            it->second = KeyState::None;
+
+            KWin::input()->keyboard()->xkb()->setModifierLatched(keyToModifier(static_cast<Qt::Key>(it->first)), false);
+
+            // We need to delay the modifier update until the client received the mouse event, otherwise
+            // the updated modifiers arrive before the mouse event and e.g. Ctrl+Click won't work
+            QTimer::singleShot(0, this, [] {
+                KWin::input()->keyboard()->xkb()->forwardModifiers();
+            });
+        }
+    }
+
+    return false;
+}
+
 #include "moc_stickykeys.cpp"
--- a/src/plugins/stickykeys/stickykeys.h
+++ b/src/plugins/stickykeys/stickykeys.h
@@ -18,6 +18,7 @@ public:
     explicit StickyKeysFilter();
 
     bool keyboardKey(KWin::KeyboardKeyEvent *event) override;
+    bool pointerButton(KWin::PointerButtonEvent *event) override;
 
     enum KeyState {
         None,