diff --git a/src/wayland/autotests/client/test_wayland_seat.cpp b/src/wayland/autotests/client/test_wayland_seat.cpp index 1a7739aa23..2d3f156832 100644 --- a/src/wayland/autotests/client/test_wayland_seat.cpp +++ b/src/wayland/autotests/client/test_wayland_seat.cpp @@ -642,12 +642,32 @@ void TestWaylandSeat::testKeyboard() QVERIFY(!m_seatInterface->focusedKeyboard()); Keyboard *keyboard = m_seat->createKeyboard(m_seat); + QSignalSpy repeatInfoSpy(keyboard, &Keyboard::keyRepeatChanged); + QVERIFY(repeatInfoSpy.isValid()); const Keyboard &ckeyboard = *keyboard; QVERIFY(keyboard->isValid()); + QCOMPARE(keyboard->isKeyRepeatEnabled(), false); + QCOMPARE(keyboard->keyRepeatDelay(), 0); + QCOMPARE(keyboard->keyRepeatRate(), 0); wl_display_flush(m_connection->display()); QTest::qWait(100); QVERIFY(m_seatInterface->focusedKeyboard()); + // we should get the repeat info announced + QCOMPARE(repeatInfoSpy.count(), 1); + QCOMPARE(keyboard->isKeyRepeatEnabled(), false); + QCOMPARE(keyboard->keyRepeatDelay(), 0); + QCOMPARE(keyboard->keyRepeatRate(), 0); + + // let's change repeat in server + m_seatInterface->setKeyRepeatInfo(25, 660); + m_seatInterface->focusedKeyboard()->client()->flush(); + QVERIFY(repeatInfoSpy.wait()); + QCOMPARE(repeatInfoSpy.count(), 2); + QCOMPARE(keyboard->isKeyRepeatEnabled(), true); + QCOMPARE(keyboard->keyRepeatRate(), 25); + QCOMPARE(keyboard->keyRepeatDelay(), 660); + m_seatInterface->setTimestamp(1); m_seatInterface->keyPressed(KEY_K); m_seatInterface->setTimestamp(2); @@ -745,6 +765,20 @@ void TestWaylandSeat::testKeyboard() QTest::qWait(100); QVERIFY(!m_seatInterface->focusedKeyboardSurface()); QVERIFY(!m_seatInterface->focusedKeyboard()); + + // create a second Keyboard to verify that repeat info is announced properly + Keyboard *keyboard2 = m_seat->createKeyboard(m_seat); + QSignalSpy repeatInfoSpy2(keyboard2, &Keyboard::keyRepeatChanged); + QVERIFY(repeatInfoSpy2.isValid()); + QVERIFY(keyboard2->isValid()); + QCOMPARE(keyboard2->isKeyRepeatEnabled(), false); + QCOMPARE(keyboard2->keyRepeatDelay(), 0); + QCOMPARE(keyboard2->keyRepeatRate(), 0); + wl_display_flush(m_connection->display()); + QVERIFY(repeatInfoSpy2.wait()); + QCOMPARE(keyboard2->isKeyRepeatEnabled(), true); + QCOMPARE(keyboard2->keyRepeatRate(), 25); + QCOMPARE(keyboard2->keyRepeatDelay(), 660); } void TestWaylandSeat::testCast() diff --git a/src/wayland/autotests/server/test_seat.cpp b/src/wayland/autotests/server/test_seat.cpp index 08064871ea..75f93fe0d3 100644 --- a/src/wayland/autotests/server/test_seat.cpp +++ b/src/wayland/autotests/server/test_seat.cpp @@ -36,6 +36,7 @@ private Q_SLOTS: void testPointerButton(); void testPointerPos(); void testDestroyThroughTerminate(); + void testRepeatInfo(); }; static const QString s_socketName = QStringLiteral("kwin-wayland-server-seat-test-0"); @@ -179,5 +180,22 @@ void TestWaylandServerSeat::testDestroyThroughTerminate() QVERIFY(!destroyedSpy.isEmpty()); } +void TestWaylandServerSeat::testRepeatInfo() +{ + Display display; + display.setSocketName(s_socketName); + display.start(); + SeatInterface *seat = display.createSeat(); + QCOMPARE(seat->keyRepeatRate(), 0); + QCOMPARE(seat->keyRepeatDelay(), 0); + seat->setKeyRepeatInfo(25, 660); + QCOMPARE(seat->keyRepeatRate(), 25); + QCOMPARE(seat->keyRepeatDelay(), 660); + // setting negative values should result in 0 + seat->setKeyRepeatInfo(-25, -660); + QCOMPARE(seat->keyRepeatRate(), 0); + QCOMPARE(seat->keyRepeatDelay(), 0); +} + QTEST_GUILESS_MAIN(TestWaylandServerSeat) #include "test_seat.moc" diff --git a/src/wayland/keyboard_interface.cpp b/src/wayland/keyboard_interface.cpp index 6f7a130e7f..1cf5c83656 100644 --- a/src/wayland/keyboard_interface.cpp +++ b/src/wayland/keyboard_interface.cpp @@ -169,6 +169,16 @@ void KeyboardInterface::updateModifiers(quint32 depressed, quint32 latched, quin d->sendModifiers(depressed, latched, locked, group, serial); } +void KeyboardInterface::repeatInfo(qint32 charactersPerSecond, qint32 delay) +{ + Q_D(); + if (wl_resource_get_version(d->resource) < 4) { + // only supported since version 4 + return; + } + wl_keyboard_send_repeat_info(d->resource, charactersPerSecond, delay); +} + SurfaceInterface *KeyboardInterface::focusedSurface() const { Q_D(); diff --git a/src/wayland/keyboard_interface.h b/src/wayland/keyboard_interface.h index bb17589e45..2921f73152 100644 --- a/src/wayland/keyboard_interface.h +++ b/src/wayland/keyboard_interface.h @@ -46,6 +46,7 @@ private: void updateModifiers(quint32 depressed, quint32 latched, quint32 locked, quint32 group, quint32 serial); void keyPressed(quint32 key, quint32 serial); void keyReleased(quint32 key, quint32 serial); + void repeatInfo(qint32 charactersPerSecond, qint32 delay); friend class SeatInterface; explicit KeyboardInterface(SeatInterface *parent, wl_resource *parentResource); diff --git a/src/wayland/seat_interface.cpp b/src/wayland/seat_interface.cpp index f023f3397c..88e3859eea 100644 --- a/src/wayland/seat_interface.cpp +++ b/src/wayland/seat_interface.cpp @@ -38,7 +38,10 @@ namespace KWayland namespace Server { -static const quint32 s_version = 3; +static const quint32 s_version = 4; +static const qint32 s_pointerVersion = 3; +static const qint32 s_touchVersion = 3; +static const qint32 s_keyboardVersion = 4; SeatInterface::Private::Private(SeatInterface *q, Display *display) : Global::Private(display, &wl_seat_interface, s_version) @@ -303,7 +306,7 @@ void SeatInterface::Private::getPointer(wl_client *client, wl_resource *resource { // TODO: only create if seat has pointer? PointerInterface *pointer = new PointerInterface(q, resource); - pointer->create(display->getConnection(client), wl_resource_get_version(resource), id); + pointer->create(display->getConnection(client), qMin(wl_resource_get_version(resource), s_pointerVersion), id); if (!pointer->resource()) { wl_resource_post_no_memory(resource); delete pointer; @@ -337,12 +340,13 @@ void SeatInterface::Private::getKeyboard(wl_client *client, wl_resource *resourc { // TODO: only create if seat has keyboard? KeyboardInterface *keyboard = new KeyboardInterface(q, resource); - keyboard->create(display->getConnection(client), wl_resource_get_version(resource), id); + keyboard->create(display->getConnection(client), qMin(wl_resource_get_version(resource), s_keyboardVersion) , id); if (!keyboard->resource()) { wl_resource_post_no_memory(resource); delete keyboard; return; } + keyboard->repeatInfo(keys.keyRepeat.charactersPerSecond, keys.keyRepeat.delay); if (keys.keymap.xkbcommonCompatible) { keyboard->setKeymap(keys.keymap.fd, keys.keymap.size); } @@ -374,7 +378,7 @@ void SeatInterface::Private::getTouch(wl_client *client, wl_resource *resource, { // TODO: only create if seat has touch? TouchInterface *touch = new TouchInterface(q, resource); - touch->create(display->getConnection(client), wl_resource_get_version(resource), id); + touch->create(display->getConnection(client), qMin(wl_resource_get_version(resource), s_touchVersion), id); if (!touch->resource()) { wl_resource_post_no_memory(resource); delete touch; @@ -718,6 +722,28 @@ void SeatInterface::updateKeyboardModifiers(quint32 depressed, quint32 latched, } } +void SeatInterface::setKeyRepeatInfo(qint32 charactersPerSecond, qint32 delay) +{ + Q_D(); + d->keys.keyRepeat.charactersPerSecond = qMax(charactersPerSecond, 0); + d->keys.keyRepeat.delay = qMax(delay, 0); + for (auto it = d->keyboards.constBegin(); it != d->keyboards.constEnd(); ++it) { + (*it)->repeatInfo(d->keys.keyRepeat.charactersPerSecond, d->keys.keyRepeat.delay); + } +} + +qint32 SeatInterface::keyRepeatDelay() const +{ + Q_D(); + return d->keys.keyRepeat.delay; +} + +qint32 SeatInterface::keyRepeatRate() const +{ + Q_D(); + return d->keys.keyRepeat.charactersPerSecond; +} + bool SeatInterface::isKeymapXkbCompatible() const { Q_D(); diff --git a/src/wayland/seat_interface.h b/src/wayland/seat_interface.h index f7f19e04b3..36adfed828 100644 --- a/src/wayland/seat_interface.h +++ b/src/wayland/seat_interface.h @@ -89,6 +89,19 @@ public: void keyPressed(quint32 key); void keyReleased(quint32 key); void updateKeyboardModifiers(quint32 depressed, quint32 latched, quint32 locked, quint32 group); + /** + * Sets the key repeat information to be forwarded to all bound keyboards. + * + * To disable key repeat set a @p charactersPerSecond of @c 0. + * + * Requires wl_seat version 4. + * + * @param charactersPerSecond The characters per second rate, value of @c 0 disables key repeating + * @param delay The delay on key press before starting repeating keys + * + * @since 5.5 + ***/ + void setKeyRepeatInfo(qint32 charactersPerSecond, qint32 delay); quint32 depressedModifiers() const; quint32 latchedModifiers() const; quint32 lockedModifiers() const; @@ -98,6 +111,20 @@ public: quint32 keymapSize() const; bool isKeymapXkbCompatible() const; QVector pressedKeys() const; + /** + * @returns The key repeat in character per second + * @since 5.5 + * @see setKeyRepeatInfo + * @see keyRepeatDelay + **/ + qint32 keyRepeatRate() const; + /** + * @returns The delay on key press before starting repeating keys + * @since 5.5 + * @see keyRepeatRate + * @see setKeyRepeatInfo + **/ + qint32 keyRepeatDelay() const; void setFocusedKeyboardSurface(SurfaceInterface *surface); SurfaceInterface *focusedKeyboardSurface() const; diff --git a/src/wayland/seat_interface_p.h b/src/wayland/seat_interface_p.h index b4f23c6f55..cc46844b7d 100644 --- a/src/wayland/seat_interface_p.h +++ b/src/wayland/seat_interface_p.h @@ -112,6 +112,10 @@ public: }; Focus focus; quint32 lastStateSerial = 0; + struct { + qint32 charactersPerSecond = 0; + qint32 delay = 0; + } keyRepeat; }; Keyboard keys; void updateKey(quint32 key, Keyboard::State state);