From cb3c6a47807b1c94e6bf3ba5b45b453b172dddad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Gr=C3=A4=C3=9Flin?= Date: Fri, 19 Feb 2016 07:53:20 +0100 Subject: [PATCH] Implement internal keyboard repeat As a Wayland server KWin does not have to emit additional key repeat events (unlike X11). The clients are responsible for handling this based on the provided key repeat information. Internally KWin needs key repeat, though. E.g. the effects need key repeat (filtering in Present Windows), window moving by keyboard needs repeat, etc. etc. This change introduces the internal key repeat. For each key press a QTimer is started which gets canceled again on the key release. If the timer fires it invoked processKey with a new KeyboardKeyAutoRepeat state. This is handled just like a KeyPress, but states are not updated and the QKeyEvent has autorepeat set to true. The event filters check for the autorepeat state and filter the event out if they are not interested in it. E.g. the filters passing the event to the Wayland client need to filter it out. Currently auto-repeat is bound to using libinput. This needs to be modified. The only backend sending repeated events is X11, thus for other backends it should be enabled. Whether creating a timer on each key event is a good idea is something to evaluate in future. Reviewed-By: Bhushan Shah --- autotests/wayland/lockscreen.cpp | 43 +++++++++++++++++++++ input.cpp | 12 +++++- input.h | 3 +- keyboard_input.cpp | 64 ++++++++++++++++++++++++++++---- keyboard_input.h | 1 + 5 files changed, 112 insertions(+), 11 deletions(-) diff --git a/autotests/wayland/lockscreen.cpp b/autotests/wayland/lockscreen.cpp index b5548e1c05..c3be06f677 100644 --- a/autotests/wayland/lockscreen.cpp +++ b/autotests/wayland/lockscreen.cpp @@ -39,6 +39,7 @@ along with this program. If not, see . #include #include #include +#include //screenlocker #include @@ -64,6 +65,7 @@ private Q_SLOTS: void testScreenEdge(); void testEffects(); void testEffectsKeyboard(); + void testEffectsKeyboardAutorepeat(); void testMoveWindow(); void testPointerShortcut(); void testAxisShortcut_data(); @@ -564,6 +566,47 @@ void LockScreenTest::testEffectsKeyboard() effects->ungrabKeyboard(); } +void LockScreenTest::testEffectsKeyboardAutorepeat() +{ + // this test is just like testEffectsKeyboard, but tests auto repeat key events + // while the key is pressed the Effect should get auto repeated events + // but the lock screen should filter them out + QScopedPointer effect(new HelperEffect); + QSignalSpy inputSpy(effect.data(), &HelperEffect::keyEvent); + QVERIFY(inputSpy.isValid()); + effects->grabKeyboard(effect.data()); + + // we need to configure the key repeat first. It is only enabled on libinput + waylandServer()->seat()->setKeyRepeatInfo(25, 300); + + quint32 timestamp = 1; + KEYPRESS(KEY_A); + QCOMPARE(inputSpy.count(), 1); + QCOMPARE(inputSpy.first().first().toString(), QStringLiteral("a")); + QVERIFY(inputSpy.wait()); + QVERIFY(inputSpy.count() > 1); + // and still more events + QVERIFY(inputSpy.wait()); + QCOMPARE(inputSpy.at(1).first().toString(), QStringLiteral("a")); + + // now release + inputSpy.clear(); + KEYRELEASE(KEY_A); + QCOMPARE(inputSpy.count(), 1); + + // while locked key repeat should not pass any events to the Effect + LOCK + KEYPRESS(KEY_B); + QVERIFY(!inputSpy.wait(200)); + KEYRELEASE(KEY_B); + QVERIFY(!inputSpy.wait(200)); + + UNLOCK + // don't test again, that's covered by testEffectsKeyboard + + effects->ungrabKeyboard(); +} + void LockScreenTest::testMoveWindow() { using namespace KWayland::Client; diff --git a/input.cpp b/input.cpp index ae8afc32f3..d4c2b3e1ba 100644 --- a/input.cpp +++ b/input.cpp @@ -111,7 +111,7 @@ class VirtualTerminalFilter : public InputEventFilter { public: bool keyEvent(QKeyEvent *event) override { // really on press and not on release? X11 switches on press. - if (event->type() == QEvent::KeyPress) { + if (event->type() == QEvent::KeyPress && !event->isAutoRepeat()) { const xkb_keysym_t keysym = event->nativeVirtualKey(); if (keysym >= XKB_KEY_XF86Switch_VT_1 && keysym <= XKB_KEY_XF86Switch_VT_12) { VirtualTerminal::self()->activate(keysym - XKB_KEY_XF86Switch_VT_1 + 1); @@ -162,6 +162,10 @@ public: if (!waylandServer()->isScreenLocked()) { return false; } + if (event->isAutoRepeat()) { + // wayland client takes care of it + return true; + } // send event to KSldApp for global accel // if event is set to accepted it means a whitelisted shortcut was triggered // in that case we filter it out and don't process it further @@ -344,7 +348,7 @@ public: return input()->shortcuts()->processAxis(event->modifiers(), direction); } bool keyEvent(QKeyEvent *event) override { - if (event->type() == QEvent::KeyPress) { + if (event->type() == QEvent::KeyPress && !event->isAutoRepeat()) { return input()->shortcuts()->processKey(event->modifiers(), event->nativeVirtualKey()); } return false; @@ -626,6 +630,10 @@ public: if (!workspace()) { return false; } + if (event->isAutoRepeat()) { + // handled by Wayland client + return false; + } auto seat = waylandServer()->seat(); input()->keyboard()->update(); seat->setTimestamp(event->timestamp()); diff --git a/input.h b/input.h index 1de4d9f9a0..d24829db71 100644 --- a/input.h +++ b/input.h @@ -68,7 +68,8 @@ public: }; enum KeyboardKeyState { KeyboardKeyReleased, - KeyboardKeyPressed + KeyboardKeyPressed, + KeyboardKeyAutoRepeat }; virtual ~InputRedirection(); void init(); diff --git a/keyboard_input.cpp b/keyboard_input.cpp index 489f0a9e7d..57ebb279ac 100644 --- a/keyboard_input.cpp +++ b/keyboard_input.cpp @@ -268,7 +268,11 @@ KeyboardInputRedirection::KeyboardInputRedirection(InputRedirection *parent) { } -KeyboardInputRedirection::~KeyboardInputRedirection() = default; +KeyboardInputRedirection::~KeyboardInputRedirection() +{ + qDeleteAll(m_repeatTimers); + m_repeatTimers.clear(); +} void KeyboardInputRedirection::init() { @@ -327,21 +331,65 @@ void KeyboardInputRedirection::processKey(uint32_t key, InputRedirection::Keyboa if (!m_inited) { return; } - emit m_input->keyStateChanged(key, state); - const Qt::KeyboardModifiers oldMods = modifiers(); - m_xkb->updateKey(key, state); - if (oldMods != modifiers()) { - emit m_input->keyboardModifiersChanged(modifiers(), oldMods); + QEvent::Type type; + bool autoRepeat = false; + switch (state) { + case InputRedirection::KeyboardKeyAutoRepeat: + autoRepeat = true; + // fall through + case InputRedirection::KeyboardKeyPressed: + type = QEvent::KeyPress; + break; + case InputRedirection::KeyboardKeyReleased: + type = QEvent::KeyRelease; + break; + default: + Q_UNREACHABLE(); } + + if (!autoRepeat) { + emit m_input->keyStateChanged(key, state); + const Qt::KeyboardModifiers oldMods = modifiers(); + m_xkb->updateKey(key, state); + if (oldMods != modifiers()) { + emit m_input->keyboardModifiersChanged(modifiers(), oldMods); + } + } + const xkb_keysym_t keySym = m_xkb->toKeysym(key); - QKeyEvent event((state == InputRedirection::KeyboardKeyPressed) ? QEvent::KeyPress : QEvent::KeyRelease, + QKeyEvent event(type, m_xkb->toQtKey(keySym), m_xkb->modifiers(), key, keySym, 0, - m_xkb->toString(m_xkb->toKeysym(key))); + m_xkb->toString(m_xkb->toKeysym(key)), + autoRepeat); event.setTimestamp(time); + if (state == InputRedirection::KeyboardKeyPressed) { + if (waylandServer()->seat()->keyRepeatDelay() != 0) { + QTimer *timer = new QTimer; + timer->setInterval(waylandServer()->seat()->keyRepeatDelay()); + connect(timer, &QTimer::timeout, this, + [this, timer, time, key] { + const int delay = 1000 / waylandServer()->seat()->keyRepeatRate(); + if (timer->interval() != delay) { + timer->setInterval(delay); + } + // TODO: better time + processKey(key, InputRedirection::KeyboardKeyAutoRepeat, time); + } + ); + m_repeatTimers.insert(key, timer); + timer->start(); + } + } else if (state == InputRedirection::KeyboardKeyReleased) { + auto it = m_repeatTimers.find(key); + if (it != m_repeatTimers.end()) { + delete it.value(); + m_repeatTimers.erase(it); + } + } const auto &filters = m_input->filters(); for (auto it = filters.begin(), end = filters.end(); it != end; it++) { diff --git a/keyboard_input.h b/keyboard_input.h index 7cada835cc..d123a0e662 100644 --- a/keyboard_input.h +++ b/keyboard_input.h @@ -108,6 +108,7 @@ private: InputRedirection *m_input; bool m_inited = false; QScopedPointer m_xkb; + QHash m_repeatTimers; }; inline