From 115ea7454b16c229bec4936b2aa30a02b113bca0 Mon Sep 17 00:00:00 2001 From: Aki Sakurai Date: Sun, 22 Oct 2023 22:15:55 +0800 Subject: [PATCH] Send tablet events to every bound wl_resource Some apps, notably Tablet KCM, will rebind the tablet interface and consume all events that should be sent to the GUI toolkit. This commit sends events to every consumer and also stores the latest cursor indexed by wl_client instead of wl_resource. BUG:473126 --- .../wayland/server/test_tablet_interface.cpp | 36 +++- src/wayland/tablet_v2.cpp | 170 ++++++++++++------ 2 files changed, 148 insertions(+), 58 deletions(-) diff --git a/autotests/wayland/server/test_tablet_interface.cpp b/autotests/wayland/server/test_tablet_interface.cpp index 57f7639e5a..d6f46e5a3c 100644 --- a/autotests/wayland/server/test_tablet_interface.cpp +++ b/autotests/wayland/server/test_tablet_interface.cpp @@ -148,7 +148,9 @@ private Q_SLOTS: void initTestCase(); void testAdd(); void testAddPad(); + void testInteractSimple_data(); void testInteractSimple(); + void testInteractSurfaceChange_data(); void testInteractSurfaceChange(); private: @@ -163,6 +165,8 @@ private: CompositorInterface *m_serverCompositor; TabletSeat *m_tabletSeatClient = nullptr; + TabletSeat *m_tabletSeatClient2 = nullptr; + TabletManagerV2Interface *m_tabletManager; QList m_surfacesClient; @@ -213,6 +217,8 @@ void TestTabletInterface::initTestCase() auto tabletClient = new QtWayland::zwp_tablet_manager_v2(registry->registry(), name, version); auto _seat = tabletClient->get_tablet_seat(*m_clientSeat); m_tabletSeatClient = new TabletSeat(_seat); + auto _seat2 = tabletClient->get_tablet_seat(*m_clientSeat); + m_tabletSeatClient2 = new TabletSeat(_seat2); } }); connect(registry, &KWayland::Client::Registry::seatAnnounced, this, [this, registry](quint32 name, quint32 version) { @@ -251,6 +257,7 @@ TestTabletInterface::~TestTabletInterface() m_thread = nullptr; } delete m_tabletSeatClient; + delete m_tabletSeatClient2; m_connection->deleteLater(); m_connection = nullptr; } @@ -311,9 +318,19 @@ void TestTabletInterface::testAddPad() } static uint s_serial = 0; + +void TestTabletInterface::testInteractSimple_data() +{ + QTest::addColumn("tabletSeatClient"); + QTest::newRow("first client") << m_tabletSeatClient; + QTest::newRow("second client") << m_tabletSeatClient2; +} + void TestTabletInterface::testInteractSimple() { - QSignalSpy frameSpy(m_tabletSeatClient->m_tools[0], &Tool::frame); + QFETCH(TabletSeat *, tabletSeatClient); + tabletSeatClient->m_tools[0]->surfaceApproximated.clear(); + QSignalSpy frameSpy(tabletSeatClient->m_tools[0], &Tool::frame); QVERIFY(!m_tool->isClientSupported()); m_tool->setCurrentSurface(m_surfaces[0]); @@ -329,13 +346,22 @@ void TestTabletInterface::testInteractSimple() QVERIFY(!m_tool->isClientSupported()); QVERIFY(frameSpy.wait(500)); - QCOMPARE(m_tabletSeatClient->m_tools[0]->surfaceApproximated.count(), 1); + QCOMPARE(tabletSeatClient->m_tools[0]->surfaceApproximated.count(), 1); +} + +void TestTabletInterface::testInteractSurfaceChange_data() +{ + QTest::addColumn("tabletSeatClient"); + QTest::newRow("first client") << m_tabletSeatClient; + QTest::newRow("second client") << m_tabletSeatClient2; } void TestTabletInterface::testInteractSurfaceChange() { - m_tabletSeatClient->m_tools[0]->surfaceApproximated.clear(); - QSignalSpy frameSpy(m_tabletSeatClient->m_tools[0], &Tool::frame); + QFETCH(TabletSeat *, tabletSeatClient); + tabletSeatClient->m_tools[0]->surfaceApproximated.clear(); + QSignalSpy frameSpy(tabletSeatClient->m_tools[0], &Tool::frame); + QVERIFY(!m_tool->isClientSupported()); m_tool->setCurrentSurface(m_surfaces[0]); QVERIFY(m_tool->isClientSupported() && m_tablet->isSurfaceSupported(m_surfaces[0])); @@ -354,7 +380,7 @@ void TestTabletInterface::testInteractSurfaceChange() QVERIFY(!m_tool->isClientSupported()); QVERIFY(frameSpy.wait(500)); - QCOMPARE(m_tabletSeatClient->m_tools[0]->surfaceApproximated.count(), 2); + QCOMPARE(tabletSeatClient->m_tools[0]->surfaceApproximated.count(), 2); } QTEST_GUILESS_MAIN(TestTabletInterface) diff --git a/src/wayland/tablet_v2.cpp b/src/wayland/tablet_v2.cpp index b817a63180..a63a0302d3 100644 --- a/src/wayland/tablet_v2.cpp +++ b/src/wayland/tablet_v2.cpp @@ -14,6 +14,7 @@ #include #include +#include namespace KWin { @@ -144,14 +145,14 @@ public: { } - wl_resource *targetResource() + std::ranges::subrange::const_iterator> targetResources() const { if (!m_surface) - return nullptr; + return {}; ClientConnection *client = m_surface->client(); - const Resource *r = resourceMap().value(*client); - return r ? r->handle : nullptr; + const auto [start, end] = resourceMap().equal_range(*client); + return std::ranges::subrange(start, end); } quint64 hardwareId() const @@ -165,7 +166,7 @@ public: void zwp_tablet_tool_v2_bind_resource(QtWaylandServer::zwp_tablet_tool_v2::Resource *resource) override { - TabletSurfaceCursorV2 *&c = m_cursors[resource->handle]; + TabletSurfaceCursorV2 *&c = m_cursors[resource->client()]; if (!c) c = new TabletSurfaceCursorV2; } @@ -186,16 +187,21 @@ public: } } - TabletSurfaceCursorV2 *c = m_cursors[resource->handle]; + TabletSurfaceCursorV2 *c = m_cursors[resource->client()]; c->d->update(serial, surface, {hotspot_x, hotspot_y}); - if (resource->handle == targetResource()) { + const auto resources = targetResources(); + if (std::any_of(resources.begin(), resources.end(), [resource](const Resource *res) { + return res->handle == resource->handle; + })) { Q_EMIT q->cursorChanged(c); } } void zwp_tablet_tool_v2_destroy_resource(Resource *resource) override { - delete m_cursors.take(resource->handle); + if (!resourceMap().contains(resource->client())) { + delete m_cursors.take(resource->client()); + } if (m_removed && resourceMap().isEmpty()) { delete q; } @@ -216,7 +222,7 @@ public: const uint32_t m_hardwareSerialHigh, m_hardwareSerialLow; const uint32_t m_hardwareIdHigh, m_hardwareIdLow; const QList m_capabilities; - QHash m_cursors; + QHash m_cursors; TabletToolV2Interface *const q; }; @@ -276,7 +282,11 @@ void TabletToolV2Interface::setCurrentSurface(SurfaceInterface *surface) d->m_lastTablet = lastTablet; } - Q_EMIT cursorChanged(d->m_cursors.value(d->targetResource())); + if (surface != nullptr) { + if (auto *const cursor = d->m_cursors.value(*surface->client())) { + Q_EMIT cursorChanged(cursor); + } + } } quint32 TabletToolV2Interface::proximitySerial() const @@ -286,31 +296,40 @@ quint32 TabletToolV2Interface::proximitySerial() const bool TabletToolV2Interface::isClientSupported() const { - return d->m_surface && d->targetResource(); + return d->m_surface && !d->targetResources().empty(); } void TabletToolV2Interface::sendButton(uint32_t button, bool pressed) { - d->send_button(d->targetResource(), - d->m_display->nextSerial(), - button, - pressed ? QtWaylandServer::zwp_tablet_tool_v2::button_state_pressed : QtWaylandServer::zwp_tablet_tool_v2::button_state_released); + const auto serial = d->m_display->nextSerial(); + for (auto *resource : d->targetResources()) { + d->send_button(resource->handle, + serial, + button, + pressed ? QtWaylandServer::zwp_tablet_tool_v2::button_state_pressed : QtWaylandServer::zwp_tablet_tool_v2::button_state_released); + } } void TabletToolV2Interface::sendMotion(const QPointF &pos) { const QPointF surfacePos = d->m_surface->toSurfaceLocal(pos); - d->send_motion(d->targetResource(), wl_fixed_from_double(surfacePos.x()), wl_fixed_from_double(surfacePos.y())); + for (auto *resource : d->targetResources()) { + d->send_motion(resource->handle, wl_fixed_from_double(surfacePos.x()), wl_fixed_from_double(surfacePos.y())); + } } void TabletToolV2Interface::sendDistance(uint32_t distance) { - d->send_distance(d->targetResource(), distance); + for (auto *resource : d->targetResources()) { + d->send_distance(resource->handle, distance); + } } void TabletToolV2Interface::sendFrame(uint32_t time) { - d->send_frame(d->targetResource(), time); + for (auto *resource : d->targetResources()) { + d->send_frame(resource->handle, time); + } if (d->m_cleanup) { d->m_surface = nullptr; @@ -321,53 +340,71 @@ void TabletToolV2Interface::sendFrame(uint32_t time) void TabletToolV2Interface::sendPressure(uint32_t pressure) { - d->send_pressure(d->targetResource(), pressure); + for (auto *resource : d->targetResources()) { + d->send_pressure(resource->handle, pressure); + } } void TabletToolV2Interface::sendRotation(qreal rotation) { - d->send_rotation(d->targetResource(), wl_fixed_from_double(rotation)); + for (auto *resource : d->targetResources()) { + d->send_rotation(resource->handle, wl_fixed_from_double(rotation)); + } } void TabletToolV2Interface::sendSlider(int32_t position) { - d->send_slider(d->targetResource(), position); + for (auto *resource : d->targetResources()) { + d->send_slider(resource->handle, position); + } } void TabletToolV2Interface::sendTilt(qreal degreesX, qreal degreesY) { - d->send_tilt(d->targetResource(), wl_fixed_from_double(degreesX), wl_fixed_from_double(degreesY)); + for (auto *resource : d->targetResources()) { + d->send_tilt(resource->handle, wl_fixed_from_double(degreesX), wl_fixed_from_double(degreesY)); + } } void TabletToolV2Interface::sendWheel(int32_t degrees, int32_t clicks) { - d->send_wheel(d->targetResource(), degrees, clicks); + for (auto *resource : d->targetResources()) { + d->send_wheel(resource->handle, degrees, clicks); + } } void TabletToolV2Interface::sendProximityIn(TabletV2Interface *tablet) { wl_resource *tabletResource = tablet->d->resourceForSurface(d->m_surface); - quint32 serial = d->m_display->nextSerial(); - - d->send_proximity_in(d->targetResource(), serial, tabletResource, d->m_surface->resource()); + const auto serial = d->m_display->nextSerial(); + for (auto *resource : d->targetResources()) { + d->send_proximity_in(resource->handle, serial, tabletResource, d->m_surface->resource()); + } d->m_proximitySerial = serial; d->m_lastTablet = tablet; } void TabletToolV2Interface::sendProximityOut() { - d->send_proximity_out(d->targetResource()); + for (auto *resource : d->targetResources()) { + d->send_proximity_out(resource->handle); + } d->m_cleanup = true; } void TabletToolV2Interface::sendDown() { - d->send_down(d->targetResource(), d->m_display->nextSerial()); + const auto serial = d->m_display->nextSerial(); + for (auto *resource : d->targetResources()) { + d->send_down(resource->handle, serial); + } } void TabletToolV2Interface::sendUp() { - d->send_up(d->targetResource()); + for (auto *resource : d->targetResources()) { + d->send_up(resource->handle); + } } class TabletPadRingV2InterfacePrivate : public QtWaylandServer::zwp_tablet_pad_ring_v2 @@ -379,11 +416,11 @@ public: { } - wl_resource *resourceForSurface(SurfaceInterface *surface) const + std::ranges::subrange::const_iterator> resourcesForSurface(SurfaceInterface *surface) const { ClientConnection *client = surface->client(); - Resource *r = resourceMap().value(*client); - return r ? r->handle : nullptr; + const auto [start, end] = resourceMap().equal_range(*client); + return std::ranges::subrange(start, end); } void zwp_tablet_pad_ring_v2_destroy(Resource *resource) override @@ -405,22 +442,30 @@ TabletPadRingV2Interface::~TabletPadRingV2Interface() = default; void TabletPadRingV2Interface::sendAngle(qreal angle) { - d->send_angle(d->resourceForSurface(d->m_pad->currentSurface()), wl_fixed_from_double(angle)); + for (auto *resource : d->resourcesForSurface(d->m_pad->currentSurface())) { + d->send_angle(resource->handle, wl_fixed_from_double(angle)); + } } void TabletPadRingV2Interface::sendFrame(quint32 time) { - d->send_frame(d->resourceForSurface(d->m_pad->currentSurface()), time); + for (auto *resource : d->resourcesForSurface(d->m_pad->currentSurface())) { + d->send_frame(resource->handle, time); + } } void TabletPadRingV2Interface::sendSource(Source source) { - d->send_source(d->resourceForSurface(d->m_pad->currentSurface()), source); + for (auto *resource : d->resourcesForSurface(d->m_pad->currentSurface())) { + d->send_source(resource->handle, source); + } } void TabletPadRingV2Interface::sendStop() { - d->send_stop(d->resourceForSurface(d->m_pad->currentSurface())); + for (auto *resource : d->resourcesForSurface(d->m_pad->currentSurface())) { + d->send_stop(resource->handle); + } } class TabletPadStripV2InterfacePrivate : public QtWaylandServer::zwp_tablet_pad_strip_v2 @@ -432,11 +477,11 @@ public: { } - wl_resource *resourceForSurface(SurfaceInterface *surface) const + std::ranges::subrange::const_iterator> resourcesForSurface(SurfaceInterface *surface) const { ClientConnection *client = surface->client(); - Resource *r = resourceMap().value(*client); - return r ? r->handle : nullptr; + const auto [start, end] = resourceMap().equal_range(*client); + return std::ranges::subrange(start, end); } void zwp_tablet_pad_strip_v2_destroy(Resource *resource) override @@ -458,22 +503,30 @@ TabletPadStripV2Interface::~TabletPadStripV2Interface() = default; void TabletPadStripV2Interface::sendFrame(quint32 time) { - d->send_frame(d->resourceForSurface(d->m_pad->currentSurface()), time); + for (auto *resource : d->resourcesForSurface(d->m_pad->currentSurface())) { + d->send_frame(resource->handle, time); + } } void TabletPadStripV2Interface::sendPosition(quint32 position) { - d->send_position(d->resourceForSurface(d->m_pad->currentSurface()), position); + for (auto *resource : d->resourcesForSurface(d->m_pad->currentSurface())) { + d->send_position(resource->handle, position); + } } void TabletPadStripV2Interface::sendSource(Source source) { - d->send_source(d->resourceForSurface(d->m_pad->currentSurface()), source); + for (auto *resource : d->resourcesForSurface(d->m_pad->currentSurface())) { + d->send_source(resource->handle, source); + } } void TabletPadStripV2Interface::sendStop() { - d->send_stop(d->resourceForSurface(d->m_pad->currentSurface())); + for (auto *resource : d->resourcesForSurface(d->m_pad->currentSurface())) { + d->send_stop(resource->handle); + } } class TabletPadGroupV2InterfacePrivate : public QtWaylandServer::zwp_tablet_pad_group_v2 @@ -486,11 +539,11 @@ public: { } - wl_resource *resourceForSurface(SurfaceInterface *surface) const + std::ranges::subrange::const_iterator> resourcesForSurface(SurfaceInterface *surface) const { ClientConnection *client = surface->client(); - Resource *r = resourceMap().value(*client); - return r ? r->handle : nullptr; + const auto [start, end] = resourceMap().equal_range(*client); + return std::ranges::subrange(start, end); } void zwp_tablet_pad_group_v2_destroy(Resource *resource) override @@ -515,7 +568,9 @@ TabletPadGroupV2Interface::~TabletPadGroupV2Interface() = default; void TabletPadGroupV2Interface::sendModeSwitch(quint32 time, quint32 serial, quint32 mode) { d->m_currentMode = mode; - d->send_mode_switch(d->resourceForSurface(d->m_pad->currentSurface()), time, serial, mode); + for (auto *resource : d->resourcesForSurface(d->m_pad->currentSurface())) { + d->send_mode_switch(resource->handle, time, serial, mode); + } } class TabletPadV2InterfacePrivate : public QtWaylandServer::zwp_tablet_pad_v2 @@ -568,11 +623,11 @@ public: Q_EMIT q->feedback(m_display->getConnection(resource->client()), button, description, serial); } - wl_resource *resourceForSurface(SurfaceInterface *surface) const + std::ranges::subrange::const_iterator> resourcesForSurface(SurfaceInterface *surface) const { ClientConnection *client = surface->client(); - Resource *r = resourceMap().value(*client); - return r ? r->handle : nullptr; + const auto [start, end] = resourceMap().equal_range(*client); + return std::ranges::subrange(start, end); } TabletPadV2Interface *const q; @@ -613,7 +668,10 @@ TabletPadV2Interface::~TabletPadV2Interface() void TabletPadV2Interface::sendButton(std::chrono::microseconds time, quint32 button, bool pressed) { - d->send_button(d->resourceForSurface(currentSurface()), std::chrono::duration_cast(time).count(), button, pressed); + const auto milliseconds = std::chrono::duration_cast(time).count(); + for (auto *resource : d->resourcesForSurface(currentSurface())) { + d->send_button(resource->handle, milliseconds, button, pressed); + } } TabletPadRingV2Interface *TabletPadV2Interface::ring(uint at) const @@ -633,14 +691,20 @@ void TabletPadV2Interface::setCurrentSurface(SurfaceInterface *surface, TabletV2 } if (d->m_currentSurface) { - d->send_leave(d->resourceForSurface(d->m_currentSurface), d->m_display->nextSerial(), d->m_currentSurface->resource()); + auto serial = d->m_display->nextSerial(); + for (auto *resource : d->resourcesForSurface(d->m_currentSurface)) { + d->send_leave(resource->handle, serial, d->m_currentSurface->resource()); + } } d->m_currentSurface = surface; if (surface) { wl_resource *tabletResource = tablet->d->resourceForSurface(surface); - d->send_enter(d->resourceForSurface(surface), d->m_display->nextSerial(), tabletResource, surface->resource()); + auto serial = d->m_display->nextSerial(); + for (auto *resource : d->resourcesForSurface(surface)) { + d->send_enter(resource->handle, serial, tabletResource, surface->resource()); + } d->m_padGroup->sendModeSwitch(0, d->m_display->nextSerial(), d->m_padGroup->d->m_currentMode); } }