diff --git a/autotests/client/test_fake_input.cpp b/autotests/client/test_fake_input.cpp index 999e970..e94be60 100644 --- a/autotests/client/test_fake_input.cpp +++ b/autotests/client/test_fake_input.cpp @@ -1,321 +1,402 @@ /******************************************************************** Copyright 2016 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ // Qt #include // client #include "../../src/client/connection_thread.h" #include "../../src/client/event_queue.h" #include "../../src/client/fakeinput.h" #include "../../src/client/registry.h" // server #include "../../src/server/display.h" #include "../../src/server/fakeinput_interface.h" #include using namespace KWayland::Client; using namespace KWayland::Server; Q_DECLARE_METATYPE(Qt::MouseButton) class FakeInputTest : public QObject { Q_OBJECT private Q_SLOTS: void init(); void cleanup(); void testAuthenticate(); void testMotion(); void testPointerButtonQt_data(); void testPointerButtonQt(); void testPointerButtonLinux_data(); void testPointerButtonLinux(); void testAxis_data(); void testAxis(); + void testTouch(); private: Display *m_display = nullptr; FakeInputInterface *m_fakeInputInterface = nullptr; FakeInputDevice *m_device = nullptr; ConnectionThread *m_connection = nullptr; QThread *m_thread = nullptr; EventQueue *m_queue = nullptr; FakeInput *m_fakeInput = nullptr; }; static const QString s_socketName = QStringLiteral("kwayland-test-fake-input-0"); void FakeInputTest::init() { delete m_display; m_display = new Display(this); m_display->setSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); m_display->createShm(); m_fakeInputInterface = m_display->createFakeInput(); m_fakeInputInterface->create(); QSignalSpy deviceCreatedSpy(m_fakeInputInterface, &FakeInputInterface::deviceCreated); QVERIFY(deviceCreatedSpy.isValid()); // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, &ConnectionThread::connected); QVERIFY(connectedSpy.isValid()); m_connection->setSocketName(s_socketName); m_thread = new QThread(this); m_connection->moveToThread(m_thread); m_thread->start(); m_connection->initConnection(); QVERIFY(connectedSpy.wait()); m_queue = new EventQueue(this); m_queue->setup(m_connection); Registry registry; QSignalSpy interfacesAnnouncedSpy(®istry, &Registry::interfacesAnnounced); QVERIFY(interfacesAnnouncedSpy.isValid()); registry.setEventQueue(m_queue); registry.create(m_connection); QVERIFY(registry.isValid()); registry.setup(); QVERIFY(interfacesAnnouncedSpy.wait()); m_fakeInput = registry.createFakeInput(registry.interface(Registry::Interface::FakeInput).name, registry.interface(Registry::Interface::FakeInput).version, this); QVERIFY(m_fakeInput->isValid()); QVERIFY(deviceCreatedSpy.wait()); m_device = deviceCreatedSpy.first().first().value(); QVERIFY(m_device); } void FakeInputTest::cleanup() { #define CLEANUP(variable) \ if (variable) { \ delete variable; \ variable = nullptr; \ } CLEANUP(m_fakeInput) CLEANUP(m_queue) if (m_connection) { m_connection->deleteLater(); m_connection = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } CLEANUP(m_device) CLEANUP(m_fakeInputInterface) CLEANUP(m_display) #undef CLEANUP } void FakeInputTest::testAuthenticate() { // this test verifies that an authenticate request is passed to the Server QVERIFY(!m_device->isAuthenticated()); QSignalSpy authenticationRequestedSpy(m_device, &FakeInputDevice::authenticationRequested); QVERIFY(authenticationRequestedSpy.isValid()); m_fakeInput->authenticate(QStringLiteral("test-case"), QStringLiteral("to test")); QVERIFY(authenticationRequestedSpy.wait()); QCOMPARE(authenticationRequestedSpy.count(), 1); QCOMPARE(authenticationRequestedSpy.first().at(0).toString(), QStringLiteral("test-case")); QCOMPARE(authenticationRequestedSpy.first().at(1).toString(), QStringLiteral("to test")); m_device->setAuthentication(true); QVERIFY(m_device->isAuthenticated()); } void FakeInputTest::testMotion() { // this test verifies that motion is properly passed to the server QVERIFY(!m_device->isAuthenticated()); QSignalSpy motionSpy(m_device, &FakeInputDevice::pointerMotionRequested); QVERIFY(motionSpy.isValid()); // without an authentication we shouldn't get the signals m_fakeInput->requestPointerMove(QSizeF(1, 2)); QVERIFY(!motionSpy.wait(100)); // now let's authenticate the interface m_device->setAuthentication(true); m_fakeInput->requestPointerMove(QSizeF(1, 2)); QVERIFY(motionSpy.wait()); QCOMPARE(motionSpy.count(), 1); QCOMPARE(motionSpy.last().first().toSizeF(), QSizeF(1, 2)); // just for the fun: once more m_fakeInput->requestPointerMove(QSizeF(0, 0)); QVERIFY(motionSpy.wait()); QCOMPARE(motionSpy.count(), 2); QCOMPARE(motionSpy.last().first().toSizeF(), QSizeF(0, 0)); } void FakeInputTest::testPointerButtonQt_data() { QTest::addColumn("qtButton"); QTest::addColumn("linuxButton"); QTest::newRow("left") << Qt::LeftButton << quint32(BTN_LEFT); QTest::newRow("right") << Qt::RightButton << quint32(BTN_RIGHT); QTest::newRow("middle") << Qt::MiddleButton << quint32(BTN_MIDDLE); } void FakeInputTest::testPointerButtonQt() { // this test verifies that pointer button events are properly passed to the server with Qt button codes QVERIFY(!m_device->isAuthenticated()); QSignalSpy pressedSpy(m_device, &FakeInputDevice::pointerButtonPressRequested); QVERIFY(pressedSpy.isValid()); QSignalSpy releasedSpy(m_device, &FakeInputDevice::pointerButtonReleaseRequested); QVERIFY(releasedSpy.isValid()); // without an authentication we shouldn't get the signals QFETCH(Qt::MouseButton, qtButton); m_fakeInput->requestPointerButtonClick(qtButton); QVERIFY(!pressedSpy.wait(100)); QVERIFY(pressedSpy.isEmpty()); QVERIFY(releasedSpy.isEmpty()); // now authenticate m_device->setAuthentication(true); // now our click should work m_fakeInput->requestPointerButtonClick(qtButton); QVERIFY(releasedSpy.wait()); QCOMPARE(pressedSpy.count(), 1); QCOMPARE(releasedSpy.count(), 1); QTEST(pressedSpy.last().first().value(), "linuxButton"); QTEST(releasedSpy.last().first().value(), "linuxButton"); // and a press/release "manually" m_fakeInput->requestPointerButtonPress(qtButton); QVERIFY(pressedSpy.wait()); QCOMPARE(pressedSpy.count(), 2); QCOMPARE(releasedSpy.count(), 1); QTEST(pressedSpy.last().first().value(), "linuxButton"); // and release m_fakeInput->requestPointerButtonRelease(qtButton); QVERIFY(releasedSpy.wait()); QCOMPARE(pressedSpy.count(), 2); QCOMPARE(releasedSpy.count(), 2); QTEST(releasedSpy.last().first().value(), "linuxButton"); } void FakeInputTest::testPointerButtonLinux_data() { QTest::addColumn("linuxButton"); QTest::newRow("left") << quint32(BTN_LEFT); QTest::newRow("right") << quint32(BTN_RIGHT); QTest::newRow("middle") << quint32(BTN_MIDDLE); QTest::newRow("side") << quint32(BTN_SIDE); QTest::newRow("extra") << quint32(BTN_EXTRA); QTest::newRow("forward") << quint32(BTN_FORWARD); QTest::newRow("back") << quint32(BTN_BACK); QTest::newRow("task") << quint32(BTN_TASK); } void FakeInputTest::testPointerButtonLinux() { // this test verifies that pointer button events are properly passed to the server with Qt button codes QVERIFY(!m_device->isAuthenticated()); QSignalSpy pressedSpy(m_device, &FakeInputDevice::pointerButtonPressRequested); QVERIFY(pressedSpy.isValid()); QSignalSpy releasedSpy(m_device, &FakeInputDevice::pointerButtonReleaseRequested); QVERIFY(releasedSpy.isValid()); // without an authentication we shouldn't get the signals QFETCH(quint32, linuxButton); m_fakeInput->requestPointerButtonClick(linuxButton); QVERIFY(!pressedSpy.wait(100)); QVERIFY(pressedSpy.isEmpty()); QVERIFY(releasedSpy.isEmpty()); // now authenticate m_device->setAuthentication(true); // now our click should work m_fakeInput->requestPointerButtonClick(linuxButton); QVERIFY(releasedSpy.wait()); QCOMPARE(pressedSpy.count(), 1); QCOMPARE(releasedSpy.count(), 1); QTEST(pressedSpy.last().first().value(), "linuxButton"); QTEST(releasedSpy.last().first().value(), "linuxButton"); // and a press/release "manually" m_fakeInput->requestPointerButtonPress(linuxButton); QVERIFY(pressedSpy.wait()); QCOMPARE(pressedSpy.count(), 2); QCOMPARE(releasedSpy.count(), 1); QTEST(pressedSpy.last().first().value(), "linuxButton"); // and release m_fakeInput->requestPointerButtonRelease(linuxButton); QVERIFY(releasedSpy.wait()); QCOMPARE(pressedSpy.count(), 2); QCOMPARE(releasedSpy.count(), 2); QTEST(releasedSpy.last().first().value(), "linuxButton"); } void FakeInputTest::testAxis_data() { QTest::addColumn("orientation"); QTest::addColumn("delta"); QTest::newRow("horizontal/1") << Qt::Horizontal << 1.0; QTest::newRow("horizontal/-2") << Qt::Horizontal << -2.0; QTest::newRow("vertical/10") << Qt::Vertical << 10.0; QTest::newRow("vertical/-20") << Qt::Vertical << -22.0; } void FakeInputTest::testAxis() { // this test verifies that pointer axis events are properly passed to the server QVERIFY(!m_device->isAuthenticated()); QSignalSpy axisSpy(m_device, &FakeInputDevice::pointerAxisRequested); QVERIFY(axisSpy.isValid()); QFETCH(Qt::Orientation, orientation); QFETCH(qreal, delta); // without an authentication we shouldn't get the signals m_fakeInput->requestPointerAxis(orientation, delta); QVERIFY(!axisSpy.wait(100)); // now authenticate m_device->setAuthentication(true); // now we can properly test m_fakeInput->requestPointerAxis(orientation, delta); QVERIFY(axisSpy.wait()); QCOMPARE(axisSpy.count(), 1); QCOMPARE(axisSpy.first().at(0).value(), orientation); QCOMPARE(axisSpy.first().at(1).value(), delta); } +void FakeInputTest::testTouch() +{ + QVERIFY(!m_device->isAuthenticated()); + QSignalSpy touchDownSpy(m_device, &FakeInputDevice::touchDownRequested); + QVERIFY(touchDownSpy.isValid()); + QSignalSpy touchMotionSpy(m_device, &FakeInputDevice::touchMotionRequested); + QVERIFY(touchMotionSpy.isValid()); + QSignalSpy touchUpSpy(m_device, &FakeInputDevice::touchUpRequested); + QVERIFY(touchUpSpy.isValid()); + QSignalSpy touchFrameSpy(m_device, &FakeInputDevice::touchFrameRequested); + QVERIFY(touchFrameSpy.isValid()); + QSignalSpy touchCancelSpy(m_device, &FakeInputDevice::touchCancelRequested); + QVERIFY(touchCancelSpy.isValid()); + + // without an authentication we shouldn't get the signals + m_fakeInput->requestTouchDown(0, QPointF(1, 2)); + QVERIFY(!touchDownSpy.wait(100)); + + m_fakeInput->requestTouchMotion(0, QPointF(3, 4)); + QVERIFY(!touchMotionSpy.wait(100)); + + m_fakeInput->requestTouchUp(0); + QVERIFY(!touchUpSpy.wait(100)); + + m_fakeInput->requestTouchDown(1, QPointF(5, 6)); + QVERIFY(!touchDownSpy.wait(100)); + + m_fakeInput->requestTouchFrame(); + QVERIFY(!touchFrameSpy.wait(100)); + + m_fakeInput->requestTouchCancel(); + QVERIFY(!touchCancelSpy.wait(100)); + + // now let's authenticate the interface + m_device->setAuthentication(true); + m_fakeInput->requestTouchDown(0, QPointF(1, 2)); + QVERIFY(touchDownSpy.wait()); + QCOMPARE(touchDownSpy.count(), 1); + QCOMPARE(touchDownSpy.last().at(0).value(), quint32(0)); + QCOMPARE(touchDownSpy.last().at(1).toPointF(), QPointF(1, 2)); + + // Same id should not trigger another touchDown. + m_fakeInput->requestTouchDown(0, QPointF(5,6)); + QVERIFY(!touchDownSpy.wait(100)); + + m_fakeInput->requestTouchMotion(0, QPointF(3, 4)); + QVERIFY(touchMotionSpy.wait()); + QCOMPARE(touchMotionSpy.count(), 1); + QCOMPARE(touchMotionSpy.last().at(0).value(), quint32(0)); + QCOMPARE(touchMotionSpy.last().at(1).toPointF(), QPointF(3, 4)); + + // touchMotion with bogus id should not trigger signal. + m_fakeInput->requestTouchMotion(1, QPointF(3, 4)); + QVERIFY(!touchMotionSpy.wait(100)); + + m_fakeInput->requestTouchUp(0); + QVERIFY(touchUpSpy.wait()); + QCOMPARE(touchUpSpy.count(), 1); + QCOMPARE(touchUpSpy.last().at(0).value(), quint32(0)); + + // touchUp with bogus id should not trigger signal. + m_fakeInput->requestTouchUp(1); + QVERIFY(!touchUpSpy.wait(100)); + + m_fakeInput->requestTouchDown(1, QPointF(5, 6)); + QVERIFY(touchDownSpy.wait()); + QCOMPARE(touchDownSpy.count(), 2); + QCOMPARE(touchDownSpy.last().at(0).value(), quint32(1)); + QCOMPARE(touchDownSpy.last().at(1).toPointF(), QPointF(5, 6)); + + m_fakeInput->requestTouchFrame(); + QVERIFY(touchFrameSpy.wait()); + QCOMPARE(touchFrameSpy.count(), 1); + + m_fakeInput->requestTouchCancel(); + QVERIFY(touchCancelSpy.wait()); + QCOMPARE(touchCancelSpy.count(), 1); +} + + QTEST_GUILESS_MAIN(FakeInputTest) #include "test_fake_input.moc" diff --git a/src/client/fakeinput.cpp b/src/client/fakeinput.cpp index d9d0fec..ef21e52 100644 --- a/src/client/fakeinput.cpp +++ b/src/client/fakeinput.cpp @@ -1,185 +1,215 @@ /******************************************************************** Copyright 2015 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #include "fakeinput.h" #include "event_queue.h" #include "seat.h" #include "wayland_pointer_p.h" - +#include #include #include #include namespace KWayland { namespace Client { class FakeInput::Private { public: WaylandPointer manager; EventQueue *queue = nullptr; void sendPointerButtonState(Qt::MouseButton button, quint32 state); }; FakeInput::FakeInput(QObject *parent) : QObject(parent) , d(new Private) { } FakeInput::~FakeInput() { release(); } void FakeInput::release() { d->manager.release(); } void FakeInput::destroy() { d->manager.destroy(); } bool FakeInput::isValid() const { return d->manager.isValid(); } void FakeInput::setup(org_kde_kwin_fake_input *manager) { Q_ASSERT(manager); Q_ASSERT(!d->manager.isValid()); d->manager.setup(manager); } EventQueue *FakeInput::eventQueue() { return d->queue; } void FakeInput::setEventQueue(EventQueue *queue) { d->queue = queue; } void FakeInput::authenticate(const QString &applicationName, const QString &reason) { Q_ASSERT(d->manager.isValid()); org_kde_kwin_fake_input_authenticate(d->manager, applicationName.toUtf8().constData(), reason.toUtf8().constData()); } void FakeInput::requestPointerMove(const QSizeF &delta) { Q_ASSERT(d->manager.isValid()); org_kde_kwin_fake_input_pointer_motion(d->manager, wl_fixed_from_double(delta.width()), wl_fixed_from_double(delta.height())); } void FakeInput::Private::sendPointerButtonState(Qt::MouseButton button, quint32 state) { Q_ASSERT(manager.isValid()); uint32_t b = 0; switch (button) { case Qt::LeftButton: b = BTN_LEFT; break; case Qt::RightButton: b = BTN_RIGHT; break; case Qt::MiddleButton: b = BTN_MIDDLE; break; default: // TODO: more buttons, check implementation in QtWayland // unsupported button return; } org_kde_kwin_fake_input_button(manager, b, state); } void FakeInput::requestPointerButtonPress(Qt::MouseButton button) { d->sendPointerButtonState(button, WL_POINTER_BUTTON_STATE_PRESSED); } void FakeInput::requestPointerButtonPress(quint32 linuxButton) { Q_ASSERT(d->manager.isValid()); org_kde_kwin_fake_input_button(d->manager, linuxButton, WL_POINTER_BUTTON_STATE_PRESSED); } void FakeInput::requestPointerButtonRelease(Qt::MouseButton button) { d->sendPointerButtonState(button, WL_POINTER_BUTTON_STATE_RELEASED); } void FakeInput::requestPointerButtonRelease(quint32 linuxButton) { Q_ASSERT(d->manager.isValid()); org_kde_kwin_fake_input_button(d->manager, linuxButton, WL_POINTER_BUTTON_STATE_RELEASED); } void FakeInput::requestPointerButtonClick(Qt::MouseButton button) { requestPointerButtonPress(button); requestPointerButtonRelease(button); } void FakeInput::requestPointerButtonClick(quint32 linuxButton) { requestPointerButtonPress(linuxButton); requestPointerButtonRelease(linuxButton); } void FakeInput::requestPointerAxis(Qt::Orientation axis, qreal delta) { Q_ASSERT(d->manager.isValid()); uint32_t a; switch (axis) { case Qt::Horizontal: a = WL_POINTER_AXIS_HORIZONTAL_SCROLL; break; case Qt::Vertical: a = WL_POINTER_AXIS_VERTICAL_SCROLL; break; default: Q_UNREACHABLE(); break; } org_kde_kwin_fake_input_axis(d->manager, a, wl_fixed_from_double(delta)); } +void FakeInput::requestTouchDown(quint32 id, const QPointF &pos) +{ + Q_ASSERT(d->manager.isValid()); + org_kde_kwin_fake_input_touch_down(d->manager, id, wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y())); +} + +void FakeInput::requestTouchMotion(quint32 id, const QPointF &pos) +{ + Q_ASSERT(d->manager.isValid()); + org_kde_kwin_fake_input_touch_motion(d->manager, id, wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y())); +} + +void FakeInput::requestTouchUp(quint32 id) +{ + Q_ASSERT(d->manager.isValid()); + org_kde_kwin_fake_input_touch_up(d->manager, id); +} + +void FakeInput::requestTouchCancel() +{ + Q_ASSERT(d->manager.isValid()); + org_kde_kwin_fake_input_touch_cancel(d->manager); +} + +void FakeInput::requestTouchFrame() +{ + Q_ASSERT(d->manager.isValid()); + org_kde_kwin_fake_input_touch_frame(d->manager); +} + FakeInput::operator org_kde_kwin_fake_input*() const { return d->manager; } FakeInput::operator org_kde_kwin_fake_input*() { return d->manager; } } } diff --git a/src/client/fakeinput.h b/src/client/fakeinput.h index e4e1ca6..9f6349a 100644 --- a/src/client/fakeinput.h +++ b/src/client/fakeinput.h @@ -1,149 +1,169 @@ /******************************************************************** Copyright 2015 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #ifndef KWAYLAND_FAKEINPUT_H #define KWAYLAND_FAKEINPUT_H #include #include struct org_kde_kwin_fake_input; namespace KWayland { namespace Client { class EventQueue; class FakeInputTimeout; class Seat; /** * @short Wrapper for the org_kde_kwin_fake_input interface. * * This class provides a convenient wrapper for the org_kde_kwin_fake_input interface. * * To use this class one needs to interact with the Registry. There are two * possible ways to create the FakeInput interface: * @code * FakeInput *m = registry->createFakeInput(name, version); * @endcode * * This creates the FakeInput and sets it up directly. As an alternative this * can also be done in a more low level way: * @code * FakeInput *m = new FakeInput; * m->setup(registry->bindFakeInput(name, version)); * @endcode * * The FakeInput can be used as a drop-in replacement for any org_kde_kwin_fake_input * pointer as it provides matching cast operators. * * @see Registry **/ class KWAYLANDCLIENT_EXPORT FakeInput : public QObject { Q_OBJECT public: /** * Creates a new FakeInput. * Note: after constructing the FakeInput it is not yet valid and one needs * to call setup. In order to get a ready to use FakeInput prefer using * Registry::createFakeInput. **/ explicit FakeInput(QObject *parent = nullptr); virtual ~FakeInput(); /** * @returns @c true if managing a org_kde_kwin_fake_input. **/ bool isValid() const; /** * Setup this FakeInput to manage the @p manager. * When using Registry::createFakeInput there is no need to call this * method. **/ void setup(org_kde_kwin_fake_input *manager); /** * Releases the org_kde_kwin_fake_input interface. * After the interface has been released the FakeInput instance is no * longer valid and can be setup with another org_kde_kwin_fake_input interface. **/ void release(); /** * Destroys the data held by this FakeInput. * This method is supposed to be used when the connection to the Wayland * server goes away. If the connection is not valid anymore, it's not * possible to call release anymore as that calls into the Wayland * connection and the call would fail. This method cleans up the data, so * that the instance can be deleted or set up to a new org_kde_kwin_fake_input interface * once there is a new connection available. * * It is suggested to connect this method to ConnectionThread::connectionDied: * @code * connect(connection, &ConnectionThread::connectionDied, manager, &FakeInput::destroy); * @endcode * * @see release **/ void destroy(); /** * Sets the @p queue to use for bound proxies. **/ void setEventQueue(EventQueue *queue); /** * @returns The event queue to use for bound proxies. **/ EventQueue *eventQueue(); void authenticate(const QString &applicationName, const QString &reason); void requestPointerMove(const QSizeF &delta); void requestPointerButtonPress(Qt::MouseButton button); void requestPointerButtonPress(quint32 linuxButton); void requestPointerButtonRelease(Qt::MouseButton button); void requestPointerButtonRelease(quint32 linuxButton); void requestPointerButtonClick(Qt::MouseButton button); void requestPointerButtonClick(quint32 linuxButton); void requestPointerAxis(Qt::Orientation axis, qreal delta); + /** + * @since 5.23 + **/ + void requestTouchDown(quint32 id, const QPointF &pos); + /** + * @since 5.23 + **/ + void requestTouchMotion(quint32 id, const QPointF &pos); + /** + * @since 5.23 + **/ + void requestTouchUp(quint32 id); + /** + * @since 5.23 + **/ + void requestTouchCancel(); + /** + * @since 5.23 + **/ + void requestTouchFrame(); operator org_kde_kwin_fake_input*(); operator org_kde_kwin_fake_input*() const; Q_SIGNALS: /** * The corresponding global for this interface on the Registry got removed. * * This signal gets only emitted if the Compositor got created by * Registry::createFakeInput * * @since 5.5 **/ void removed(); private: class Private; QScopedPointer d; }; } } #endif diff --git a/src/client/protocols/fake-input.xml b/src/client/protocols/fake-input.xml index cc934c3..849187e 100644 --- a/src/client/protocols/fake-input.xml +++ b/src/client/protocols/fake-input.xml @@ -1,53 +1,87 @@ . ]]> - + 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. But also to support use case like kdeconnect's mouse pad interface. A compositor should not trust the input received from this interface. Clients should not expect that the compositor honors the requests from this interface. A client should use this request to tell the compositor why it wants to use this interface. The compositor might use the information to decide whether it wants to grant the request. The data might also be passed to the user to decide whether the application should get granted access to this very privileged interface. + + + A client should use this request to send touch down event at specific + co-ordinates. + + + + + + + + A client should use this request to send touch motion to specific position. + + + + + + + + A client should use this request to send touch up event. + + + + + + A client should use this request to cancel the current + touch event. + + + + + A client should use this request to send touch frame event. + + diff --git a/src/client/registry.cpp b/src/client/registry.cpp index ad08037..3c78221 100644 --- a/src/client/registry.cpp +++ b/src/client/registry.cpp @@ -1,659 +1,659 @@ /******************************************************************** Copyright 2014 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #include "registry.h" #include "compositor.h" #include "connection_thread.h" #include "datadevicemanager.h" #include "dpms.h" #include "event_queue.h" #include "fakeinput.h" #include "fullscreen_shell.h" #include "idle.h" #include "logging_p.h" #include "outputconfiguration.h" #include "outputmanagement.h" #include "outputdevice.h" #include "output.h" #include "plasmashell.h" #include "plasmawindowmanagement.h" #include "seat.h" #include "shadow.h" #include "blur.h" #include "contrast.h" #include "server_decoration.h" #include "slide.h" #include "shell.h" #include "shm_pool.h" #include "subcompositor.h" #include "textinput_p.h" #include "wayland_pointer_p.h" // Qt #include // wayland #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /***** * How to add another interface: * * define a new enum value in Registry::Interface * * define the bind method * * define the create method * * define the Announced signal * * define the Removed signal * * add a block to s_interfaces * * add the BIND macro for the new bind * * add the CREATE macro for the new create * * extend registry unit test to verify that it works ****/ namespace KWayland { namespace Client { namespace { struct SuppertedInterfaceData { quint32 maxVersion; QByteArray name; const wl_interface *interface; void (Registry::*announcedSignal)(quint32, quint32); void (Registry::*removedSignal)(quint32); }; static const QMap s_interfaces = { {Registry::Interface::Compositor, { 3, QByteArrayLiteral("wl_compositor"), &wl_compositor_interface, &Registry::compositorAnnounced, &Registry::compositorRemoved }}, {Registry::Interface::DataDeviceManager, { 2, QByteArrayLiteral("wl_data_device_manager"), &wl_data_device_manager_interface, &Registry::dataDeviceManagerAnnounced, &Registry::dataDeviceManagerRemoved }}, {Registry::Interface::Output, { 2, QByteArrayLiteral("wl_output"), &wl_output_interface, &Registry::outputAnnounced, &Registry::outputRemoved }}, {Registry::Interface::Shm, { 1, QByteArrayLiteral("wl_shm"), &wl_shm_interface, &Registry::shmAnnounced, &Registry::shmRemoved }}, {Registry::Interface::Seat, { 4, QByteArrayLiteral("wl_seat"), &wl_seat_interface, &Registry::seatAnnounced, &Registry::seatRemoved }}, {Registry::Interface::Shell, { 1, QByteArrayLiteral("wl_shell"), &wl_shell_interface, &Registry::shellAnnounced, &Registry::shellRemoved }}, {Registry::Interface::SubCompositor, { 1, QByteArrayLiteral("wl_subcompositor"), &wl_subcompositor_interface, &Registry::subCompositorAnnounced, &Registry::subCompositorRemoved }}, {Registry::Interface::PlasmaShell, { 2, QByteArrayLiteral("org_kde_plasma_shell"), &org_kde_plasma_shell_interface, &Registry::plasmaShellAnnounced, &Registry::plasmaShellRemoved }}, {Registry::Interface::PlasmaWindowManagement, { 4, QByteArrayLiteral("org_kde_plasma_window_management"), &org_kde_plasma_window_management_interface, &Registry::plasmaWindowManagementAnnounced, &Registry::plasmaWindowManagementRemoved }}, {Registry::Interface::Idle, { 1, QByteArrayLiteral("org_kde_kwin_idle"), &org_kde_kwin_idle_interface, &Registry::idleAnnounced, &Registry::idleRemoved }}, {Registry::Interface::FakeInput, { - 1, + 2, QByteArrayLiteral("org_kde_kwin_fake_input"), &org_kde_kwin_fake_input_interface, &Registry::fakeInputAnnounced, &Registry::fakeInputRemoved }}, {Registry::Interface::OutputManagement, { 1, QByteArrayLiteral("org_kde_kwin_outputmanagement"), &org_kde_kwin_outputmanagement_interface, &Registry::outputManagementAnnounced, &Registry::outputManagementRemoved }}, {Registry::Interface::OutputDevice, { 1, QByteArrayLiteral("org_kde_kwin_outputdevice"), &org_kde_kwin_outputdevice_interface, &Registry::outputDeviceAnnounced, &Registry::outputDeviceRemoved }}, {Registry::Interface::Shadow, { 1, QByteArrayLiteral("org_kde_kwin_shadow_manager"), &org_kde_kwin_shadow_manager_interface, &Registry::shadowAnnounced, &Registry::shadowRemoved }}, {Registry::Interface::Blur, { 1, QByteArrayLiteral("org_kde_kwin_blur_manager"), &org_kde_kwin_blur_manager_interface, &Registry::blurAnnounced, &Registry::blurRemoved }}, {Registry::Interface::Contrast, { 1, QByteArrayLiteral("org_kde_kwin_contrast_manager"), &org_kde_kwin_contrast_manager_interface, &Registry::contrastAnnounced, &Registry::contrastRemoved }}, {Registry::Interface::Slide, { 1, QByteArrayLiteral("org_kde_kwin_slide_manager"), &org_kde_kwin_slide_manager_interface, &Registry::slideAnnounced, &Registry::slideRemoved }}, {Registry::Interface::FullscreenShell, { 1, QByteArrayLiteral("_wl_fullscreen_shell"), &_wl_fullscreen_shell_interface, &Registry::fullscreenShellAnnounced, &Registry::fullscreenShellRemoved }}, {Registry::Interface::Dpms, { 1, QByteArrayLiteral("org_kde_kwin_dpms_manager"), &org_kde_kwin_dpms_manager_interface, &Registry::dpmsAnnounced, &Registry::dpmsRemoved }}, {Registry::Interface::ServerSideDecorationManager, { 1, QByteArrayLiteral("org_kde_kwin_server_decoration_manager"), &org_kde_kwin_server_decoration_manager_interface, &Registry::serverSideDecorationManagerAnnounced, &Registry::serverSideDecorationManagerRemoved }}, {Registry::Interface::TextInputManagerUnstableV0, { 1, QByteArrayLiteral("wl_text_input_manager"), &wl_text_input_manager_interface, &Registry::textInputManagerUnstableV0Announced, &Registry::textInputManagerUnstableV0Removed }}, {Registry::Interface::TextInputManagerUnstableV2, { 1, QByteArrayLiteral("zwp_text_input_manager_v2"), &zwp_text_input_manager_v2_interface, &Registry::textInputManagerUnstableV2Announced, &Registry::textInputManagerUnstableV2Removed }} }; static quint32 maxVersion(const Registry::Interface &interface) { auto it = s_interfaces.find(interface); if (it != s_interfaces.end()) { return it.value().maxVersion; } return 0; } } class Registry::Private { public: Private(Registry *q); void setup(); bool hasInterface(Interface interface) const; AnnouncedInterface interface(Interface interface) const; QVector interfaces(Interface interface) const; Interface interfaceForName(quint32 name) const; template T *bind(Interface interface, uint32_t name, uint32_t version) const; template T *create(quint32 name, quint32 version, QObject *parent, WL *(Registry::*bindMethod)(uint32_t, uint32_t) const); WaylandPointer registry; static const struct wl_callback_listener s_callbackListener; WaylandPointer callback; EventQueue *queue = nullptr; private: void handleAnnounce(uint32_t name, const char *interface, uint32_t version); void handleRemove(uint32_t name); void handleGlobalSync(); static void globalAnnounce(void *data, struct wl_registry *registry, uint32_t name, const char *interface, uint32_t version); static void globalRemove(void *data, struct wl_registry *registry, uint32_t name); static void globalSync(void *data, struct wl_callback *callback, uint32_t serial); Registry *q; struct InterfaceData { Interface interface; uint32_t name; uint32_t version; }; QList m_interfaces; static const struct wl_registry_listener s_registryListener; }; Registry::Private::Private(Registry *q) : q(q) { } void Registry::Private::setup() { wl_registry_add_listener(registry, &s_registryListener, this); wl_callback_add_listener(callback, &s_callbackListener, this); } Registry::Registry(QObject *parent) : QObject(parent) , d(new Private(this)) { } Registry::~Registry() { release(); } void Registry::release() { d->registry.release(); d->callback.release(); } void Registry::destroy() { d->registry.destroy(); d->callback.destroy(); } void Registry::create(wl_display *display) { Q_ASSERT(display); Q_ASSERT(!isValid()); d->registry.setup(wl_display_get_registry(display)); d->callback.setup(wl_display_sync(display)); if (d->queue) { d->queue->addProxy(d->registry); d->queue->addProxy(d->callback); } } void Registry::create(ConnectionThread *connection) { create(connection->display()); } void Registry::setup() { Q_ASSERT(isValid()); d->setup(); } void Registry::setEventQueue(EventQueue *queue) { d->queue = queue; if (!queue) { return; } if (d->registry) { d->queue->addProxy(d->registry); } if (d->callback) { d->queue->addProxy(d->callback); } } EventQueue *Registry::eventQueue() { return d->queue; } #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct wl_registry_listener Registry::Private::s_registryListener = { globalAnnounce, globalRemove }; const struct wl_callback_listener Registry::Private::s_callbackListener = { globalSync }; #endif void Registry::Private::globalAnnounce(void *data, wl_registry *registry, uint32_t name, const char *interface, uint32_t version) { auto r = reinterpret_cast(data); Q_ASSERT(registry == r->registry); r->handleAnnounce(name, interface, version); } void Registry::Private::globalRemove(void *data, wl_registry *registry, uint32_t name) { auto r = reinterpret_cast(data); Q_ASSERT(registry == r->registry); r->handleRemove(name); } void Registry::Private::globalSync(void* data, wl_callback* callback, uint32_t serial) { Q_UNUSED(serial) auto r = reinterpret_cast(data); Q_ASSERT(r->callback == callback); r->handleGlobalSync(); r->callback.destroy(); } void Registry::Private::handleGlobalSync() { emit q->interfacesAnnounced(); } namespace { static Registry::Interface nameToInterface(const char *interface) { for (auto it = s_interfaces.begin(); it != s_interfaces.end(); ++it) { if (qstrcmp(interface, it.value().name) == 0) { return it.key(); } } return Registry::Interface::Unknown; } } void Registry::Private::handleAnnounce(uint32_t name, const char *interface, uint32_t version) { Interface i = nameToInterface(interface); emit q->interfaceAnnounced(QByteArray(interface), name, version); if (i == Interface::Unknown) { qCDebug(KWAYLAND_CLIENT) << "Unknown interface announced: " << interface << "/" << name << "/" << version; return; } qCDebug(KWAYLAND_CLIENT) << "Wayland Interface: " << interface << "/" << name << "/" << version; m_interfaces.append({i, name, version}); auto it = s_interfaces.constFind(i); if (it != s_interfaces.end()) { emit (q->*it.value().announcedSignal)(name, version); } } void Registry::Private::handleRemove(uint32_t name) { auto it = std::find_if(m_interfaces.begin(), m_interfaces.end(), [name](const InterfaceData &data) { return data.name == name; } ); if (it != m_interfaces.end()) { InterfaceData data = *(it); m_interfaces.erase(it); auto sit = s_interfaces.find(data.interface); if (sit != s_interfaces.end()) { emit (q->*sit.value().removedSignal)(data.name); } } emit q->interfaceRemoved(name); } bool Registry::Private::hasInterface(Registry::Interface interface) const { auto it = std::find_if(m_interfaces.begin(), m_interfaces.end(), [interface](const InterfaceData &data) { return data.interface == interface; } ); return it != m_interfaces.end(); } QVector Registry::Private::interfaces(Interface interface) const { QVector retVal; for (auto it = m_interfaces.constBegin(); it != m_interfaces.constEnd(); ++it) { const auto &data = *it; if (data.interface == interface) { retVal << AnnouncedInterface{data.name, data.version}; } } return retVal; } Registry::AnnouncedInterface Registry::Private::interface(Interface interface) const { const auto all = interfaces(interface); if (!all.isEmpty()) { return all.last(); } return AnnouncedInterface{0, 0}; } Registry::Interface Registry::Private::interfaceForName(quint32 name) const { auto it = std::find_if(m_interfaces.constBegin(), m_interfaces.constEnd(), [name] (const InterfaceData &data) { return data.name == name; }); if (it == m_interfaces.constEnd()) { return Interface::Unknown; } return (*it).interface; } bool Registry::hasInterface(Registry::Interface interface) const { return d->hasInterface(interface); } QVector Registry::interfaces(Interface interface) const { return d->interfaces(interface); } Registry::AnnouncedInterface Registry::interface(Interface interface) const { return d->interface(interface); } #define BIND2(__NAME__, __INAME__, __WL__) \ __WL__ *Registry::bind##__NAME__(uint32_t name, uint32_t version) const \ { \ return d->bind<__WL__>(Interface::__INAME__, name, qMin(maxVersion(Interface::__INAME__), version)); \ } #define BIND(__NAME__, __WL__) BIND2(__NAME__, __NAME__, __WL__) BIND(Compositor, wl_compositor) BIND(Output, wl_output) BIND(Seat, wl_seat) BIND(Shell, wl_shell) BIND(Shm, wl_shm) BIND(SubCompositor, wl_subcompositor) BIND(FullscreenShell, _wl_fullscreen_shell) BIND(DataDeviceManager, wl_data_device_manager) BIND(PlasmaShell, org_kde_plasma_shell) BIND(PlasmaWindowManagement, org_kde_plasma_window_management) BIND(Idle, org_kde_kwin_idle) BIND(FakeInput, org_kde_kwin_fake_input) BIND(OutputManagement, org_kde_kwin_outputmanagement) BIND(OutputDevice, org_kde_kwin_outputdevice) BIND(ServerSideDecorationManager, org_kde_kwin_server_decoration_manager) BIND(TextInputManagerUnstableV0, wl_text_input_manager) BIND(TextInputManagerUnstableV2, zwp_text_input_manager_v2) BIND2(ShadowManager, Shadow, org_kde_kwin_shadow_manager) BIND2(BlurManager, Blur, org_kde_kwin_blur_manager) BIND2(ContrastManager, Contrast, org_kde_kwin_contrast_manager) BIND2(SlideManager, Slide, org_kde_kwin_slide_manager) BIND2(DpmsManager, Dpms, org_kde_kwin_dpms_manager) #undef BIND #undef BIND2 template T *Registry::Private::create(quint32 name, quint32 version, QObject *parent, WL *(Registry::*bindMethod)(uint32_t, uint32_t) const) { T *t = new T(parent); t->setEventQueue(queue); t->setup((q->*bindMethod)(name, version)); QObject::connect(q, &Registry::interfaceRemoved, t, [t, name] (quint32 removed) { if (name == removed) { emit t->removed(); } } ); return t; } #define CREATE2(__NAME__, __BINDNAME__) \ __NAME__ *Registry::create##__NAME__(quint32 name, quint32 version, QObject *parent) \ { \ return d->create<__NAME__>(name, version, parent, &Registry::bind##__BINDNAME__); \ } #define CREATE(__NAME__) CREATE2(__NAME__, __NAME__) CREATE(Compositor) CREATE(Seat) CREATE(Shell) CREATE(SubCompositor) CREATE(FullscreenShell) CREATE(Output) CREATE(DataDeviceManager) CREATE(PlasmaShell) CREATE(PlasmaWindowManagement) CREATE(Idle) CREATE(FakeInput) CREATE(OutputManagement) CREATE(OutputDevice) CREATE(ShadowManager) CREATE(BlurManager) CREATE(ContrastManager) CREATE(SlideManager) CREATE(DpmsManager) CREATE(ServerSideDecorationManager) CREATE2(ShmPool, Shm) #undef CREATE #undef CREATE2 TextInputManager *Registry::createTextInputManager(quint32 name, quint32 version, QObject *parent) { switch (d->interfaceForName(name)) { case Interface::TextInputManagerUnstableV0: return d->create(name, version, parent, &Registry::bindTextInputManagerUnstableV0); case Interface::TextInputManagerUnstableV2: return d->create(name, version, parent, &Registry::bindTextInputManagerUnstableV2); default: return nullptr; } } namespace { static const wl_interface *wlInterface(Registry::Interface interface) { auto it = s_interfaces.find(interface); if (it != s_interfaces.end()) { return it.value().interface; } return nullptr; } } template T *Registry::Private::bind(Registry::Interface interface, uint32_t name, uint32_t version) const { auto it = std::find_if(m_interfaces.begin(), m_interfaces.end(), [=](const InterfaceData &data) { return data.interface == interface && data.name == name && data.version >= version; }); if (it == m_interfaces.end()) { qCDebug(KWAYLAND_CLIENT) << "Don't have interface " << int(interface) << "with name " << name << "and minimum version" << version; return nullptr; } auto t = reinterpret_cast(wl_registry_bind(registry, name, wlInterface(interface), version)); if (queue) { queue->addProxy(t); } return t; } bool Registry::isValid() const { return d->registry.isValid(); } wl_registry *Registry::registry() { return d->registry; } Registry::operator wl_registry*() const { return d->registry; } Registry::operator wl_registry*() { return d->registry; } } } diff --git a/src/server/fakeinput_interface.cpp b/src/server/fakeinput_interface.cpp index dd1c7fc..7c7d064 100644 --- a/src/server/fakeinput_interface.cpp +++ b/src/server/fakeinput_interface.cpp @@ -1,219 +1,294 @@ /******************************************************************** Copyright 2015 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #include "fakeinput_interface.h" #include "display.h" #include "global_p.h" #include +#include #include #include namespace KWayland { namespace Server { class FakeInputInterface::Private : public Global::Private { public: Private(FakeInputInterface *q, Display *d); QList devices; private: void bind(wl_client *client, uint32_t version, uint32_t id) override; static void authenticateCallback(wl_client *client, wl_resource *resource, const char *application, const char *reason); static void pointerMotionCallback(wl_client *client, wl_resource *resource, wl_fixed_t delta_x, wl_fixed_t delta_y); static void buttonCallback(wl_client *client, wl_resource *resource, uint32_t button, uint32_t state); static void axisCallback(wl_client *client, wl_resource *resource, uint32_t axis, wl_fixed_t value); + static void touchDownCallback(wl_client *client, wl_resource *resource, quint32 id, wl_fixed_t x, wl_fixed_t y); + static void touchMotionCallback(wl_client *client, wl_resource *resource, quint32 id, wl_fixed_t x, wl_fixed_t y); + 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 unbind(wl_resource *resource); static Private *cast(wl_resource *r) { return reinterpret_cast(wl_resource_get_user_data(r)); } static FakeInputDevice *device(wl_resource *r); FakeInputInterface *q; static const struct org_kde_kwin_fake_input_interface s_interface; static const quint32 s_version; + static QList touchIds; }; class FakeInputDevice::Private { public: Private(wl_resource *resource, FakeInputInterface *interface, FakeInputDevice *q); wl_resource *resource; FakeInputInterface *interface; bool authenticated = false; private: FakeInputDevice *q; }; -const quint32 FakeInputInterface::Private::s_version = 1; +const quint32 FakeInputInterface::Private::s_version = 2; +QList FakeInputInterface::Private::touchIds = QList(); #ifndef DOXYGEN_SHOULD_SKIP_THIS const struct org_kde_kwin_fake_input_interface FakeInputInterface::Private::s_interface = { authenticateCallback, pointerMotionCallback, buttonCallback, - axisCallback + axisCallback, + touchDownCallback, + touchMotionCallback, + touchUpCallback, + touchCancelCallback, + touchFrameCallback }; #endif FakeInputInterface::Private::Private(FakeInputInterface *q, Display *d) : Global::Private(d, &org_kde_kwin_fake_input_interface, s_version) , q(q) { } void FakeInputInterface::Private::bind(wl_client *client, uint32_t version, uint32_t id) { auto c = display->getConnection(client); wl_resource *resource = c->createResource(&org_kde_kwin_fake_input_interface, qMin(version, s_version), id); if (!resource) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation(resource, &s_interface, this, unbind); FakeInputDevice *device = new FakeInputDevice(resource, q); devices << device; QObject::connect(device, &FakeInputDevice::destroyed, q, [device, this] { devices.removeAll(device); }); emit q->deviceCreated(device); } void FakeInputInterface::Private::unbind(wl_resource *resource) { if (FakeInputDevice *d = device(resource)) { d->deleteLater(); } } FakeInputDevice *FakeInputInterface::Private::device(wl_resource *r) { Private *p = cast(r); auto it = std::find_if(p->devices.constBegin(), p->devices.constEnd(), [r] (FakeInputDevice *device) { return device->resource() == r; } ); if (it != p->devices.constEnd()) { return *it; } return nullptr; } void FakeInputInterface::Private::authenticateCallback(wl_client *client, wl_resource *resource, const char *application, const char *reason) { Q_UNUSED(client) FakeInputDevice *d = device(resource); if (!d) { return; } emit d->authenticationRequested(QString::fromUtf8(application), QString::fromUtf8(reason)); } void FakeInputInterface::Private::pointerMotionCallback(wl_client *client, wl_resource *resource, wl_fixed_t delta_x, wl_fixed_t delta_y) { Q_UNUSED(client) FakeInputDevice *d = device(resource); if (!d || !d->isAuthenticated()) { return; } emit d->pointerMotionRequested(QSizeF(wl_fixed_to_double(delta_x), wl_fixed_to_double(delta_y))); } void FakeInputInterface::Private::axisCallback(wl_client *client, wl_resource *resource, uint32_t axis, wl_fixed_t value) { Q_UNUSED(client) FakeInputDevice *d = device(resource); if (!d || !d->isAuthenticated()) { return; } Qt::Orientation orientation; switch (axis) { case WL_POINTER_AXIS_HORIZONTAL_SCROLL: orientation = Qt::Horizontal; break; case WL_POINTER_AXIS_VERTICAL_SCROLL: orientation = Qt::Vertical; break; default: // invalid return; } emit d->pointerAxisRequested(orientation, wl_fixed_to_double(value)); } void FakeInputInterface::Private::buttonCallback(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_POINTER_BUTTON_STATE_PRESSED: emit d->pointerButtonPressRequested(button); break; case WL_POINTER_BUTTON_STATE_RELEASED: emit d->pointerButtonReleaseRequested(button); break; default: // nothing break; } } +void FakeInputInterface::Private::touchDownCallback(wl_client *client, wl_resource *resource, quint32 id, wl_fixed_t x, wl_fixed_t y) +{ + Q_UNUSED(client) + FakeInputDevice *d = device(resource); + if (!d || !d->isAuthenticated()) { + return; + } + if (touchIds.contains(id)) { + return; + } + touchIds << id; + emit d->touchDownRequested(id, QPointF(wl_fixed_to_double(x), wl_fixed_to_double(y))); +} + +void FakeInputInterface::Private::touchMotionCallback(wl_client *client, wl_resource *resource, quint32 id, wl_fixed_t x, wl_fixed_t y) +{ + Q_UNUSED(client) + FakeInputDevice *d = device(resource); + if (!d || !d->isAuthenticated()) { + return; + } + if (!touchIds.contains(id)) { + return; + } + emit d->touchMotionRequested(id, QPointF(wl_fixed_to_double(x), wl_fixed_to_double(y))); +} + +void FakeInputInterface::Private::touchUpCallback(wl_client *client, wl_resource *resource, quint32 id) +{ + Q_UNUSED(client) + FakeInputDevice *d = device(resource); + if (!d || !d->isAuthenticated()) { + return; + } + if (!touchIds.contains(id)) { + return; + } + touchIds.removeOne(id); + emit d->touchUpRequested(id); +} + +void FakeInputInterface::Private::touchCancelCallback(wl_client *client, wl_resource *resource) +{ + Q_UNUSED(client) + FakeInputDevice *d = device(resource); + if (!d || !d->isAuthenticated()) { + return; + } + touchIds.clear(); + emit d->touchCancelRequested(); +} + +void FakeInputInterface::Private::touchFrameCallback(wl_client *client, wl_resource *resource) +{ + Q_UNUSED(client) + FakeInputDevice *d = device(resource); + if (!d || !d->isAuthenticated()) { + return; + } + emit d->touchFrameRequested(); +} + FakeInputInterface::FakeInputInterface(Display *display, QObject *parent) : Global(new Private(this, display), parent) { } FakeInputInterface::~FakeInputInterface() = default; FakeInputDevice::Private::Private(wl_resource *resource, FakeInputInterface *interface, FakeInputDevice *q) : resource(resource) , interface(interface) , q(q) { } FakeInputDevice::FakeInputDevice(wl_resource *resource, FakeInputInterface *parent) : QObject(parent) , d(new Private(resource, parent, this)) { } FakeInputDevice::~FakeInputDevice() = default; void FakeInputDevice::setAuthentication(bool authenticated) { d->authenticated = authenticated; } wl_resource *FakeInputDevice::resource() { return d->resource; } bool FakeInputDevice::isAuthenticated() const { return d->authenticated; } } } diff --git a/src/server/fakeinput_interface.h b/src/server/fakeinput_interface.h index 2edde3f..89458fe 100644 --- a/src/server/fakeinput_interface.h +++ b/src/server/fakeinput_interface.h @@ -1,141 +1,171 @@ /******************************************************************** Copyright 2015 Martin Gräßlin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . *********************************************************************/ #ifndef KWAYLAND_SERVER_FAKEINPUT_INTERFACE_H #define KWAYLAND_SERVER_FAKEINPUT_INTERFACE_H #include #include "global.h" struct wl_resource; namespace KWayland { namespace Server { class Display; class FakeInputDevice; /** * @brief Represents the Global for org_kde_kwin_fake_input interface. * * The fake input interface allows clients to send fake input events to the * Wayland server. For the actual events it creates a FakeInputDevice. Whenever * the FakeInputInterface creates a device the signal deviceCreated gets emitted. * * Accepting fake input events is a security risk. The server should make a * dedicated decision about whether it wants to accept fake input events from a * device. Because of that by default no events are forwarded to the server. The * device needs to request authentication and the server must explicitly authenticate * the device. The recommendation is that the server only accepts input for in some * way trusted clients. * * @see FakeInputDevice * @since 5.4 **/ class KWAYLANDSERVER_EXPORT FakeInputInterface : public Global { Q_OBJECT public: virtual ~FakeInputInterface(); Q_SIGNALS: /** * Signal emitted whenever a client bound the fake input @p device. * @param device The created FakeInputDevice **/ void deviceCreated(KWayland::Server::FakeInputDevice *device); private: explicit FakeInputInterface(Display *display, QObject *parent = nullptr); friend class Display; class Private; }; /** * @brief Represents the Resource for a org_kde_kwin_fake_input interface. * * @see FakeInputInterface * @since 5.4 **/ class KWAYLANDSERVER_EXPORT FakeInputDevice : public QObject { Q_OBJECT public: virtual ~FakeInputDevice(); /** * @returns the native wl_resource. **/ wl_resource *resource(); /** * Authenticate this device to send events. If @p authenticated is @c true events are * accepted, for @c false events are no longer accepted. * * @param authenticated Whether the FakeInputDevice should be considered authenticated **/ void setAuthentication(bool authenticated); /** * @returns whether the FakeInputDevice is authenticated and allowed to send events, default is @c false. **/ bool isAuthenticated() const; Q_SIGNALS: /** * Request for authentication. * * The server might use the provided information to make a decision on whether the * FakeInputDevice should get authenticated. It is recommended to not trust the data * and to combine it with information from ClientConnection. * * @param application A textual description of the application * @param reason A textual description of the reason why the application wants to send fake input events **/ void authenticationRequested(const QString &application, const QString &reason); /** * Request a pointer motion by @p delta. **/ void pointerMotionRequested(const QSizeF &delta); /** * Requests a pointer button pressed for @p button. **/ void pointerButtonPressRequested(quint32 button); /** * Requests a pointer button release for @p button. **/ void pointerButtonReleaseRequested(quint32 button); /** * Requests a pointer axis for the given @p orientation by @p delta. **/ void pointerAxisRequested(Qt::Orientation orientation, qreal delta); + /** + * Requests a touch down at @p pos and identified by @p id. + * + * @since 5.23 + **/ + void touchDownRequested(quint32 id, const QPointF &pos); + /** + * Requests a touch motion by @p pos and identified by @p id. + * + * @since 5.23 + **/ + void touchMotionRequested(quint32 id, const QPointF &pos); + /** + * Requests a touch up identified by @p id. + * + * @since 5.23 + **/ + void touchUpRequested(quint32 id); + /** + * Requests a touch cancel event. + * + * @since 5.23 + **/ + void touchCancelRequested(); + /** + * Requests a touch frame event. + * + * @since 5.23 + **/ + void touchFrameRequested(); private: friend class FakeInputInterface; FakeInputDevice(wl_resource *resource, FakeInputInterface *parent); class Private; QScopedPointer d; }; } } Q_DECLARE_METATYPE(KWayland::Server::FakeInputDevice*) #endif