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 <victoria.fischer@mercedes-benz.com>
This commit is contained in:
Aleix Pol Gonzalez 2024-05-23 15:27:53 +02:00 committed by Vlad Zahorodnii
parent 912d339489
commit 6c4ded2034
16 changed files with 393 additions and 230 deletions

View file

@ -183,22 +183,29 @@ void TouchInputTest::testMultipleTouchPoints()
QCOMPARE(pointAddedSpy.count(), 0); QCOMPARE(pointAddedSpy.count(), 0);
QCOMPARE(pointMovedSpy.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++); Test::touchDown(2, window->mapFromLocal(QPointF(-100, -100)), timestamp++);
QVERIFY(pointAddedSpy.wait()); QVERIFY(pointAddedSpy.wait());
QCOMPARE(pointAddedSpy.count(), 1); QCOMPARE(pointAddedSpy.count(), 1);
QCOMPARE(m_touch->sequence().count(), 2); QCOMPARE(m_touch->sequence().count(), 2);
QCOMPARE(m_touch->sequence().at(1)->isDown(), true); 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); 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++); Test::touchMotion(2, window->mapFromLocal(QPointF(0, 0)), timestamp++);
QVERIFY(pointMovedSpy.wait()); QVERIFY(pointMovedSpy.wait());
QCOMPARE(pointMovedSpy.count(), 1); QCOMPARE(pointMovedSpy.count(), 1);
QCOMPARE(m_touch->sequence().count(), 2); QCOMPARE(m_touch->sequence().count(), 2);
QCOMPARE(m_touch->sequence().at(1)->isDown(), true); 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++); Test::touchUp(1, timestamp++);
QVERIFY(pointRemovedSpy.wait()); QVERIFY(pointRemovedSpy.wait());

View file

