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