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;