@ -14,6 +14,7 @@
#include "wayland/display.h" #include "wayland/display.h"
#include "wayland/seat.h" #include "wayland/seat.h"
#include "wayland/seat_p.h" #include "wayland/seat_p.h"
#include "wayland/subcompositor.h"
#include "KWayland/Client/compositor.h" #include "KWayland/Client/compositor.h"
#include "KWayland/Client/connection_thread.h" #include "KWayland/Client/connection_thread.h"
@ -25,6 +26,8 @@
#include "KWayland/Client/registry.h" #include "KWayland/Client/registry.h"
#include "KWayland/Client/seat.h" #include "KWayland/Client/seat.h"
#include "KWayland/Client/shm_pool.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/surface.h"
#include "KWayland/Client/touch.h" #include "KWayland/Client/touch.h"
@ -40,19 +43,23 @@ private Q_SLOTS:
void testPointerDragAndDrop(); void testPointerDragAndDrop();
void testTouchDragAndDrop(); void testTouchDragAndDrop();
void testTouchSubsurfacesDragAndDrop();
void testDragAndDropWithCancelByDestroyDataSource(); void testDragAndDropWithCancelByDestroyDataSource();
void testPointerEventsIgnored(); void testPointerEventsIgnored();
private: private:
KWayland::Client::Surface *createSurface(); KWayland::Client::Surface *createSurface();
KWayland::Client::SubSurface *createSubSurface(KWayland::Client::Surface *surface, KWayland::Client::Surface *parentSurface);
KWin::SurfaceInterface *getServerSurface(); KWin::SurfaceInterface *getServerSurface();
KWin::Display *m_display = nullptr; KWin::Display *m_display = nullptr;
KWin::CompositorInterface *m_compositorInterface = nullptr; KWin::CompositorInterface *m_compositorInterface = nullptr;
KWin::SubCompositorInterface *m_subcompositorInterface = nullptr;
KWin::DataDeviceManagerInterface *m_dataDeviceManagerInterface = nullptr; KWin::DataDeviceManagerInterface *m_dataDeviceManagerInterface = nullptr;
KWin::SeatInterface *m_seatInterface = nullptr; KWin::SeatInterface *m_seatInterface = nullptr;
KWayland::Client::ConnectionThread *m_connection = nullptr; KWayland::Client::ConnectionThread *m_connection = nullptr;
KWayland::Client::Compositor *m_compositor = nullptr; KWayland::Client::Compositor *m_compositor = nullptr;
KWayland::Client::SubCompositor *m_subcompositor = nullptr;
KWayland::Client::EventQueue *m_queue = nullptr; KWayland::Client::EventQueue *m_queue = nullptr;
KWayland::Client::DataDevice *m_dataDevice = nullptr; KWayland::Client::DataDevice *m_dataDevice = nullptr;
KWayland::Client::DataSource *m_dataSource = nullptr; KWayland::Client::DataSource *m_dataSource = nullptr;
@ -82,6 +89,7 @@ void TestDragAndDrop::init()
m_connection->setSocketName(s_socketName); m_connection->setSocketName(s_socketName);
m_compositorInterface = new CompositorInterface(m_display, m_display); 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 = new SeatInterface(m_display, m_display);
m_seatInterface->setHasPointer(true); m_seatInterface->setHasPointer(true);
m_seatInterface->setHasTouch(true); m_seatInterface->setHasTouch(true);
@ -117,6 +125,7 @@ void TestDragAndDrop::init()
QVERIFY(variable); QVERIFY(variable);
CREATE(m_compositor, Compositor, Compositor) CREATE(m_compositor, Compositor, Compositor)
CREATE(m_subcompositor, SubCompositor, SubCompositor)
CREATE(m_seat, Seat, Seat) CREATE(m_seat, Seat, Seat)
CREATE(m_ddm, DataDeviceManager, DataDeviceManager) CREATE(m_ddm, DataDeviceManager, DataDeviceManager)
CREATE(m_shm, ShmPool, Shm) CREATE(m_shm, ShmPool, Shm)
@ -147,10 +156,11 @@ void TestDragAndDrop::cleanup()
DELETE(m_dataDevice) DELETE(m_dataDevice)
DELETE(m_shm) DELETE(m_shm)
DELETE(m_compositor) DELETE(m_compositor)
DELETE(m_subcompositor)
DELETE(m_ddm) DELETE(m_ddm)
DELETE(m_seat) DELETE(m_seat)
DELETE(m_queue)
DELETE(m_registry) DELETE(m_registry)
DELETE(m_queue)
#undef DELETE #undef DELETE
if (m_thread) { if (m_thread) {
m_thread->quit(); m_thread->quit();
@ -177,6 +187,13 @@ KWayland::Client::Surface *TestDragAndDrop::createSurface()
return s; 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() KWin::SurfaceInterface *TestDragAndDrop::getServerSurface()
{ {
using namespace KWin; using namespace KWin;
@ -276,6 +293,105 @@ void TestDragAndDrop::testPointerDragAndDrop()
QVERIFY(pointerMotionSpy.isEmpty()); 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<KWayland::Client::Surface> parentSurface(createSurface());
parentSurface->setSize(QSize(100, 100));
auto serverSurface = getServerSurface();
QVERIFY(serverSurface);
std::unique_ptr<KWayland::Client::Surface> s(createSurface());
s->setSize(QSize(100, 100));
std::unique_ptr<KWayland::Client::SubSurface> 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<KWayland::Client::TouchPoint> tp(sequenceStartedSpy.first().at(0).value<KWayland::Client::TouchPoint *>());
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<quint32>(), 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() void TestDragAndDrop::testTouchDragAndDrop()
{ {
// this test verifies the very basic drag and drop on one surface, an enter, a move and the drop // 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 // now we need to pass touch focus to the Surface and simulate a touch down
QSignalSpy sequenceStartedSpy(m_touch, &KWayland::Client::Touch::sequenceStarted); QSignalSpy sequenceStartedSpy(m_touch, &KWayland::Client::Touch::sequenceStarted);
QSignalSpy pointAddedSpy(m_touch, &KWayland::Client::Touch::pointAdded); QSignalSpy pointAddedSpy(m_touch, &KWayland::Client::Touch::pointAdded);
m_seatInterface->setFocusedTouchSurface(serverSurface);
m_seatInterface->setTimestamp(timestamp++); m_seatInterface->setTimestamp(timestamp++);
const qint32 touchId = 0; 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()); QVERIFY(sequenceStartedSpy.wait());
std::unique_ptr<KWayland::Client::TouchPoint> tp(sequenceStartedSpy.first().at(0).value<KWayland::Client::TouchPoint *>()); std::unique_ptr<KWayland::Client::TouchPoint> tp(sequenceStartedSpy.first().at(0).value<KWayland::Client::TouchPoint *>());

View file

@ -1740,9 +1740,8 @@ void TestWaylandSeat::testTouch()
SurfaceInterface *serverSurface = surfaceCreatedSpy.first().first().value<KWin::SurfaceInterface *>(); SurfaceInterface *serverSurface = surfaceCreatedSpy.first().first().value<KWin::SurfaceInterface *>();
QVERIFY(serverSurface); QVERIFY(serverSurface);
m_seatInterface->setFocusedTouchSurface(serverSurface);
// no keyboard yet // no keyboard yet
QCOMPARE(m_seatInterface->focusedTouchSurface(), serverSurface); QCOMPARE(m_seatInterface->isSurfaceTouched(serverSurface), false);
KWayland::Client::Touch *touch = m_seat->createTouch(m_seat); KWayland::Client::Touch *touch = m_seat->createTouch(m_seat);
QVERIFY(touch->isValid()); QVERIFY(touch->isValid());
@ -1762,10 +1761,9 @@ void TestWaylandSeat::testTouch()
std::chrono::milliseconds timestamp(1); std::chrono::milliseconds timestamp(1);
// try a few things // try a few things
m_seatInterface->setFocusedTouchSurfacePosition(QPointF(10, 20)); const QPointF surfacePosition(10, 20);
QCOMPARE(m_seatInterface->focusedTouchSurfacePosition(), QPointF(10, 20));
m_seatInterface->setTimestamp(timestamp++); 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()); QVERIFY(sequenceStartedSpy.wait());
QCOMPARE(sequenceStartedSpy.count(), 1); QCOMPARE(sequenceStartedSpy.count(), 1);
QCOMPARE(sequenceEndedSpy.count(), 0); QCOMPARE(sequenceEndedSpy.count(), 0);
@ -1818,7 +1816,7 @@ void TestWaylandSeat::testTouch()
// add onther point // add onther point
m_seatInterface->setTimestamp(timestamp++); 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->notifyTouchFrame();
QVERIFY(frameEndedSpy.wait()); QVERIFY(frameEndedSpy.wait());
QCOMPARE(sequenceStartedSpy.count(), 1); QCOMPARE(sequenceStartedSpy.count(), 1);
@ -1866,7 +1864,7 @@ void TestWaylandSeat::testTouch()
// send another down and up // send another down and up
m_seatInterface->setTimestamp(timestamp++); 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->notifyTouchFrame();
m_seatInterface->setTimestamp(timestamp++); m_seatInterface->setTimestamp(timestamp++);
m_seatInterface->notifyTouchUp(1); m_seatInterface->notifyTouchUp(1);
@ -1888,9 +1886,8 @@ void TestWaylandSeat::testTouch()
QVERIFY(!m_seatInterface->isTouchSequence()); QVERIFY(!m_seatInterface->isTouchSequence());
// try cancel // try cancel
m_seatInterface->setFocusedTouchSurface(serverSurface, QPointF(15, 26));
m_seatInterface->setTimestamp(timestamp++); 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->notifyTouchFrame();
m_seatInterface->notifyTouchCancel(); m_seatInterface->notifyTouchCancel();
QVERIFY(sequenceCanceledSpy.wait()); QVERIFY(sequenceCanceledSpy.wait());

View file

@ -419,8 +419,9 @@ public:
} }
auto seat = waylandServer()->seat(); auto seat = waylandServer()->seat();
seat->setTimestamp(time); seat->setTimestamp(time);
if (touchSurfaceAllowed()) { Window *window = input()->findToplevel(pos);
seat->notifyTouchDown(id, pos); if (window && surfaceAllowed(window->surface())) {
seat->notifyTouchDown(window->surface(), window->bufferGeometry().topLeft(), id, pos);
} }
return true; return true;
} }
@ -431,9 +432,7 @@ public:
} }
auto seat = waylandServer()->seat(); auto seat = waylandServer()->seat();
seat->setTimestamp(time); seat->setTimestamp(time);
if (touchSurfaceAllowed()) { seat->notifyTouchMotion(id, pos);
seat->notifyTouchMotion(id, pos);
}
return true; return true;
} }
bool touchUp(qint32 id, std::chrono::microseconds time) override bool touchUp(qint32 id, std::chrono::microseconds time) override
@ -443,9 +442,7 @@ public:
} }
auto seat = waylandServer()->seat(); auto seat = waylandServer()->seat();
seat->setTimestamp(time); seat->setTimestamp(time);
if (touchSurfaceAllowed()) { seat->notifyTouchUp(id);
seat->notifyTouchUp(id);
}
return true; return true;
} }
bool pinchGestureBegin(int fingerCount, std::chrono::microseconds time) override bool pinchGestureBegin(int fingerCount, std::chrono::microseconds time) override
@ -501,9 +498,9 @@ public:
} }
private: 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)) { if (Window *t = waylandServer()->findWindow(s)) {
return t->isLockScreen() || t->isInputMethod() || t->isLockScreenOverlay(); return t->isLockScreen() || t->isInputMethod() || t->isLockScreenOverlay();
} }
@ -513,15 +510,11 @@ private:
} }
bool pointerSurfaceAllowed() const bool pointerSurfaceAllowed() const
{ {
return surfaceAllowed(&SeatInterface::focusedPointerSurface); return surfaceAllowed(waylandServer()->seat()->focusedPointerSurface());
} }
bool keyboardSurfaceAllowed() const bool keyboardSurfaceAllowed() const
{ {
return surfaceAllowed(&SeatInterface::focusedKeyboardSurface); return surfaceAllowed(waylandServer()->seat()->focusedKeyboardSurface());
}
bool touchSurfaceAllowed() const
{
return surfaceAllowed(&SeatInterface::focusedTouchSurface);
} }
}; };
@ -1929,8 +1922,20 @@ public:
bool touchDown(qint32 id, const QPointF &pos, std::chrono::microseconds time) override bool touchDown(qint32 id, const QPointF &pos, std::chrono::microseconds time) override
{ {
auto seat = waylandServer()->seat(); 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->setTimestamp(time);
seat->notifyTouchDown(id, pos); QObject::connect(w, &Window::bufferGeometryChanged, tp, [w, tp]() {
tp->setSurfacePosition(w->bufferGeometry().topLeft());
});
return true; return true;
} }
bool touchMotion(qint32 id, const QPointF &pos, std::chrono::microseconds time) override bool touchMotion(qint32 id, const QPointF &pos, std::chrono::microseconds time) override
@ -2596,8 +2601,9 @@ public:
if (m_touchId != id) { if (m_touchId != id) {
return true; return true;
} }
Window *window = input()->findToplevel(pos);
seat->setTimestamp(time); seat->setTimestamp(time);
seat->notifyTouchDown(id, pos); seat->notifyTouchDown(window->surface(), window->bufferGeometry().topLeft(), id, pos);
m_lastPos = pos; m_lastPos = pos;
return true; return true;
} }

