Only repeat one key

Summary:
So far KWin tried to repeat all pressed keys which should repeat. But
this is not how X11 and e.g. QtWayland handle it. There only one key -
the last one which got pressed - repeats. And this makes sense as the
key is used to generate a keysym and that one KWin caches. Thus the
logic so far resulted in incorrect keysyms to be generated during the
repeat. E.g. pressing a, pressing b, releasing b would repeat b instead
of the hold a as b was the last generated keysym.

This change addresses this problem and let's only one key repeat at a
time. When the currently repeating key gets released the repeat timer is
stopped and other hold keys won't repeat any more. This also matches the
behavior of X11 and QtWayland.

BUG: 369091
FIXED-IN: 5.8.1

Reviewers: #kwin, #plasma_on_wayland

Subscribers: plasma-devel, kwin

Tags: #plasma_on_wayland, #kwin

Differential Revision: https://phabricator.kde.org/D2941
icc-effect-5.14.5
Martin Gräßlin 2016-10-05 08:26:29 +02:00
parent 26ad65b1e9
commit 3bc6089394
3 changed files with 24 additions and 25 deletions

View File

@ -133,7 +133,6 @@ void GlobalShortcutsTest::testRepeatedTrigger()
QVERIFY(triggeredSpy.wait());
// now release the key
kwinApp()->platform()->keyboardKeyReleased(KEY_5, timestamp++);
QEXPECT_FAIL("", "BUG 369091", Continue);
QVERIFY(!triggeredSpy.wait(500));
kwinApp()->platform()->keyboardKeyReleased(KEY_WAKEUP, timestamp++);

View File

@ -463,17 +463,25 @@ KeyboardInputRedirection::KeyboardInputRedirection(InputRedirection *parent)
{
}
KeyboardInputRedirection::~KeyboardInputRedirection()
{
qDeleteAll(m_repeatTimers);
m_repeatTimers.clear();
}
KeyboardInputRedirection::~KeyboardInputRedirection() = default;
void KeyboardInputRedirection::init()
{
Q_ASSERT(!m_inited);
m_inited = true;
// setup key repeat
m_keyRepeat.timer = new QTimer(this);
connect(m_keyRepeat.timer, &QTimer::timeout, this,
[this] {
if (waylandServer()->seat()->keyRepeatRate() != 0) {
m_keyRepeat.timer->setInterval(1000 / waylandServer()->seat()->keyRepeatRate());
}
// TODO: better time
processKey(m_keyRepeat.key, InputRedirection::KeyboardKeyAutoRepeat, m_keyRepeat.time);
}
);
connect(workspace(), &QObject::destroyed, this, [this] { m_inited = false; });
connect(waylandServer(), &QObject::destroyed, this, [this] { m_inited = false; });
connect(workspace(), &Workspace::clientActivated, this,
@ -615,26 +623,14 @@ void KeyboardInputRedirection::processKey(uint32_t key, InputRedirection::Keyboa
device);
if (state == InputRedirection::KeyboardKeyPressed) {
if (m_xkb->shouldKeyRepeat(key) && 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();
m_keyRepeat.timer->setInterval(waylandServer()->seat()->keyRepeatDelay());
m_keyRepeat.key = key;
m_keyRepeat.time = time;
m_keyRepeat.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);
if (key == m_keyRepeat.key) {
m_keyRepeat.timer->stop();
}
}

View File

@ -140,8 +140,12 @@ private:
InputRedirection *m_input;
bool m_inited = false;
QScopedPointer<Xkb> m_xkb;
QHash<quint32, QTimer*> m_repeatTimers;
QMetaObject::Connection m_activeClientSurfaceChangedConnection;
struct {
quint32 key = 0;
quint32 time = 0;
QTimer *timer = nullptr;
} m_keyRepeat;
};
inline