diff --git a/src/server/datadevice_interface.cpp b/src/server/datadevice_interface.cpp --- a/src/server/datadevice_interface.cpp +++ b/src/server/datadevice_interface.cpp @@ -54,7 +54,7 @@ struct Drag { SurfaceInterface *surface = nullptr; QMetaObject::Connection destroyConnection; - QMetaObject::Connection pointerPosConnection; + QMetaObject::Connection posConnection; QMetaObject::Connection sourceActionConnection; QMetaObject::Connection targetActionConnection; quint32 serial = 0; @@ -99,10 +99,14 @@ void DataDeviceInterface::Private::startDrag(DataSourceInterface *dataSource, SurfaceInterface *origin, SurfaceInterface *i, quint32 serial) { - // TODO: allow touch - if (!seat->hasImplicitPointerGrab(serial) || seat->focusedPointerSurface() != origin) { - // Surface doesn't have pointer grab. - return; + const bool pointerGrab = seat->hasImplicitPointerGrab(serial) && seat->focusedPointerSurface() == origin; + if (!pointerGrab) { + // Client doesn't have pointer grab. + const bool touchGrab = seat->hasImplicitTouchGrab(serial) && seat->focusedTouchSurface() == origin; + if (!touchGrab) { + // Client neither has pointer nor touch grab. No drag start allowed. + return; + } } // TODO: source is allowed to be null, handled client internally! Q_Q(DataDeviceInterface); @@ -248,9 +252,9 @@ return; } wl_data_device_send_drop(d->resource); - if (d->drag.pointerPosConnection) { - disconnect(d->drag.pointerPosConnection); - d->drag.pointerPosConnection = QMetaObject::Connection(); + if (d->drag.posConnection) { + disconnect(d->drag.posConnection); + d->drag.posConnection = QMetaObject::Connection(); } disconnect(d->drag.destroyConnection); d->drag.destroyConnection = QMetaObject::Connection(); @@ -265,9 +269,9 @@ if (d->resource && d->drag.surface->resource()) { wl_data_device_send_leave(d->resource); } - if (d->drag.pointerPosConnection) { - disconnect(d->drag.pointerPosConnection); - d->drag.pointerPosConnection = QMetaObject::Connection(); + if (d->drag.posConnection) { + disconnect(d->drag.posConnection); + d->drag.posConnection = QMetaObject::Connection(); } disconnect(d->drag.destroyConnection); d->drag.destroyConnection = QMetaObject::Connection(); @@ -292,25 +296,39 @@ DataOfferInterface *offer = d->createDataOffer(source); d->drag.surface = surface; if (d->seat->isDragPointer()) { - d->drag.pointerPosConnection = connect(d->seat, &SeatInterface::pointerPosChanged, this, + d->drag.posConnection = connect(d->seat, &SeatInterface::pointerPosChanged, this, [this] { Q_D(); const QPointF pos = d->seat->dragSurfaceTransformation().map(d->seat->pointerPos()); wl_data_device_send_motion(d->resource, d->seat->timestamp(), wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y())); client()->flush(); } ); + } else if (d->seat->isDragTouch()) { + d->drag.posConnection = connect(d->seat, &SeatInterface::touchMoved, this, + [this](qint32 id, quint32 serial, const QPointF &globalPosition) { + Q_D(); + Q_UNUSED(id); + if (serial != d->drag.serial) { + // different touch down has been moved + return; + } + const QPointF pos = d->seat->dragSurfaceTransformation().map(globalPosition); + wl_data_device_send_motion(d->resource, d->seat->timestamp(), + wl_fixed_from_double(pos.x()), wl_fixed_from_double(pos.y())); + client()->flush(); + } + ); } - // TODO: same for touch d->drag.destroyConnection = connect(d->drag.surface, &QObject::destroyed, this, [this] { Q_D(); if (d->resource) { wl_data_device_send_leave(d->resource); } - if (d->drag.pointerPosConnection) { - disconnect(d->drag.pointerPosConnection); + if (d->drag.posConnection) { + disconnect(d->drag.posConnection); } d->drag = Private::Drag(); } diff --git a/src/server/seat_interface.h b/src/server/seat_interface.h --- a/src/server/seat_interface.h +++ b/src/server/seat_interface.h @@ -589,6 +589,12 @@ void touchFrame(); void cancelTouchSequence(); bool isTouchSequence() const; + /** + * @returns true if there is a touch sequence going on associated with a touch + * down of the given @p serial. + * @since 5.XX + **/ + bool hasImplicitTouchGrab(quint32 serial) const; ///@} /** @@ -663,6 +669,7 @@ void hasKeyboardChanged(bool); void hasTouchChanged(bool); void pointerPosChanged(const QPointF &pos); + void touchMoved(qint32 id, quint32 serial, const QPointF &globalPosition); void timestampChanged(quint32); void pointerCreated(KWayland::Server::PointerInterface*); diff --git a/src/server/seat_interface.cpp b/src/server/seat_interface.cpp --- a/src/server/seat_interface.cpp +++ b/src/server/seat_interface.cpp @@ -292,18 +292,23 @@ ); QObject::connect(dataDevice, &DataDeviceInterface::dragStarted, q, [this, dataDevice] { - if (q->hasImplicitPointerGrab(dataDevice->dragImplicitGrabSerial())) { + const auto dragSerial = dataDevice->dragImplicitGrabSerial(); + auto *dragSurface = dataDevice->origin(); + if (q->hasImplicitPointerGrab(dragSerial)) { drag.mode = Drag::Mode::Pointer; + drag.sourcePointer = interfaceForSurface(dragSurface, pointers); + drag.transformation = globalPointer.focus.transformation; + } else if (q->hasImplicitTouchGrab(dragSerial)) { + drag.mode = Drag::Mode::Touch; + drag.sourceTouch = interfaceForSurface(dragSurface, touchs); + // TODO: touch transformation } else { - // TODO: touch + // no implicit grab, abort drag return; } drag.source = dataDevice; drag.target = dataDevice; - drag.surface = dataDevice->origin(); - drag.sourcePointer = interfaceForSurface(drag.surface, pointers); - // TODO: transformation needs to be either pointer or touch - drag.transformation = globalPointer.focus.transformation; + drag.surface = dragSurface; drag.destroyConnection = QObject::connect(dataDevice, &QObject::destroyed, q, [this] { endDrag(display->nextSerial()); @@ -645,9 +650,11 @@ d->drag.target->updateDragTarget(nullptr, serial); } d->drag.target = d->dataDeviceForSurface(surface); - // TODO: update touch if (d->drag.mode == Private::Drag::Mode::Pointer) { setPointerPos(globalPosition); + } else if (d->drag.mode == Private::Drag::Mode::Touch && + d->globalTouch.focus.firstTouchPos != globalPosition) { + touchMove(d->globalTouch.ids.first(), globalPosition); } if (d->drag.target) { d->drag.surface = surface; @@ -662,8 +669,14 @@ void SeatInterface::setDragTarget(SurfaceInterface *surface, const QMatrix4x4 &inputTransformation) { - // TODO: handle touch - setDragTarget(surface, pointerPos(), inputTransformation); + Q_D(); + if (d->drag.mode == Private::Drag::Mode::Pointer) { + setDragTarget(surface, pointerPos(), inputTransformation); + } else { + Q_ASSERT(d->drag.mode == Private::Drag::Mode::Touch); + setDragTarget(surface, d->globalTouch.focus.firstTouchPos, inputTransformation); + } + } SurfaceInterface *SeatInterface::focusedPointerSurface() const @@ -1253,6 +1266,16 @@ for (auto it = d->globalTouch.focus.touchs.constBegin(), end = d->globalTouch.focus.touchs.constEnd(); it != end; ++it) { (*it)->cancel(); } + if (d->drag.mode == Private::Drag::Mode::Touch) { + // cancel the drag, don't drop. + if (d->drag.target) { + // remove the current target + d->drag.target->updateDragTarget(nullptr, 0); + d->drag.target = nullptr; + } + // and end the drag for the source, serial does not matter + d->endDrag(0); + } d->globalTouch.ids.clear(); } @@ -1289,6 +1312,7 @@ // changing surface not allowed during a touch sequence return; } + Q_ASSERT(!isDragTouch()); Q_D(); if (d->globalTouch.focus.surface) { disconnect(d->globalTouch.focus.destroyConnection); @@ -1329,6 +1353,10 @@ (*it)->down(id, serial, pos); } + if (id == 0) { + d->globalTouch.focus.firstTouchPos = globalPosition; + } + #if HAVE_LINUX_INPUT_H if (id == 0 && d->globalTouch.focus.touchs.isEmpty()) { // If the client did not bind the touch interface fall back @@ -1348,7 +1376,7 @@ } #endif - d->globalTouch.ids << id; + d->globalTouch.ids[id] = serial; return id; } @@ -1361,6 +1389,10 @@ (*it)->move(id, pos); } + if (id == 0) { + d->globalTouch.focus.firstTouchPos = globalPosition; + } + if (id == 0 && d->globalTouch.focus.touchs.isEmpty()) { // Client did not bind touch, fall back to emulating with pointer events. forEachInterface(focusedTouchSurface(), d->pointers, @@ -1370,13 +1402,19 @@ } ); } + emit touchMoved(id, d->globalTouch.ids[id], globalPosition); } void SeatInterface::touchUp(qint32 id) { Q_D(); Q_ASSERT(d->globalTouch.ids.contains(id)); const qint32 serial = display()->nextSerial(); + if (d->drag.mode == Private::Drag::Mode::Touch && + d->drag.source->dragImplicitGrabSerial() == d->globalTouch.ids.value(id)) { + // the implicitly grabbing touch point has been upped + d->endDrag(serial); + } for (auto it = d->globalTouch.focus.touchs.constBegin(), end = d->globalTouch.focus.touchs.constEnd(); it != end; ++it) { (*it)->up(id, serial); } @@ -1404,6 +1442,16 @@ } } +bool SeatInterface::hasImplicitTouchGrab(quint32 serial) const +{ + Q_D(); + if (!d->globalTouch.focus.surface) { + // origin surface has been destroyed + return false; + } + return d->globalTouch.ids.key(serial, -1) != -1; +} + bool SeatInterface::isDrag() const { Q_D(); diff --git a/src/server/seat_interface_p.h b/src/server/seat_interface_p.h --- a/src/server/seat_interface_p.h +++ b/src/server/seat_interface_p.h @@ -24,6 +24,7 @@ #include "global_p.h" // Qt #include +#include #include #include // Wayland @@ -147,9 +148,10 @@ QVector touchs; QMetaObject::Connection destroyConnection; QPointF offset = QPointF(); + QPointF firstTouchPos; }; Focus focus; - QVector ids; + QMap ids; }; Touch globalTouch; @@ -164,6 +166,7 @@ DataDeviceInterface *target = nullptr; SurfaceInterface *surface = nullptr; PointerInterface *sourcePointer = nullptr; + TouchInterface *sourceTouch = nullptr; QMatrix4x4 transformation; QMetaObject::Connection destroyConnection; QMetaObject::Connection dragSourceDestroyConnection;