View file

@ -126,7 +126,8 @@ void WorkspaceScene::createDndIconItem()
connect(waylandServer()->seat(), &SeatInterface::pointerPosChanged, m_dndIcon.get(), updatePosition); connect(waylandServer()->seat(), &SeatInterface::pointerPosChanged, m_dndIcon.get(), updatePosition);
} else if (waylandServer()->seat()->isDragTouch()) { } else if (waylandServer()->seat()->isDragTouch()) {
auto updatePosition = [this]() { 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->setPosition(touchPos);
m_dndIcon->setOutput(workspace()->outputAt(touchPos)); m_dndIcon->setOutput(workspace()->outputAt(touchPos));
}; };

View file

@ -94,33 +94,9 @@ void TouchInputRedirection::focusUpdate(Window *focusOld, Window *focusNow)
if (focusOld && focusOld->isClient()) { if (focusOld && focusOld->isClient()) {
focusOld->pointerLeaveEvent(); focusOld->pointerLeaveEvent();
} }
disconnect(m_focusGeometryConnection);
m_focusGeometryConnection = QMetaObject::Connection();
if (focusNow && focusNow->isClient()) { if (focusNow && focusNow->isClient()) {
focusNow->pointerEnterEvent(m_lastPosition); 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) void TouchInputRedirection::cleanupDecoration(Decoration::DecoratedClientImpl *old, Decoration::DecoratedClientImpl *now)

View file

@ -79,7 +79,6 @@ private:
QSet<qint32> m_activeTouchPoints; QSet<qint32> m_activeTouchPoints;
qint32 m_decorationId = -1; qint32 m_decorationId = -1;
qint32 m_internalId = -1; qint32 m_internalId = -1;
QMetaObject::Connection m_focusGeometryConnection;
bool m_windowUpdatedInCycle = false; bool m_windowUpdatedInCycle = false;
QPointF m_lastPosition; QPointF m_lastPosition;
}; };

