From 6c4ded2034e3bae0a98951b91ca5e9e1a1cccc53 Mon Sep 17 00:00:00 2001 From: Aleix Pol Gonzalez Date: Thu, 23 May 2024 15:27:53 +0200 Subject: [PATCH] input: Allow different surfaces to be interacted in parallel by touch While on pointers and keyboards the focus patterns follows rather naturally, on touch screens it doesn't so much. This change adapts our touch infrastructure to allow for multiple surfaces to be issued touch events without forcing all interactions into the same one. Signed-off-by: Victoria Fischer --- autotests/integration/touch_input_test.cpp | 15 +- autotests/wayland/client/test_drag_drop.cpp | 121 +++++++++- .../wayland/client/test_wayland_seat.cpp | 15 +- src/input.cpp | 42 ++-- src/scene/workspacescene.cpp | 3 +- src/touch_input.cpp | 24 -- src/touch_input.h | 1 - src/wayland/datadevice.cpp | 4 +- src/wayland/seat.cpp | 227 ++++++++++-------- src/wayland/seat.h | 31 ++- src/wayland/seat_p.h | 20 +- src/wayland/surface.cpp | 5 + src/wayland/surface.h | 5 + src/wayland/touch.cpp | 96 ++++---- src/wayland/touch.h | 12 +- src/wayland/touch_p.h | 2 +- 16 files changed, 393 insertions(+), 230 deletions(-) diff --git a/autotests/integration/touch_input_test.cpp b/autotests/integration/touch_input_test.cpp index b0fc1ffb90..b9d20c88b6 100644 --- a/autotests/integration/touch_input_test.cpp +++ b/autotests/integration/touch_input_test.cpp @@ -183,22 +183,29 @@ void TouchInputTest::testMultipleTouchPoints() QCOMPARE(pointAddedSpy.count(), 0); QCOMPARE(pointMovedSpy.count(), 0); - // a point outside the window + auto [window2, surface2, shellSurface2, decoration2] = showWindow(decorated); + QCOMPARE(window2->isDecorated(), decorated); + QVERIFY(window2); + window2->moveResize(QRect(0, 0, 100, 50)); + + // a point outside the window, where window2 is Test::touchDown(2, window->mapFromLocal(QPointF(-100, -100)), timestamp++); QVERIFY(pointAddedSpy.wait()); QCOMPARE(pointAddedSpy.count(), 1); QCOMPARE(m_touch->sequence().count(), 2); QCOMPARE(m_touch->sequence().at(1)->isDown(), true); - QCOMPARE(m_touch->sequence().at(1)->position(), QPointF(-100, -100)); + QCOMPARE(m_touch->sequence().at(1)->surface(), surface2.get()); + QCOMPARE(m_touch->sequence().at(1)->position(), QPointF(0, 0)); QCOMPARE(pointMovedSpy.count(), 0); - // let's move that one + // let's move that one. Since it started in window2 it stays with window2 Test::touchMotion(2, window->mapFromLocal(QPointF(0, 0)), timestamp++); QVERIFY(pointMovedSpy.wait()); QCOMPARE(pointMovedSpy.count(), 1); QCOMPARE(m_touch->sequence().count(), 2); QCOMPARE(m_touch->sequence().at(1)->isDown(), true); - QCOMPARE(m_touch->sequence().at(1)->position(), QPointF(0, 0)); + QCOMPARE(m_touch->sequence().at(1)->surface(), surface2.get()); + QCOMPARE(m_touch->sequence().at(1)->position(), QPointF(100, 100)); Test::touchUp(1, timestamp++); QVERIFY(pointRemovedSpy.wait()); diff --git a/autotests/wayland/client/test_drag_drop.cpp b/autotests/wayland/client/test_drag_drop.cpp index 58757454e5..bb97992020 100644 --- a/autotests/wayland/client/test_drag_drop.cpp +++ b/autotests/wayland/client/test_drag_drop.cpp @@ -14,6 +14,7 @@ #include "wayland/display.h" #include "wayland/seat.h" #include "wayland/seat_p.h" +#include "wayland/subcompositor.h" #include "KWayland/Client/compositor.h" #include "KWayland/Client/connection_thread.h" @@ -25,6 +26,8 @@ #include "KWayland/Client/registry.h" #include "KWayland/Client/seat.h" #include "KWayland/Client/shm_pool.h" +#include "KWayland/Client/subcompositor.h" +#include "KWayland/Client/subsurface.h" #include "KWayland/Client/surface.h" #include "KWayland/Client/touch.h" @@ -40,19 +43,23 @@ private Q_SLOTS: void testPointerDragAndDrop(); void testTouchDragAndDrop(); + void testTouchSubsurfacesDragAndDrop(); void testDragAndDropWithCancelByDestroyDataSource(); void testPointerEventsIgnored(); private: KWayland::Client::Surface *createSurface(); + KWayland::Client::SubSurface *createSubSurface(KWayland::Client::Surface *surface, KWayland::Client::Surface *parentSurface); KWin::SurfaceInterface *getServerSurface(); KWin::Display *m_display = nullptr; KWin::CompositorInterface *m_compositorInterface = nullptr; + KWin::SubCompositorInterface *m_subcompositorInterface = nullptr; KWin::DataDeviceManagerInterface *m_dataDeviceManagerInterface = nullptr; KWin::SeatInterface *m_seatInterface = nullptr; KWayland::Client::ConnectionThread *m_connection = nullptr; KWayland::Client::Compositor *m_compositor = nullptr; + KWayland::Client::SubCompositor *m_subcompositor = nullptr; KWayland::Client::EventQueue *m_queue = nullptr; KWayland::Client::DataDevice *m_dataDevice = nullptr; KWayland::Client::DataSource *m_dataSource = nullptr; @@ -82,6 +89,7 @@ void TestDragAndDrop::init() m_connection->setSocketName(s_socketName); m_compositorInterface = new CompositorInterface(m_display, m_display); + m_subcompositorInterface = new SubCompositorInterface(m_display, m_display); m_seatInterface = new SeatInterface(m_display, m_display); m_seatInterface->setHasPointer(true); m_seatInterface->setHasTouch(true); @@ -117,6 +125,7 @@ void TestDragAndDrop::init() QVERIFY(variable); CREATE(m_compositor, Compositor, Compositor) + CREATE(m_subcompositor, SubCompositor, SubCompositor) CREATE(m_seat, Seat, Seat) CREATE(m_ddm, DataDeviceManager, DataDeviceManager) CREATE(m_shm, ShmPool, Shm) @@ -147,10 +156,11 @@ void TestDragAndDrop::cleanup() DELETE(m_dataDevice) DELETE(m_shm) DELETE(m_compositor) + DELETE(m_subcompositor) DELETE(m_ddm) DELETE(m_seat) - DELETE(m_queue) DELETE(m_registry) + DELETE(m_queue) #undef DELETE if (m_thread) { m_thread->quit(); @@ -177,6 +187,13 @@ KWayland::Client::Surface *TestDragAndDrop::createSurface() return s; } +KWayland::Client::SubSurface *TestDragAndDrop::createSubSurface(KWayland::Client::Surface *surface, KWayland::Client::Surface *parentSurface) +{ + auto s = m_subcompositor->createSubSurface(surface, parentSurface); + Q_ASSERT(s->isValid()); + return s; +} + KWin::SurfaceInterface *TestDragAndDrop::getServerSurface() { using namespace KWin; @@ -276,6 +293,105 @@ void TestDragAndDrop::testPointerDragAndDrop() QVERIFY(pointerMotionSpy.isEmpty()); } +void TestDragAndDrop::testTouchSubsurfacesDragAndDrop() +{ + // this test verifies the very basic drag and drop on one surface, an enter, a move and the drop + using namespace KWin; + // first create a window + std::unique_ptr parentSurface(createSurface()); + parentSurface->setSize(QSize(100, 100)); + + auto serverSurface = getServerSurface(); + QVERIFY(serverSurface); + + std::unique_ptr s(createSurface()); + s->setSize(QSize(100, 100)); + std::unique_ptr subSurface(createSubSurface(s.get(), parentSurface.get())); + QVERIFY(subSurface); + subSurface->setPosition({0, 0}); + + auto serverChildSurface = getServerSurface(); + QVERIFY(serverChildSurface); + + QSignalSpy dataSourceSelectedActionChangedSpy(m_dataSource, &KWayland::Client::DataSource::selectedDragAndDropActionChanged); + + auto timestamp = 2ms; + + // now we need to pass touch focus to the Surface and simulate a touch down + QSignalSpy sequenceStartedSpy(m_touch, &KWayland::Client::Touch::sequenceStarted); + QSignalSpy pointAddedSpy(m_touch, &KWayland::Client::Touch::pointAdded); + m_seatInterface->setTimestamp(timestamp++); + const qint32 touchId = 0; + m_seatInterface->notifyTouchDown(serverSurface, QPoint(0, 0), touchId, QPointF(50, 50)); + QVERIFY(sequenceStartedSpy.wait()); + + std::unique_ptr tp(sequenceStartedSpy.first().at(0).value()); + QVERIFY(tp != nullptr); + QCOMPARE(tp->time(), quint32(2)); + + // add some signal spies for client side + QSignalSpy dragEnteredSpy(m_dataDevice, &KWayland::Client::DataDevice::dragEntered); + QSignalSpy dragMotionSpy(m_dataDevice, &KWayland::Client::DataDevice::dragMotion); + QSignalSpy touchMotionSpy(m_touch, &KWayland::Client::Touch::pointMoved); + QSignalSpy sourceDropSpy(m_dataSource, &KWayland::Client::DataSource::dragAndDropPerformed); + + // now we can start the drag and drop + QSignalSpy dragStartedSpy(m_seatInterface, &SeatInterface::dragStarted); + m_dataSource->setDragAndDropActions(KWayland::Client::DataDeviceManager::DnDAction::Copy | KWayland::Client::DataDeviceManager::DnDAction::Move); + m_dataDevice->startDrag(tp->downSerial(), m_dataSource, s.get()); + QVERIFY(dragStartedSpy.wait()); + QCOMPARE(m_seatInterface->dragSurface(), serverSurface); + QCOMPARE(m_seatInterface->dragSurfaceTransformation(), QMatrix4x4()); + QVERIFY(!m_seatInterface->dragIcon()); + QCOMPARE(SeatInterfacePrivate::get(m_seatInterface)->drag.dragImplicitGrabSerial, tp->downSerial()); + QVERIFY(dragEnteredSpy.wait()); + QCOMPARE(dragEnteredSpy.count(), 1); + QCOMPARE(dragEnteredSpy.first().first().value(), m_display->serial()); + QCOMPARE(dragEnteredSpy.first().last().toPointF(), QPointF(50.0, 50.0)); + QCOMPARE(m_dataDevice->dragSurface().data(), parentSurface.get()); + auto offer = m_dataDevice->dragOffer(); + QVERIFY(offer); + QCOMPARE(offer->selectedDragAndDropAction(), KWayland::Client::DataDeviceManager::DnDAction::None); + QSignalSpy offerActionChangedSpy(offer, &KWayland::Client::DataOffer::selectedDragAndDropActionChanged); + QCOMPARE(m_dataDevice->dragOffer()->offeredMimeTypes().count(), 1); + QCOMPARE(m_dataDevice->dragOffer()->offeredMimeTypes().first().name(), QStringLiteral("text/plain")); + QTRY_COMPARE(offer->sourceDragAndDropActions(), KWayland::Client::DataDeviceManager::DnDAction::Copy | KWayland::Client::DataDeviceManager::DnDAction::Move); + offer->accept(QStringLiteral("text/plain"), dragEnteredSpy.last().at(0).toUInt()); + offer->setDragAndDropActions(KWayland::Client::DataDeviceManager::DnDAction::Copy | KWayland::Client::DataDeviceManager::DnDAction::Move, KWayland::Client::DataDeviceManager::DnDAction::Move); + QVERIFY(offerActionChangedSpy.wait()); + QCOMPARE(offerActionChangedSpy.count(), 1); + QCOMPARE(offer->selectedDragAndDropAction(), KWayland::Client::DataDeviceManager::DnDAction::Move); + QCOMPARE(dataSourceSelectedActionChangedSpy.count(), 1); + QCOMPARE(m_dataSource->selectedDragAndDropAction(), KWayland::Client::DataDeviceManager::DnDAction::Move); + + // simulate motion + m_seatInterface->setTimestamp(timestamp++); + m_seatInterface->notifyTouchMotion(touchId, QPointF(75, 75)); + QVERIFY(dragMotionSpy.wait()); + QCOMPARE(dragMotionSpy.count(), 1); + QCOMPARE(dragMotionSpy.first().first().toPointF(), QPointF(75, 75)); + QCOMPARE(dragMotionSpy.first().last().toUInt(), 3u); + + // simulate drop + QSignalSpy serverDragEndedSpy(m_seatInterface, &SeatInterface::dragEnded); + QSignalSpy droppedSpy(m_dataDevice, &KWayland::Client::DataDevice::dropped); + m_seatInterface->setTimestamp(timestamp++); + m_seatInterface->notifyTouchUp(touchId); + QVERIFY(sourceDropSpy.isEmpty()); + QVERIFY(droppedSpy.wait()); + QCOMPARE(sourceDropSpy.count(), 1); + QCOMPARE(serverDragEndedSpy.count(), 1); + + QSignalSpy finishedSpy(m_dataSource, &KWayland::Client::DataSource::dragAndDropFinished); + offer->dragAndDropFinished(); + QVERIFY(finishedSpy.wait()); + delete offer; + + // verify that we did not get any further input events + QVERIFY(touchMotionSpy.isEmpty()); + QCOMPARE(pointAddedSpy.count(), 0); +} + void TestDragAndDrop::testTouchDragAndDrop() { // this test verifies the very basic drag and drop on one surface, an enter, a move and the drop @@ -293,10 +409,9 @@ void TestDragAndDrop::testTouchDragAndDrop() // now we need to pass touch focus to the Surface and simulate a touch down QSignalSpy sequenceStartedSpy(m_touch, &KWayland::Client::Touch::sequenceStarted); QSignalSpy pointAddedSpy(m_touch, &KWayland::Client::Touch::pointAdded); - m_seatInterface->setFocusedTouchSurface(serverSurface); m_seatInterface->setTimestamp(timestamp++); const qint32 touchId = 0; - m_seatInterface->notifyTouchDown(touchId, QPointF(50, 50)); + m_seatInterface->notifyTouchDown(serverSurface, QPoint(0, 0), touchId, QPointF(50, 50)); QVERIFY(sequenceStartedSpy.wait()); std::unique_ptr tp(sequenceStartedSpy.first().at(0).value()); diff --git a/autotests/wayland/client/test_wayland_seat.cpp b/autotests/wayland/client/test_wayland_seat.cpp index 172ff9a576..c390faefb1 100644 --- a/autotests/wayland/client/test_wayland_seat.cpp +++ b/autotests/wayland/client/test_wayland_seat.cpp @@ -1740,9 +1740,8 @@ void TestWaylandSeat::testTouch() SurfaceInterface *serverSurface = surfaceCreatedSpy.first().first().value(); QVERIFY(serverSurface); - m_seatInterface->setFocusedTouchSurface(serverSurface); // no keyboard yet - QCOMPARE(m_seatInterface->focusedTouchSurface(), serverSurface); + QCOMPARE(m_seatInterface->isSurfaceTouched(serverSurface), false); KWayland::Client::Touch *touch = m_seat->createTouch(m_seat); QVERIFY(touch->isValid()); @@ -1762,10 +1761,9 @@ void TestWaylandSeat::testTouch() std::chrono::milliseconds timestamp(1); // try a few things - m_seatInterface->setFocusedTouchSurfacePosition(QPointF(10, 20)); - QCOMPARE(m_seatInterface->focusedTouchSurfacePosition(), QPointF(10, 20)); + const QPointF surfacePosition(10, 20); m_seatInterface->setTimestamp(timestamp++); - m_seatInterface->notifyTouchDown(0, QPointF(15, 26)); + m_seatInterface->notifyTouchDown(serverSurface, QPointF(10, 20), 0, QPointF(15, 26)); QVERIFY(sequenceStartedSpy.wait()); QCOMPARE(sequenceStartedSpy.count(), 1); QCOMPARE(sequenceEndedSpy.count(), 0); @@ -1818,7 +1816,7 @@ void TestWaylandSeat::testTouch() // add onther point m_seatInterface->setTimestamp(timestamp++); - m_seatInterface->notifyTouchDown(1, QPointF(15, 26)); + m_seatInterface->notifyTouchDown(serverSurface, surfacePosition, 1, QPointF(15, 26)); m_seatInterface->notifyTouchFrame(); QVERIFY(frameEndedSpy.wait()); QCOMPARE(sequenceStartedSpy.count(), 1); @@ -1866,7 +1864,7 @@ void TestWaylandSeat::testTouch() // send another down and up m_seatInterface->setTimestamp(timestamp++); - m_seatInterface->notifyTouchDown(1, QPointF(15, 26)); + m_seatInterface->notifyTouchDown(serverSurface, surfacePosition, 1, QPointF(15, 26)); m_seatInterface->notifyTouchFrame(); m_seatInterface->setTimestamp(timestamp++); m_seatInterface->notifyTouchUp(1); @@ -1888,9 +1886,8 @@ void TestWaylandSeat::testTouch() QVERIFY(!m_seatInterface->isTouchSequence()); // try cancel - m_seatInterface->setFocusedTouchSurface(serverSurface, QPointF(15, 26)); m_seatInterface->setTimestamp(timestamp++); - m_seatInterface->notifyTouchDown(0, QPointF(15, 26)); + m_seatInterface->notifyTouchDown(serverSurface, QPointF(15, 26), 0, QPointF(15, 26)); m_seatInterface->notifyTouchFrame(); m_seatInterface->notifyTouchCancel(); QVERIFY(sequenceCanceledSpy.wait()); diff --git a/src/input.cpp b/src/input.cpp index c2f5426af9..bd8b5ef325 100644 --- a/src/input.cpp +++ b/src/input.cpp @@ -419,8 +419,9 @@ public: } auto seat = waylandServer()->seat(); seat->setTimestamp(time); - if (touchSurfaceAllowed()) { - seat->notifyTouchDown(id, pos); + Window *window = input()->findToplevel(pos); + if (window && surfaceAllowed(window->surface())) { + seat->notifyTouchDown(window->surface(), window->bufferGeometry().topLeft(), id, pos); } return true; } @@ -431,9 +432,7 @@ public: } auto seat = waylandServer()->seat(); seat->setTimestamp(time); - if (touchSurfaceAllowed()) { - seat->notifyTouchMotion(id, pos); - } + seat->notifyTouchMotion(id, pos); return true; } bool touchUp(qint32 id, std::chrono::microseconds time) override @@ -443,9 +442,7 @@ public: } auto seat = waylandServer()->seat(); seat->setTimestamp(time); - if (touchSurfaceAllowed()) { - seat->notifyTouchUp(id); - } + seat->notifyTouchUp(id); return true; } bool pinchGestureBegin(int fingerCount, std::chrono::microseconds time) override @@ -501,9 +498,9 @@ public: } private: - bool surfaceAllowed(SurfaceInterface *(SeatInterface::*method)() const) const + bool surfaceAllowed(SurfaceInterface *s) const { - if (SurfaceInterface *s = (waylandServer()->seat()->*method)()) { + if (s) { if (Window *t = waylandServer()->findWindow(s)) { return t->isLockScreen() || t->isInputMethod() || t->isLockScreenOverlay(); } @@ -513,15 +510,11 @@ private: } bool pointerSurfaceAllowed() const { - return surfaceAllowed(&SeatInterface::focusedPointerSurface); + return surfaceAllowed(waylandServer()->seat()->focusedPointerSurface()); } bool keyboardSurfaceAllowed() const { - return surfaceAllowed(&SeatInterface::focusedKeyboardSurface); - } - bool touchSurfaceAllowed() const - { - return surfaceAllowed(&SeatInterface::focusedTouchSurface); + return surfaceAllowed(waylandServer()->seat()->focusedKeyboardSurface()); } }; @@ -1929,8 +1922,20 @@ public: bool touchDown(qint32 id, const QPointF &pos, std::chrono::microseconds time) override { auto seat = waylandServer()->seat(); + auto w = input()->findToplevel(pos); + if (!w) { + qCCritical(KWIN_CORE) << "Could not touch down, there's no window under" << pos; + return false; + } + auto tp = seat->notifyTouchDown(w->surface(), w->bufferGeometry().topLeft(), id, pos); + if (!tp) { + qCCritical(KWIN_CORE) << "Could not touch down" << pos; + return false; + } seat->setTimestamp(time); - seat->notifyTouchDown(id, pos); + QObject::connect(w, &Window::bufferGeometryChanged, tp, [w, tp]() { + tp->setSurfacePosition(w->bufferGeometry().topLeft()); + }); return true; } bool touchMotion(qint32 id, const QPointF &pos, std::chrono::microseconds time) override @@ -2596,8 +2601,9 @@ public: if (m_touchId != id) { return true; } + Window *window = input()->findToplevel(pos); seat->setTimestamp(time); - seat->notifyTouchDown(id, pos); + seat->notifyTouchDown(window->surface(), window->bufferGeometry().topLeft(), id, pos); m_lastPos = pos; return true; } diff --git a/src/scene/workspacescene.cpp b/src/scene/workspacescene.cpp index 5b3456e3ad..aad9fa0e96 100644 --- a/src/scene/workspacescene.cpp +++ b/src/scene/workspacescene.cpp @@ -126,7 +126,8 @@ void WorkspaceScene::createDndIconItem() connect(waylandServer()->seat(), &SeatInterface::pointerPosChanged, m_dndIcon.get(), updatePosition); } else if (waylandServer()->seat()->isDragTouch()) { auto updatePosition = [this]() { - const auto touchPos = waylandServer()->seat()->firstTouchPointPosition(); + auto seat = waylandServer()->seat(); + const auto touchPos = seat->firstTouchPointPosition(seat->dragSurface()); m_dndIcon->setPosition(touchPos); m_dndIcon->setOutput(workspace()->outputAt(touchPos)); }; diff --git a/src/touch_input.cpp b/src/touch_input.cpp index c267b4c4a7..ba48e62832 100644 --- a/src/touch_input.cpp +++ b/src/touch_input.cpp @@ -94,33 +94,9 @@ void TouchInputRedirection::focusUpdate(Window *focusOld, Window *focusNow) if (focusOld && focusOld->isClient()) { focusOld->pointerLeaveEvent(); } - disconnect(m_focusGeometryConnection); - m_focusGeometryConnection = QMetaObject::Connection(); - if (focusNow && focusNow->isClient()) { focusNow->pointerEnterEvent(m_lastPosition); } - - auto seat = waylandServer()->seat(); - if (!focusNow || !focusNow->surface()) { - seat->setFocusedTouchSurface(nullptr); - return; - } - - // TODO: invalidate pointer focus? - - // FIXME: add input transformation API to SeatInterface for touch input - seat->setFocusedTouchSurface(focusNow->surface(), -1 * focusNow->inputTransformation().map(focusNow->pos()) + focusNow->pos()); - m_focusGeometryConnection = connect(focusNow, &Window::frameGeometryChanged, this, [this]() { - if (!focus()) { - return; - } - auto seat = waylandServer()->seat(); - if (focus()->surface() != seat->focusedTouchSurface()) { - return; - } - seat->setFocusedTouchSurfacePosition(-1 * focus()->inputTransformation().map(focus()->pos()) + focus()->pos()); - }); } void TouchInputRedirection::cleanupDecoration(Decoration::DecoratedClientImpl *old, Decoration::DecoratedClientImpl *now) diff --git a/src/touch_input.h b/src/touch_input.h index ffa9c823f0..eeb8b4f4f1 100644 --- a/src/touch_input.h +++ b/src/touch_input.h @@ -79,7 +79,6 @@ private: QSet m_activeTouchPoints; qint32 m_decorationId = -1; qint32 m_internalId = -1; - QMetaObject::Connection m_focusGeometryConnection; bool m_windowUpdatedInCycle = false; QPointF m_lastPosition; }; diff --git a/src/wayland/datadevice.cpp b/src/wayland/datadevice.cpp index b7102fa315..2c50e5ec5f 100644 --- a/src/wayland/datadevice.cpp +++ b/src/wayland/datadevice.cpp @@ -93,7 +93,7 @@ void DataDeviceInterfacePrivate::data_device_start_drag(Resource *resource, const bool pointerGrab = seat->hasImplicitPointerGrab(serial) && seat->focusedPointerSurface() == focusSurface; if (!pointerGrab) { // Client doesn't have pointer grab. - const bool touchGrab = seat->hasImplicitTouchGrab(serial) && seat->focusedTouchSurface() == focusSurface; + const bool touchGrab = seat->hasImplicitTouchGrab(serial) && seat->isSurfaceTouched(focusSurface); if (!touchGrab) { // Client neither has pointer nor touch grab. No drag start allowed. return; @@ -339,7 +339,7 @@ void DataDeviceInterface::updateDragTarget(SurfaceInterface *surface, quint32 se if (d->seat->isDragPointer()) { pos = d->seat->dragSurfaceTransformation().map(d->seat->pointerPos()); } else if (d->seat->isDragTouch()) { - pos = d->seat->dragSurfaceTransformation().map(d->seat->firstTouchPointPosition()); + pos = d->seat->dragSurfaceTransformation().map(d->seat->firstTouchPointPosition(surface)); } d->send_enter(serial, surface->resource(), wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y()), offer ? offer->resource() : nullptr); if (offer) { diff --git a/src/wayland/seat.cpp b/src/wayland/seat.cpp index 4c961e1808..7887cc63f9 100644 --- a/src/wayland/seat.cpp +++ b/src/wayland/seat.cpp @@ -41,6 +41,19 @@ namespace KWin { static const int s_version = 9; +/// Maps surface to the surface at @p pos, be it @p surface or one of its subsurfaces +static SurfaceInterface *mapToSurfaceInPosition(SurfaceInterface *surface, QPointF &pos) +{ + auto ret = surface->inputSurfaceAt(pos); + if (ret && ret != surface) { + pos = surface->mapToChild(ret, pos); + } + if (!ret) { + ret = surface; + } + return ret; +} + SeatInterfacePrivate *SeatInterfacePrivate::get(SeatInterface *seat) { return seat->d.get(); @@ -459,13 +472,7 @@ void SeatInterface::notifyPointerMotion(const QPointF &pos) } QPointF localPosition = focusedPointerSurfaceTransformation().map(pos); - SurfaceInterface *effectiveFocusedSurface = focusedSurface->inputSurfaceAt(localPosition); - if (!effectiveFocusedSurface) { - effectiveFocusedSurface = focusedSurface; - } - if (focusedSurface != effectiveFocusedSurface) { - localPosition = focusedSurface->mapToChild(effectiveFocusedSurface, localPosition); - } + SurfaceInterface *effectiveFocusedSurface = mapToSurfaceInPosition(focusedSurface, localPosition); if (d->pointer->focusedSurface() != effectiveFocusedSurface) { d->pointer->sendEnter(effectiveFocusedSurface, localPosition, display()->nextSerial()); @@ -514,8 +521,8 @@ void SeatInterface::setDragTarget(AbstractDropHandler *dropTarget, if (d->drag.mode == SeatInterfacePrivate::Drag::Mode::Pointer) { notifyPointerMotion(globalPosition); notifyPointerFrame(); - } else if (d->drag.mode == SeatInterfacePrivate::Drag::Mode::Touch && d->globalTouch.focus.firstTouchPos != globalPosition) { - notifyTouchMotion(d->globalTouch.ids.first(), globalPosition); + } else if (d->drag.mode == SeatInterfacePrivate::Drag::Mode::Touch && firstTouchPointPosition(surface) != globalPosition) { + notifyTouchMotion(d->globalTouch.ids.begin()->second->serial, globalPosition); } if (d->drag.target) { @@ -540,7 +547,7 @@ void SeatInterface::setDragTarget(AbstractDropHandler *target, SurfaceInterface setDragTarget(target, surface, pointerPos(), inputTransformation); } else { Q_ASSERT(d->drag.mode == SeatInterfacePrivate::Drag::Mode::Touch); - setDragTarget(target, surface, d->globalTouch.focus.firstTouchPos, inputTransformation); + setDragTarget(target, surface, firstTouchPointPosition(surface), inputTransformation); } } @@ -585,13 +592,7 @@ void SeatInterface::notifyPointerEnter(SurfaceInterface *surface, const QPointF d->globalPointer.pos = position; QPointF localPosition = focusedPointerSurfaceTransformation().map(position); - SurfaceInterface *effectiveFocusedSurface = surface->inputSurfaceAt(localPosition); - if (!effectiveFocusedSurface) { - effectiveFocusedSurface = surface; - } - if (surface != effectiveFocusedSurface) { - localPosition = surface->mapToChild(effectiveFocusedSurface, localPosition); - } + SurfaceInterface *effectiveFocusedSurface = mapToSurfaceInPosition(surface, localPosition); d->pointer->sendEnter(effectiveFocusedSurface, localPosition, serial); if (d->keyboard) { d->keyboard->setModifierFocusSurface(effectiveFocusedSurface); @@ -991,7 +992,10 @@ void SeatInterface::notifyTouchCancel() if (!d->touch) { return; } - d->touch->sendCancel(); + for (auto it = d->globalTouch.focus.begin(), itEnd = d->globalTouch.focus.end(); it != itEnd;) { + d->touch->sendCancel(it->first); + it = d->globalTouch.focus.erase(it); + } if (d->drag.mode == SeatInterfacePrivate::Drag::Mode::Touch) { // cancel the drag, don't drop. serial does not matter @@ -1000,19 +1004,14 @@ void SeatInterface::notifyTouchCancel() d->globalTouch.ids.clear(); } -SurfaceInterface *SeatInterface::focusedTouchSurface() const +bool SeatInterface::isSurfaceTouched(SurfaceInterface *surface) const { - return d->globalTouch.focus.surface; -} - -QPointF SeatInterface::focusedTouchSurfacePosition() const -{ - return d->globalTouch.focus.offset; + return d->globalTouch.focus.contains(surface->mainSurface()); } bool SeatInterface::isTouchSequence() const { - return !d->globalTouch.ids.isEmpty(); + return !d->globalTouch.ids.empty(); } TouchInterface *SeatInterface::touch() const @@ -1020,80 +1019,88 @@ TouchInterface *SeatInterface::touch() const return d->touch.get(); } -QPointF SeatInterface::firstTouchPointPosition() const +QPointF SeatInterface::firstTouchPointPosition(SurfaceInterface *surface) const { - return d->globalTouch.focus.firstTouchPos; + const auto it = d->globalTouch.focus.find(surface); + if (it == d->globalTouch.focus.end()) { + qCWarning(KWIN_CORE) << "Requested a first touch on a surface that isn't touched" << surface; + return {}; + } + return it->second->firstTouchPos; } -void SeatInterface::setFocusedTouchSurface(SurfaceInterface *surface, const QPointF &surfacePosition) +void TouchPoint::setSurfacePosition(const QPointF &surfacePosition) { - if (!d->touch) { - return; - } - if (isTouchSequence()) { - // changing surface not allowed during a touch sequence - return; - } - if (isDragTouch()) { - return; - } - if (d->globalTouch.focus.surface) { - disconnect(d->globalTouch.focus.destroyConnection); - } - d->globalTouch.focus = SeatInterfacePrivate::Touch::Focus(); - d->globalTouch.focus.surface = surface; - setFocusedTouchSurfacePosition(surfacePosition); + auto interaction = seat->d->globalTouch.focus.find(surface); + Q_ASSERT(interaction != seat->d->globalTouch.focus.end()); + interaction->second->offset = surfacePosition; + interaction->second->transformation = QMatrix4x4(); + interaction->second->transformation.translate(-surfacePosition.x(), -surfacePosition.y()); +} - if (d->globalTouch.focus.surface) { - d->globalTouch.focus.destroyConnection = connect(surface, &QObject::destroyed, this, [this]() { - if (isTouchSequence()) { - // Surface destroyed during touch sequence - send a cancel - d->touch->sendCancel(); - } - d->globalTouch.focus = SeatInterfacePrivate::Touch::Focus(); +void SeatInterface::discardSurfaceTouches(SurfaceInterface *surface) +{ + if (!surface) { + return; + } + auto it = d->globalTouch.focus.find(surface); + if (it == d->globalTouch.focus.end()) { + return; + } + + for (auto itId = d->globalTouch.ids.begin(); itId != d->globalTouch.ids.end();) { + if (itId->second->surface == surface) { + itId = d->globalTouch.ids.erase(itId); + } else { + ++itId; + } + } + d->touch->sendCancel(surface); + d->globalTouch.focus.erase(it); +} + +TouchPoint *SeatInterface::notifyTouchDown(SurfaceInterface *surface, const QPointF &surfacePosition, qint32 id, const QPointF &globalPosition) +{ + Q_ASSERT(!d->globalTouch.ids.contains(id)); + if (!d->touch || !surface) { + return {}; + } + + auto it = d->globalTouch.focus.find(surface); + if (it == d->globalTouch.focus.end()) { + d->globalTouch.focus[surface] = std::make_unique(); + it = d->globalTouch.focus.find(surface); + + it->second->firstTouchPos = globalPosition; + it->second->destroyConnection = QObject::connect(surface, &SurfaceInterface::aboutToBeDestroyed, this, [this, surface]() { + discardSurfaceTouches(surface); }); } -} + it->second->refs++; + it->second->offset = surfacePosition; + it->second->transformation = QMatrix4x4(); + it->second->transformation.translate(-surfacePosition.x(), -surfacePosition.y()); -void SeatInterface::setFocusedTouchSurfacePosition(const QPointF &surfacePosition) -{ - d->globalTouch.focus.offset = surfacePosition; - d->globalTouch.focus.transformation = QMatrix4x4(); - d->globalTouch.focus.transformation.translate(-surfacePosition.x(), -surfacePosition.y()); -} + auto pos = globalPosition - it->second->offset; + SurfaceInterface *effectiveTouchedSurface = mapToSurfaceInPosition(surface, pos); + const quint32 serial = display()->nextSerial(); + d->touch->sendDown(effectiveTouchedSurface, id, serial, pos); -void SeatInterface::notifyTouchDown(qint32 id, const QPointF &globalPosition) -{ - if (!d->touch || !focusedTouchSurface()) { - return; - } - const qint32 serial = display()->nextSerial(); - auto pos = globalPosition - d->globalTouch.focus.offset; - - SurfaceInterface *effectiveFocusedSurface = focusedTouchSurface()->inputSurfaceAt(pos); - if (effectiveFocusedSurface && effectiveFocusedSurface != focusedTouchSurface()) { - pos = focusedTouchSurface()->mapToChild(effectiveFocusedSurface, pos); - } else if (!effectiveFocusedSurface) { - effectiveFocusedSurface = focusedTouchSurface(); - } - d->touch->sendDown(id, serial, pos, effectiveFocusedSurface); - - if (id == 0) { - d->globalTouch.focus.firstTouchPos = globalPosition; - } - - if (id == 0 && hasPointer() && focusedTouchSurface()) { + if (id == 0 && hasPointer() && surface) { TouchInterfacePrivate *touchPrivate = TouchInterfacePrivate::get(d->touch.get()); - if (!touchPrivate->hasTouchesForClient(effectiveFocusedSurface->client())) { + if (!touchPrivate->hasTouchesForClient(effectiveTouchedSurface->client())) { // If the client did not bind the touch interface fall back // to at least emulating touch through pointer events. - d->pointer->sendEnter(effectiveFocusedSurface, pos, serial); + d->pointer->sendEnter(effectiveTouchedSurface, pos, serial); d->pointer->sendMotion(pos); d->pointer->sendFrame(); } } - d->globalTouch.ids[id] = serial; + auto tp = std::make_unique(serial, surface, this); + auto r = tp.get(); + d->globalTouch.ids[id] = std::move(tp); + return r; } void SeatInterface::notifyTouchMotion(qint32 id, const QPointF &globalPosition) @@ -1101,38 +1108,41 @@ void SeatInterface::notifyTouchMotion(qint32 id, const QPointF &globalPosition) if (!d->touch) { return; } - auto itTouch = d->globalTouch.ids.constFind(id); - if (itTouch == d->globalTouch.ids.constEnd()) { + auto itTouch = d->globalTouch.ids.find(id); + if (itTouch == d->globalTouch.ids.cend()) { // This can happen in cases where the interaction started while the device was asleep qCWarning(KWIN_CORE) << "Detected a touch move that never has been down, discarding"; return; } + Q_ASSERT(itTouch->second->surface); - auto pos = globalPosition - d->globalTouch.focus.offset; - SurfaceInterface *effectiveFocusedSurface = d->touch->focusedSurface(); - if (effectiveFocusedSurface && focusedTouchSurface() != effectiveFocusedSurface) { - pos = focusedTouchSurface()->mapToChild(effectiveFocusedSurface, pos); + auto interaction = d->globalTouch.focus.find(itTouch->second->surface); + if (interaction == d->globalTouch.focus.end()) { + qCDebug(KWIN_CORE) << "Surface not there, discarding"; + return; } + auto pos = globalPosition - interaction->second->offset; + SurfaceInterface *effectiveTouchedSurface = mapToSurfaceInPosition(d->globalTouch.ids[id]->surface, pos); if (isDragTouch()) { // handled by DataDevice } else { - d->touch->sendMotion(id, pos); + d->touch->sendMotion(effectiveTouchedSurface, id, pos); } if (id == 0) { - d->globalTouch.focus.firstTouchPos = globalPosition; + interaction->second->firstTouchPos = globalPosition; - if (hasPointer() && focusedTouchSurface()) { + if (hasPointer() && itTouch->second->surface) { TouchInterfacePrivate *touchPrivate = TouchInterfacePrivate::get(d->touch.get()); - if (!touchPrivate->hasTouchesForClient(focusedTouchSurface()->client())) { + if (!touchPrivate->hasTouchesForClient(itTouch->second->surface->client())) { // Client did not bind touch, fall back to emulating with pointer events. d->pointer->sendMotion(pos); d->pointer->sendFrame(); } } } - Q_EMIT touchMoved(id, *itTouch, globalPosition); + Q_EMIT touchMoved(id, itTouch->second->serial, globalPosition); } void SeatInterface::notifyTouchUp(qint32 id) @@ -1148,15 +1158,16 @@ void SeatInterface::notifyTouchUp(qint32 id) return; } const qint32 serial = d->display->nextSerial(); - if (d->drag.mode == SeatInterfacePrivate::Drag::Mode::Touch && d->drag.dragImplicitGrabSerial == d->globalTouch.ids.value(id)) { + if (d->drag.mode == SeatInterfacePrivate::Drag::Mode::Touch && d->drag.dragImplicitGrabSerial == itTouch->second->serial) { // the implicitly grabbing touch point has been upped d->endDrag(); } - d->touch->sendUp(id, serial); - if (id == 0 && hasPointer() && focusedTouchSurface()) { + auto client = itTouch->second->surface->client(); + d->touch->sendUp(client, id, serial); + if (id == 0 && hasPointer() && client) { TouchInterfacePrivate *touchPrivate = TouchInterfacePrivate::get(d->touch.get()); - if (!touchPrivate->hasTouchesForClient(focusedTouchSurface()->client())) { + if (!touchPrivate->hasTouchesForClient(client)) { // Client did not bind touch, fall back to emulating with pointer events. const quint32 serial = display()->nextSerial(); d->pointer->sendButton(BTN_LEFT, PointerButtonState::Released, serial); @@ -1164,6 +1175,12 @@ void SeatInterface::notifyTouchUp(qint32 id) } } + auto it = d->globalTouch.focus.find(itTouch->second->surface); + Q_ASSERT(it != d->globalTouch.focus.end()); + it->second->refs--; + if (it->second->refs == 0) { + d->globalTouch.focus.erase(it); + } d->globalTouch.ids.erase(itTouch); } @@ -1177,11 +1194,9 @@ void SeatInterface::notifyTouchFrame() bool SeatInterface::hasImplicitTouchGrab(quint32 serial) const { - if (!d->globalTouch.focus.surface) { - // origin surface has been destroyed - return false; - } - return d->globalTouch.ids.key(serial, -1) != -1; + return std::ranges::any_of(std::as_const(d->globalTouch.ids), [serial](const auto &x) { + return x.second->serial == serial; + }); } bool SeatInterface::isDrag() const @@ -1357,13 +1372,15 @@ void SeatInterface::startDrag(AbstractDataSource *dragSource, SurfaceInterface * if (d->drag.mode != SeatInterfacePrivate::Drag::Mode::None) { return; } + originSurface = originSurface->mainSurface(); if (hasImplicitPointerGrab(dragSerial)) { d->drag.mode = SeatInterfacePrivate::Drag::Mode::Pointer; d->drag.transformation = d->globalPointer.focus.transformation; - } else if (hasImplicitTouchGrab(dragSerial)) { + } else if (hasImplicitTouchGrab(dragSerial) && d->globalTouch.focus.contains(originSurface)) { d->drag.mode = SeatInterfacePrivate::Drag::Mode::Touch; - d->drag.transformation = d->globalTouch.focus.transformation; + // identify touch id + d->drag.transformation = d->globalTouch.focus[originSurface]->transformation; } else { // no implicit grab, abort drag return; diff --git a/src/wayland/seat.h b/src/wayland/seat.h index 650149aab1..3c95c6922f 100644 --- a/src/wayland/seat.h +++ b/src/wayland/seat.h @@ -24,6 +24,7 @@ class DataDeviceInterface; class Display; class KeyboardInterface; class PointerInterface; +class SeatInterface; class SeatInterfacePrivate; class SurfaceInterface; class TextInputV1Interface; @@ -84,6 +85,24 @@ enum class KeyboardKeyState : quint32 { Pressed = 1, }; +class TouchPoint : public QObject +{ + Q_OBJECT +public: + TouchPoint(quint32 serial, SurfaceInterface *surface, SeatInterface *seat) + : serial(serial) + , surface(surface) + , seat(seat) + { + } + + void setSurfacePosition(const QPointF &offset); + + quint32 serial = 0; + SurfaceInterface *surface = nullptr; + SeatInterface *seat = nullptr; +}; + /** * @brief Represents a Seat on the Wayland Display. * @@ -560,18 +579,15 @@ public: * @name touch related methods */ ///@{ - void setFocusedTouchSurface(SurfaceInterface *surface, const QPointF &surfacePosition = QPointF()); - SurfaceInterface *focusedTouchSurface() const; TouchInterface *touch() const; - void setFocusedTouchSurfacePosition(const QPointF &surfacePosition); - QPointF focusedTouchSurfacePosition() const; - void notifyTouchDown(qint32 id, const QPointF &globalPosition); + bool isSurfaceTouched(SurfaceInterface *surface) const; + TouchPoint *notifyTouchDown(SurfaceInterface *surface, const QPointF &surfacePosition, qint32 id, const QPointF &globalPosition); void notifyTouchUp(qint32 id); void notifyTouchMotion(qint32 id, const QPointF &globalPosition); void notifyTouchFrame(); void notifyTouchCancel(); bool isTouchSequence() const; - QPointF firstTouchPointPosition() const; + QPointF firstTouchPointPosition(SurfaceInterface *surface) const; /** * @returns true if there is a touch sequence going on associated with a touch * down of the given @p serial. @@ -714,8 +730,11 @@ Q_SIGNALS: void focusedKeyboardSurfaceAboutToChange(SurfaceInterface *nextSurface); private: + void discardSurfaceTouches(SurfaceInterface *surface); + std::unique_ptr d; friend class SeatInterfacePrivate; + friend class TouchPoint; }; } diff --git a/src/wayland/seat_p.h b/src/wayland/seat_p.h index bdfea7bb05..9413e8174f 100644 --- a/src/wayland/seat_p.h +++ b/src/wayland/seat_p.h @@ -115,16 +115,28 @@ public: // Touch related members struct Touch { - struct Focus + struct Interaction { + Interaction() + { + } + Q_DISABLE_COPY(Interaction) + + ~Interaction() + { + QObject::disconnect(destroyConnection); + } + SurfaceInterface *surface = nullptr; QMetaObject::Connection destroyConnection; - QPointF offset = QPointF(); QPointF firstTouchPos; + QPointF offset; QMatrix4x4 transformation; + uint refs = 0; }; - Focus focus; - QMap ids; + std::unordered_map> focus; + + std::map> ids; }; Touch globalTouch; diff --git a/src/wayland/surface.cpp b/src/wayland/surface.cpp index 8bb2077b21..601aab3286 100644 --- a/src/wayland/surface.cpp +++ b/src/wayland/surface.cpp @@ -870,6 +870,11 @@ SubSurfaceInterface *SurfaceInterface::subSurface() const return d->subsurface.handle; } +SurfaceInterface *SurfaceInterface::mainSurface() +{ + return subSurface() ? subSurface()->mainSurface() : this; +} + QSizeF SurfaceInterface::size() const { return d->surfaceSize; diff --git a/src/wayland/surface.h b/src/wayland/surface.h index dad42f268d..021f91cac6 100644 --- a/src/wayland/surface.h +++ b/src/wayland/surface.h @@ -359,6 +359,11 @@ public: */ void traverseTree(std::function callback); + /** + * @returns the last surface found while traversing the subsurfaces parents + */ + SurfaceInterface *mainSurface(); + Q_SIGNALS: /** * This signal is emitted when the underlying wl_surface resource is about to be freed. diff --git a/src/wayland/touch.cpp b/src/wayland/touch.cpp index c8844417a0..055d3cfa9e 100644 --- a/src/wayland/touch.cpp +++ b/src/wayland/touch.cpp @@ -49,18 +49,13 @@ TouchInterface::~TouchInterface() { } -SurfaceInterface *TouchInterface::focusedSurface() const +void TouchInterface::sendCancel(SurfaceInterface *surface) { - return d->focusedSurface; -} - -void TouchInterface::sendCancel() -{ - if (!d->focusedSurface) { + if (!surface) { return; } - const auto touchResources = d->touchesForClient(d->focusedSurface->client()); + const auto touchResources = d->touchesForClient(surface->client()); for (TouchInterfacePrivate::Resource *resource : touchResources) { d->send_cancel(resource->handle); } @@ -68,62 +63,71 @@ void TouchInterface::sendCancel() void TouchInterface::sendFrame() { - if (!d->focusedSurface) { - return; - } - - const auto touchResources = d->touchesForClient(d->focusedSurface->client()); - for (TouchInterfacePrivate::Resource *resource : touchResources) { - d->send_frame(resource->handle); + for (auto client : std::as_const(d->m_clientsInFrame)) { + if (!client) { + continue; + } + const auto touchResources = d->touchesForClient(client); + for (TouchInterfacePrivate::Resource *resource : touchResources) { + d->send_frame(resource->handle); + } } + d->m_clientsInFrame.clear(); } -void TouchInterface::sendMotion(qint32 id, const QPointF &localPos) -{ - if (!d->focusedSurface) { - return; - } - - QPointF pos = d->focusedSurface->toSurfaceLocal(localPos); - - const auto touchResources = d->touchesForClient(d->focusedSurface->client()); - for (TouchInterfacePrivate::Resource *resource : touchResources) { - d->send_motion(resource->handle, d->seat->timestamp().count(), id, wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y())); - } -} - -void TouchInterface::sendUp(qint32 id, quint32 serial) -{ - if (!d->focusedSurface) { - return; - } - - const auto touchResources = d->touchesForClient(d->focusedSurface->client()); - for (TouchInterfacePrivate::Resource *resource : touchResources) { - d->send_up(resource->handle, serial, d->seat->timestamp().count(), id); - } -} - -void TouchInterface::sendDown(qint32 id, quint32 serial, const QPointF &localPos, SurfaceInterface *surface) +void TouchInterface::sendMotion(SurfaceInterface *surface, qint32 id, const QPointF &localPos) { if (!surface) { return; } - d->focusedSurface = surface; + QPointF pos = surface->toSurfaceLocal(localPos); - QPointF pos = d->focusedSurface->toSurfaceLocal(localPos); + const auto touchResources = d->touchesForClient(surface->client()); + for (TouchInterfacePrivate::Resource *resource : touchResources) { + d->send_motion(resource->handle, d->seat->timestamp().count(), id, wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y())); + } + addToFrame(surface->client()); +} - const auto touchResources = d->touchesForClient(d->focusedSurface->client()); +void TouchInterface::sendUp(ClientConnection *client, qint32 id, quint32 serial) +{ + if (!client) { + return; + } + + const auto touchResources = d->touchesForClient(client); + for (TouchInterfacePrivate::Resource *resource : touchResources) { + d->send_up(resource->handle, serial, d->seat->timestamp().count(), id); + } + addToFrame(client); +} + +void TouchInterface::sendDown(SurfaceInterface *surface, qint32 id, quint32 serial, const QPointF &localPos) +{ + if (!surface) { + return; + } + + const QPointF pos = surface->toSurfaceLocal(localPos); + const auto touchResources = d->touchesForClient(surface->client()); for (TouchInterfacePrivate::Resource *resource : touchResources) { d->send_down(resource->handle, serial, d->seat->timestamp().count(), - d->focusedSurface->resource(), + surface->resource(), id, wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y())); } + addToFrame(surface->client()); +} + +void TouchInterface::addToFrame(ClientConnection *client) +{ + if (!d->m_clientsInFrame.contains(client)) { + d->m_clientsInFrame.append(client); + } } } // namespace KWin diff --git a/src/wayland/touch.h b/src/wayland/touch.h index 32e613dcb1..af810ff044 100644 --- a/src/wayland/touch.h +++ b/src/wayland/touch.h @@ -14,6 +14,7 @@ namespace KWin { +class ClientConnection; class SeatInterface; class SurfaceInterface; class TouchInterfacePrivate; @@ -29,16 +30,15 @@ class KWIN_EXPORT TouchInterface : public QObject public: ~TouchInterface() override; - SurfaceInterface *focusedSurface() const; - - void sendDown(qint32 id, quint32 serial, const QPointF &localPos, SurfaceInterface *surface); - void sendUp(qint32 id, quint32 serial); + void sendDown(SurfaceInterface *surface, qint32 id, quint32 serial, const QPointF &localPos); + void sendUp(ClientConnection *client, qint32 id, quint32 serial); + void sendCancel(SurfaceInterface *surface); + void sendMotion(SurfaceInterface *surface, qint32 id, const QPointF &localPos); void sendFrame(); - void sendCancel(); - void sendMotion(qint32 id, const QPointF &localPos); private: explicit TouchInterface(SeatInterface *seat); + void addToFrame(ClientConnection *client); std::unique_ptr d; friend class SeatInterfacePrivate; diff --git a/src/wayland/touch_p.h b/src/wayland/touch_p.h index 81560b2976..1c3dc3c5a6 100644 --- a/src/wayland/touch_p.h +++ b/src/wayland/touch_p.h @@ -26,8 +26,8 @@ public: bool hasTouchesForClient(ClientConnection *client) const; TouchInterface *q; - QPointer focusedSurface; SeatInterface *seat; + QList> m_clientsInFrame; protected: void touch_release(Resource *resource) override;