diff --git a/autotests/client/test_fake_input.cpp b/autotests/client/test_fake_input.cpp --- a/autotests/client/test_fake_input.cpp +++ b/autotests/client/test_fake_input.cpp @@ -52,6 +52,8 @@ void testAxis_data(); void testAxis(); void testTouch(); + void testKeyboardKeyLinux_data(); + void testKeyboardKeyLinux(); private: Display *m_display = nullptr; @@ -423,6 +425,57 @@ QCOMPARE(touchCancelSpy.count(), 1); } +void FakeInputTest::testKeyboardKeyLinux_data() +{ + QTest::addColumn("linuxKey"); + + QTest::newRow("A") << quint32(KEY_A); + QTest::newRow("S") << quint32(KEY_S); + QTest::newRow("D") << quint32(KEY_D); + QTest::newRow("F") << quint32(KEY_F); +} + +void FakeInputTest::testKeyboardKeyLinux() +{ + // this test verifies that keyboard key events are properly passed to the server with Qt button codes + QVERIFY(!m_device->isAuthenticated()); + QSignalSpy pressedSpy(m_device, &FakeInputDevice::keyboardKeyPressRequested); + QVERIFY(pressedSpy.isValid()); + QSignalSpy releasedSpy(m_device, &FakeInputDevice::keyboardKeyReleaseRequested); + QVERIFY(releasedSpy.isValid()); + + // without an authentication we shouldn't get the signals + QFETCH(quint32, linuxKey); + m_fakeInput->requestKeyboardKeyPress(linuxKey); + m_fakeInput->requestKeyboardKeyRelease(linuxKey); + QVERIFY(!pressedSpy.wait(100)); + QVERIFY(pressedSpy.isEmpty()); + QVERIFY(releasedSpy.isEmpty()); + + // now authenticate + m_device->setAuthentication(true); + // now our click should work + m_fakeInput->requestKeyboardKeyPress(linuxKey); + m_fakeInput->requestKeyboardKeyRelease(linuxKey); + QVERIFY(releasedSpy.wait()); + QCOMPARE(pressedSpy.count(), 1); + QCOMPARE(releasedSpy.count(), 1); + QTEST(pressedSpy.last().first().value(), "linuxKey"); + QTEST(releasedSpy.last().first().value(), "linuxKey"); + + // and a press/release "manually" + m_fakeInput->requestKeyboardKeyPress(linuxKey); + QVERIFY(pressedSpy.wait()); + QCOMPARE(pressedSpy.count(), 2); + QCOMPARE(releasedSpy.count(), 1); + QTEST(pressedSpy.last().first().value(), "linuxKey"); + // and release + m_fakeInput->requestKeyboardKeyRelease(linuxKey); + QVERIFY(releasedSpy.wait()); + QCOMPARE(pressedSpy.count(), 2); + QCOMPARE(releasedSpy.count(), 2); + QTEST(releasedSpy.last().first().value(), "linuxKey"); +} QTEST_GUILESS_MAIN(FakeInputTest) #include "test_fake_input.moc" diff --git a/src/client/fakeinput.h b/src/client/fakeinput.h --- a/src/client/fakeinput.h +++ b/src/client/fakeinput.h @@ -210,6 +210,21 @@ **/ void requestTouchFrame(); + /** + * Request a keyboard key press. + * @param linuxButton The button code as defined in linux/input-event-codes.h + * + * @since 5.63 + **/ + void requestKeyboardKeyPress(quint32 linuxKey); + /** + * Request a keyboard button release. + * @param linuxButton The button code as defined in linux/input-event-codes.h + * + * @since 5.63 + **/ + void requestKeyboardKeyRelease(quint32 linuxKey); + operator org_kde_kwin_fake_input*(); operator org_kde_kwin_fake_input*() const; diff --git a/src/client/fakeinput.cpp b/src/client/fakeinput.cpp --- a/src/client/fakeinput.cpp +++ b/src/client/fakeinput.cpp @@ -216,6 +216,27 @@ org_kde_kwin_fake_input_touch_frame(d->manager); } +void FakeInput::requestKeyboardKeyPress(quint32 linuxKey) +{ + Q_ASSERT(d->manager.isValid()); + if (wl_proxy_get_version(d->manager) < ORG_KDE_KWIN_FAKE_INPUT_KEYBOARD_KEY_SINCE_VERSION) { + return; + } + + org_kde_kwin_fake_input_keyboard_key(d->manager, linuxKey, WL_KEYBOARD_KEY_STATE_PRESSED); +} + +void FakeInput::requestKeyboardKeyRelease(quint32 linuxKey) +{ + Q_ASSERT(d->manager.isValid()); + if (wl_proxy_get_version(d->manager) < ORG_KDE_KWIN_FAKE_INPUT_KEYBOARD_KEY_SINCE_VERSION) { + return; + } + + org_kde_kwin_fake_input_keyboard_key(d->manager, linuxKey, WL_KEYBOARD_KEY_STATE_RELEASED); +} + + FakeInput::operator org_kde_kwin_fake_input*() const { return d->manager; diff --git a/src/client/protocols/fake-input.xml b/src/client/protocols/fake-input.xml --- a/src/client/protocols/fake-input.xml +++ b/src/client/protocols/fake-input.xml @@ -16,7 +16,7 @@ You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . ]]> - + This interface allows other processes to provide fake input events. Purpose is on the one hand side to provide testing facilities like XTest on X11. @@ -87,5 +87,9 @@ + + + + diff --git a/src/client/registry.cpp b/src/client/registry.cpp --- a/src/client/registry.cpp +++ b/src/client/registry.cpp @@ -206,7 +206,7 @@ &Registry::remoteAccessManagerRemoved }}, {Registry::Interface::FakeInput, { - 3, + 4, QByteArrayLiteral("org_kde_kwin_fake_input"), &org_kde_kwin_fake_input_interface, &Registry::fakeInputAnnounced, diff --git a/src/server/fakeinput_interface.h b/src/server/fakeinput_interface.h --- a/src/server/fakeinput_interface.h +++ b/src/server/fakeinput_interface.h @@ -161,6 +161,18 @@ * @since 5.23 **/ void touchFrameRequested(); + /** + * Requests a keyboard key pressed for @p key. + * + * @since 5.63 + **/ + void keyboardKeyPressRequested(quint32 key); + /** + * Requests a keyboard key release for @p key. + * + * @since 5.63 + **/ + void keyboardKeyReleaseRequested(quint32 key); private: friend class FakeInputInterface; diff --git a/src/server/fakeinput_interface.cpp b/src/server/fakeinput_interface.cpp --- a/src/server/fakeinput_interface.cpp +++ b/src/server/fakeinput_interface.cpp @@ -50,6 +50,7 @@ static void touchUpCallback(wl_client *client, wl_resource *resource, quint32 id); static void touchCancelCallback(wl_client *client, wl_resource *resource); static void touchFrameCallback(wl_client *client, wl_resource *resource); + static void keyboardKeyCallback(wl_client *client, wl_resource *resource, uint32_t button, uint32_t state); static void unbind(wl_resource *resource); static Private *cast(wl_resource *r) { @@ -75,7 +76,7 @@ FakeInputDevice *q; }; -const quint32 FakeInputInterface::Private::s_version = 3; +const quint32 FakeInputInterface::Private::s_version = 4; QList FakeInputInterface::Private::touchIds = QList(); #ifndef DOXYGEN_SHOULD_SKIP_THIS @@ -89,7 +90,8 @@ touchUpCallback, touchCancelCallback, touchFrameCallback, - pointerMotionAbsoluteCallback + pointerMotionAbsoluteCallback, + keyboardKeyCallback }; #endif @@ -265,6 +267,26 @@ emit d->touchFrameRequested(); } +void FakeInputInterface::Private::keyboardKeyCallback(wl_client *client, wl_resource *resource, uint32_t button, uint32_t state) +{ + Q_UNUSED(client) + FakeInputDevice *d = device(resource); + if (!d || !d->isAuthenticated()) { + return; + } + switch (state) { + case WL_KEYBOARD_KEY_STATE_PRESSED: + emit d->keyboardKeyPressRequested(button); + break; + case WL_KEYBOARD_KEY_STATE_RELEASED: + emit d->keyboardKeyReleaseRequested(button); + break; + default: + // nothing + break; + } +} + FakeInputInterface::FakeInputInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) {