View file

@ -93,7 +93,7 @@ void DataDeviceInterfacePrivate::data_device_start_drag(Resource *resource,
const bool pointerGrab = seat->hasImplicitPointerGrab(serial) && seat->focusedPointerSurface() == focusSurface; const bool pointerGrab = seat->hasImplicitPointerGrab(serial) && seat->focusedPointerSurface() == focusSurface;
if (!pointerGrab) { if (!pointerGrab) {
// Client doesn't have pointer grab. // 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) { if (!touchGrab) {
// Client neither has pointer nor touch grab. No drag start allowed. // Client neither has pointer nor touch grab. No drag start allowed.
return; return;
@ -339,7 +339,7 @@ void DataDeviceInterface::updateDragTarget(SurfaceInterface *surface, quint32 se
if (d->seat->isDragPointer()) { if (d->seat->isDragPointer()) {
pos = d->seat->dragSurfaceTransformation().map(d->seat->pointerPos()); pos = d->seat->dragSurfaceTransformation().map(d->seat->pointerPos());
} else if (d->seat->isDragTouch()) { } 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); d->send_enter(serial, surface->resource(), wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y()), offer ? offer->resource() : nullptr);
if (offer) { if (offer) {

View file

@ -41,6 +41,19 @@ namespace KWin
{ {
static const int s_version = 9; 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) SeatInterfacePrivate *SeatInterfacePrivate::get(SeatInterface *seat)
{ {
return seat->d.get(); return seat->d.get();
@ -459,13 +472,7 @@ void SeatInterface::notifyPointerMotion(const QPointF &pos)
} }
QPointF localPosition = focusedPointerSurfaceTransformation().map(pos); QPointF localPosition = focusedPointerSurfaceTransformation().map(pos);
SurfaceInterface *effectiveFocusedSurface = focusedSurface->inputSurfaceAt(localPosition); SurfaceInterface *effectiveFocusedSurface = mapToSurfaceInPosition(focusedSurface, localPosition);
if (!effectiveFocusedSurface) {
effectiveFocusedSurface = focusedSurface;
}
if (focusedSurface != effectiveFocusedSurface) {
localPosition = focusedSurface->mapToChild(effectiveFocusedSurface, localPosition);
}
if (d->pointer->focusedSurface() != effectiveFocusedSurface) { if (d->pointer->focusedSurface() != effectiveFocusedSurface) {
d->pointer->sendEnter(effectiveFocusedSurface, localPosition, display()->nextSerial()); d->pointer->sendEnter(effectiveFocusedSurface, localPosition, display()->nextSerial());
@ -514,8 +521,8 @@ void SeatInterface::setDragTarget(AbstractDropHandler *dropTarget,
if (d->drag.mode == SeatInterfacePrivate::Drag::Mode::Pointer) { if (d->drag.mode == SeatInterfacePrivate::Drag::Mode::Pointer) {
notifyPointerMotion(globalPosition); notifyPointerMotion(globalPosition);
notifyPointerFrame(); notifyPointerFrame();
} else if (d->drag.mode == SeatInterfacePrivate::Drag::Mode::Touch && d->globalTouch.focus.firstTouchPos != globalPosition) { } else if (d->drag.mode == SeatInterfacePrivate::Drag::Mode::Touch && firstTouchPointPosition(surface) != globalPosition) {
notifyTouchMotion(d->globalTouch.ids.first(), globalPosition); notifyTouchMotion(d->globalTouch.ids.begin()->second->serial, globalPosition);
} }
if (d->drag.target) { if (d->drag.target) {
@ -540,7 +547,7 @@ void SeatInterface::setDragTarget(AbstractDropHandler *target, SurfaceInterface
setDragTarget(target, surface, pointerPos(), inputTransformation); setDragTarget(target, surface, pointerPos(), inputTransformation);
} else { } else {
Q_ASSERT(d->drag.mode == SeatInterfacePrivate::Drag::Mode::Touch); 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; d->globalPointer.pos = position;
QPointF localPosition = focusedPointerSurfaceTransformation().map(position); QPointF localPosition = focusedPointerSurfaceTransformation().map(position);
SurfaceInterface *effectiveFocusedSurface = surface->inputSurfaceAt(localPosition); SurfaceInterface *effectiveFocusedSurface = mapToSurfaceInPosition(surface, localPosition);
if (!effectiveFocusedSurface) {
effectiveFocusedSurface = surface;
}
if (surface != effectiveFocusedSurface) {
localPosition = surface->mapToChild(effectiveFocusedSurface, localPosition);
}
d->pointer->sendEnter(effectiveFocusedSurface, localPosition, serial); d->pointer->sendEnter(effectiveFocusedSurface, localPosition, serial);
if (d->keyboard) { if (d->keyboard) {
d->keyboard->setModifierFocusSurface(effectiveFocusedSurface); d->keyboard->setModifierFocusSurface(effectiveFocusedSurface);
@ -991,7 +992,10 @@ void SeatInterface::notifyTouchCancel()
if (!d->touch) { if (!d->touch) {
return; 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) { if (d->drag.mode == SeatInterfacePrivate::Drag::Mode::Touch) {
// cancel the drag, don't drop. serial does not matter // cancel the drag, don't drop. serial does not matter
@ -1000,19 +1004,14 @@ void SeatInterface::notifyTouchCancel()
d->globalTouch.ids.clear(); d->globalTouch.ids.clear();
} }
SurfaceInterface *SeatInterface::focusedTouchSurface() const bool SeatInterface::isSurfaceTouched(SurfaceInterface *surface) const
{ {
return d->globalTouch.focus.surface; return d->globalTouch.focus.contains(surface->mainSurface());
}
QPointF SeatInterface::focusedTouchSurfacePosition() const
{
return d->globalTouch.focus.offset;
} }
bool SeatInterface::isTouchSequence() const bool SeatInterface::isTouchSequence() const
{ {
return !d->globalTouch.ids.isEmpty(); return !d->globalTouch.ids.empty();
} }
TouchInterface *SeatInterface::touch() const TouchInterface *SeatInterface::touch() const
@ -1020,80 +1019,88 @@ TouchInterface *SeatInterface::touch() const
return d->touch.get(); 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) { auto interaction = seat->d->globalTouch.focus.find(surface);
return; Q_ASSERT(interaction != seat->d->globalTouch.focus.end());
} interaction->second->offset = surfacePosition;
if (isTouchSequence()) { interaction->second->transformation = QMatrix4x4();
// changing surface not allowed during a touch sequence interaction->second->transformation.translate(-surfacePosition.x(), -surfacePosition.y());
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);
if (d->globalTouch.focus.surface) { void SeatInterface::discardSurfaceTouches(SurfaceInterface *surface)
d->globalTouch.focus.destroyConnection = connect(surface, &QObject::destroyed, this, [this]() { {
if (isTouchSequence()) { if (!surface) {
// Surface destroyed during touch sequence - send a cancel return;
d->touch->sendCancel(); }
} auto it = d->globalTouch.focus.find(surface);
d->globalTouch.focus = SeatInterfacePrivate::Touch::Focus(); 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<SeatInterfacePrivate::Touch::Interaction>();
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) auto pos = globalPosition - it->second->offset;
{ SurfaceInterface *effectiveTouchedSurface = mapToSurfaceInPosition(surface, pos);
d->globalTouch.focus.offset = surfacePosition; const quint32 serial = display()->nextSerial();
d->globalTouch.focus.transformation = QMatrix4x4(); d->touch->sendDown(effectiveTouchedSurface, id, serial, pos);
d->globalTouch.focus.transformation.translate(-surfacePosition.x(), -surfacePosition.y());
}
void SeatInterface::notifyTouchDown(qint32 id, const QPointF &globalPosition) if (id == 0 && hasPointer() && surface) {
{
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()) {
TouchInterfacePrivate *touchPrivate = TouchInterfacePrivate::get(d->touch.get()); 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 // If the client did not bind the touch interface fall back
// to at least emulating touch through pointer events. // 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->sendMotion(pos);
d->pointer->sendFrame(); d->pointer->sendFrame();
} }
} }
d->globalTouch.ids[id] = serial; auto tp = std::make_unique<TouchPoint>(serial, surface, this);
auto r = tp.get();
d->globalTouch.ids[id] = std::move(tp);
return r;
} }
void SeatInterface::notifyTouchMotion(qint32 id, const QPointF &globalPosition) void SeatInterface::notifyTouchMotion(qint32 id, const QPointF &globalPosition)
@ -1101,38 +1108,41 @@ void SeatInterface::notifyTouchMotion(qint32 id, const QPointF &globalPosition)
if (!d->touch) { if (!d->touch) {
return; return;
} }
auto itTouch = d->globalTouch.ids.constFind(id); auto itTouch = d->globalTouch.ids.find(id);
if (itTouch == d->globalTouch.ids.constEnd()) { if (itTouch == d->globalTouch.ids.cend()) {
// This can happen in cases where the interaction started while the device was asleep // 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"; qCWarning(KWIN_CORE) << "Detected a touch move that never has been down, discarding";
return; return;
} }
Q_ASSERT(itTouch->second->surface);
auto pos = globalPosition - d->globalTouch.focus.offset; auto interaction = d->globalTouch.focus.find(itTouch->second->surface);
SurfaceInterface *effectiveFocusedSurface = d->touch->focusedSurface(); if (interaction == d->globalTouch.focus.end()) {
if (effectiveFocusedSurface && focusedTouchSurface() != effectiveFocusedSurface) { qCDebug(KWIN_CORE) << "Surface not there, discarding";
pos = focusedTouchSurface()->mapToChild(effectiveFocusedSurface, pos); return;
} }
auto pos = globalPosition - interaction->second->offset;
SurfaceInterface *effectiveTouchedSurface = mapToSurfaceInPosition(d->globalTouch.ids[id]->surface, pos);
if (isDragTouch()) { if (isDragTouch()) {
// handled by DataDevice // handled by DataDevice
} else { } else {
d->touch->sendMotion(id, pos); d->touch->sendMotion(effectiveTouchedSurface, id, pos);
} }
if (id == 0) { 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()); 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. // Client did not bind touch, fall back to emulating with pointer events.
d->pointer->sendMotion(pos); d->pointer->sendMotion(pos);
d->pointer->sendFrame(); d->pointer->sendFrame();
} }
} }
} }
Q_EMIT touchMoved(id, *itTouch, globalPosition); Q_EMIT touchMoved(id, itTouch->second->serial, globalPosition);
} }
void SeatInterface::notifyTouchUp(qint32 id) void SeatInterface::notifyTouchUp(qint32 id)
@ -1148,15 +1158,16 @@ void SeatInterface::notifyTouchUp(qint32 id)
return; return;
} }
const qint32 serial = d->display->nextSerial(); 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 // the implicitly grabbing touch point has been upped
d->endDrag(); 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()); 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. // Client did not bind touch, fall back to emulating with pointer events.
const quint32 serial = display()->nextSerial(); const quint32 serial = display()->nextSerial();
d->pointer->sendButton(BTN_LEFT, PointerButtonState::Released, serial); 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); d->globalTouch.ids.erase(itTouch);
} }
@ -1177,11 +1194,9 @@ void SeatInterface::notifyTouchFrame()
bool SeatInterface::hasImplicitTouchGrab(quint32 serial) const bool SeatInterface::hasImplicitTouchGrab(quint32 serial) const
{ {
if (!d->globalTouch.focus.surface) { return std::ranges::any_of(std::as_const(d->globalTouch.ids), [serial](const auto &x) {
// origin surface has been destroyed return x.second->serial == serial;
return false; });
}
return d->globalTouch.ids.key(serial, -1) != -1;
} }
bool SeatInterface::isDrag() const bool SeatInterface::isDrag() const
@ -1357,13 +1372,15 @@ void SeatInterface::startDrag(AbstractDataSource *dragSource, SurfaceInterface *
if (d->drag.mode != SeatInterfacePrivate::Drag::Mode::None) { if (d->drag.mode != SeatInterfacePrivate::Drag::Mode::None) {
return; return;
} }
originSurface = originSurface->mainSurface();
if (hasImplicitPointerGrab(dragSerial)) { if (hasImplicitPointerGrab(dragSerial)) {
d->drag.mode = SeatInterfacePrivate::Drag::Mode::Pointer; d->drag.mode = SeatInterfacePrivate::Drag::Mode::Pointer;
d->drag.transformation = d->globalPointer.focus.transformation; 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.mode = SeatInterfacePrivate::Drag::Mode::Touch;
d->drag.transformation = d->globalTouch.focus.transformation; // identify touch id
d->drag.transformation = d->globalTouch.focus[originSurface]->transformation;
} else { } else {
// no implicit grab, abort drag // no implicit grab, abort drag
return; return;

View file

@ -24,6 +24,7 @@ class DataDeviceInterface;
class Display; class Display;
class KeyboardInterface; class KeyboardInterface;
class PointerInterface; class PointerInterface;
class SeatInterface;
class SeatInterfacePrivate; class SeatInterfacePrivate;
class SurfaceInterface; class SurfaceInterface;
class TextInputV1Interface; class TextInputV1Interface;
@ -84,6 +85,24 @@ enum class KeyboardKeyState : quint32 {
Pressed = 1, 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. * @brief Represents a Seat on the Wayland Display.
* *
@ -560,18 +579,15 @@ public:
* @name touch related methods * @name touch related methods
*/ */
///@{ ///@{
void setFocusedTouchSurface(SurfaceInterface *surface, const QPointF &surfacePosition = QPointF());
SurfaceInterface *focusedTouchSurface() const;
TouchInterface *touch() const; TouchInterface *touch() const;
void setFocusedTouchSurfacePosition(const QPointF &surfacePosition); bool isSurfaceTouched(SurfaceInterface *surface) const;
QPointF focusedTouchSurfacePosition() const; TouchPoint *notifyTouchDown(SurfaceInterface *surface, const QPointF &surfacePosition, qint32 id, const QPointF &globalPosition);
void notifyTouchDown(qint32 id, const QPointF &globalPosition);
void notifyTouchUp(qint32 id); void notifyTouchUp(qint32 id);
void notifyTouchMotion(qint32 id, const QPointF &globalPosition); void notifyTouchMotion(qint32 id, const QPointF &globalPosition);
void notifyTouchFrame(); void notifyTouchFrame();
void notifyTouchCancel(); void notifyTouchCancel();
bool isTouchSequence() const; 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 * @returns true if there is a touch sequence going on associated with a touch
* down of the given @p serial. * down of the given @p serial.
@ -714,8 +730,11 @@ Q_SIGNALS:
void focusedKeyboardSurfaceAboutToChange(SurfaceInterface *nextSurface); void focusedKeyboardSurfaceAboutToChange(SurfaceInterface *nextSurface);
private: private:
void discardSurfaceTouches(SurfaceInterface *surface);
std::unique_ptr<SeatInterfacePrivate> d; std::unique_ptr<SeatInterfacePrivate> d;
friend class SeatInterfacePrivate; friend class SeatInterfacePrivate;
friend class TouchPoint;
}; };
} }

View file

@ -115,16 +115,28 @@ public:
// Touch related members // Touch related members
struct Touch struct Touch
{ {
struct Focus struct Interaction
{ {
Interaction()
{
}
Q_DISABLE_COPY(Interaction)
~Interaction()
{
QObject::disconnect(destroyConnection);
}
SurfaceInterface *surface = nullptr; SurfaceInterface *surface = nullptr;
QMetaObject::Connection destroyConnection; QMetaObject::Connection destroyConnection;
QPointF offset = QPointF();
QPointF firstTouchPos; QPointF firstTouchPos;
QPointF offset;
QMatrix4x4 transformation; QMatrix4x4 transformation;
uint refs = 0;
}; };
Focus focus; std::unordered_map<SurfaceInterface *, std::unique_ptr<Interaction>> focus;
QMap<qint32, quint32> ids;
std::map<qint32, std::unique_ptr<TouchPoint>> ids;
}; };
Touch globalTouch; Touch globalTouch;

View file

@ -870,6 +870,11 @@ SubSurfaceInterface *SurfaceInterface::subSurface() const
return d->subsurface.handle; return d->subsurface.handle;
} }
SurfaceInterface *SurfaceInterface::mainSurface()
{
return subSurface() ? subSurface()->mainSurface() : this;
}
QSizeF SurfaceInterface::size() const QSizeF SurfaceInterface::size() const
{ {
return d->surfaceSize; return d->surfaceSize;

View file

@ -359,6 +359,11 @@ public:
*/ */
void traverseTree(std::function<void(SurfaceInterface *surface)> callback); void traverseTree(std::function<void(SurfaceInterface *surface)> callback);
/**
* @returns the last surface found while traversing the subsurfaces parents
*/
SurfaceInterface *mainSurface();
Q_SIGNALS: Q_SIGNALS:
/** /**
* This signal is emitted when the underlying wl_surface resource is about to be freed. * This signal is emitted when the underlying wl_surface resource is about to be freed.

View file

@ -49,18 +49,13 @@ TouchInterface::~TouchInterface()
{ {
} }
SurfaceInterface *TouchInterface::focusedSurface() const void TouchInterface::sendCancel(SurfaceInterface *surface)
{ {
return d->focusedSurface; if (!surface) {
}
void TouchInterface::sendCancel()
{
if (!d->focusedSurface) {
return; return;
} }
const auto touchResources = d->touchesForClient(d->focusedSurface->client()); const auto touchResources = d->touchesForClient(surface->client());
for (TouchInterfacePrivate::Resource *resource : touchResources) { for (TouchInterfacePrivate::Resource *resource : touchResources) {
d->send_cancel(resource->handle); d->send_cancel(resource->handle);
} }
@ -68,62 +63,71 @@ void TouchInterface::sendCancel()
void TouchInterface::sendFrame() void TouchInterface::sendFrame()
{ {
if (!d->focusedSurface) { for (auto client : std::as_const(d->m_clientsInFrame)) {
return; if (!client) {
} continue;
}
const auto touchResources = d->touchesForClient(d->focusedSurface->client()); const auto touchResources = d->touchesForClient(client);
for (TouchInterfacePrivate::Resource *resource : touchResources) { for (TouchInterfacePrivate::Resource *resource : touchResources) {
d->send_frame(resource->handle); d->send_frame(resource->handle);
}
} }
d->m_clientsInFrame.clear();
} }
void TouchInterface::sendMotion(qint32 id, const QPointF &localPos) void TouchInterface::sendMotion(SurfaceInterface *surface, 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)
{ {
if (!surface) { if (!surface) {
return; 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) { for (TouchInterfacePrivate::Resource *resource : touchResources) {
d->send_down(resource->handle, d->send_down(resource->handle,
serial, serial,
d->seat->timestamp().count(), d->seat->timestamp().count(),
d->focusedSurface->resource(), surface->resource(),
id, id,
wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.x()),
wl_fixed_from_double(pos.y())); 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 } // namespace KWin

View file

@ -14,6 +14,7 @@
namespace KWin namespace KWin
{ {
class ClientConnection;
class SeatInterface; class SeatInterface;
class SurfaceInterface; class SurfaceInterface;
class TouchInterfacePrivate; class TouchInterfacePrivate;
@ -29,16 +30,15 @@ class KWIN_EXPORT TouchInterface : public QObject
public: public:
~TouchInterface() override; ~TouchInterface() override;
SurfaceInterface *focusedSurface() const; void sendDown(SurfaceInterface *surface, qint32 id, quint32 serial, const QPointF &localPos);
void sendUp(ClientConnection *client, qint32 id, quint32 serial);
void sendDown(qint32 id, quint32 serial, const QPointF &localPos, SurfaceInterface *surface); void sendCancel(SurfaceInterface *surface);
void sendUp(qint32 id, quint32 serial); void sendMotion(SurfaceInterface *surface, qint32 id, const QPointF &localPos);
void sendFrame(); void sendFrame();
void sendCancel();
void sendMotion(qint32 id, const QPointF &localPos);
private: private:
explicit TouchInterface(SeatInterface *seat); explicit TouchInterface(SeatInterface *seat);
void addToFrame(ClientConnection *client);
std::unique_ptr<TouchInterfacePrivate> d; std::unique_ptr<TouchInterfacePrivate> d;
friend class SeatInterfacePrivate; friend class SeatInterfacePrivate;

View file

@ -26,8 +26,8 @@ public:
bool hasTouchesForClient(ClientConnection *client) const; bool hasTouchesForClient(ClientConnection *client) const;
TouchInterface *q; TouchInterface *q;
QPointer<SurfaceInterface> focusedSurface;
SeatInterface *seat; SeatInterface *seat;
QList<QPointer<ClientConnection>> m_clientsInFrame;
protected: protected:
void touch_release(Resource *resource) override; void touch_release(Resource *resource) override;