Index: src/server/datadevice_interface.h =================================================================== --- src/server/datadevice_interface.h +++ src/server/datadevice_interface.h @@ -82,6 +82,11 @@ * @since 5.6 **/ void updateDragTarget(SurfaceInterface *surface, quint32 serial); + /** + * Mark this DataDeviceInterface as being a proxy device for @p remote. + * @since 5.XX + **/ + void updateProxy(SurfaceInterface *remote); Q_SIGNALS: void dragStarted(); Index: src/server/datadevice_interface.cpp =================================================================== --- src/server/datadevice_interface.cpp +++ src/server/datadevice_interface.cpp @@ -61,6 +61,8 @@ }; Drag drag; + SurfaceInterface *proxyRemoteSurface = nullptr; + private: DataDeviceInterface *q_func() { return reinterpret_cast(q); @@ -99,10 +101,15 @@ void DataDeviceInterface::Private::startDrag(DataSourceInterface *dataSource, SurfaceInterface *origin, SurfaceInterface *i, quint32 serial) { - const bool pointerGrab = seat->hasImplicitPointerGrab(serial) && seat->focusedPointerSurface() == origin; + SurfaceInterface *focusSurface = origin; + if (proxyRemoteSurface) { + // origin is a proxy surface + focusSurface = proxyRemoteSurface; + } + 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() == origin; + const bool touchGrab = seat->hasImplicitTouchGrab(serial) && seat->focusedTouchSurface() == focusSurface; if (!touchGrab) { // Client neither has pointer nor touch grab. No drag start allowed. return; @@ -209,7 +216,7 @@ SurfaceInterface *DataDeviceInterface::origin() const { Q_D(); - return d->surface; + return d->proxyRemoteSurface ? d->proxyRemoteSurface : d->surface; } DataSourceInterface *DataDeviceInterface::selection() const @@ -292,6 +299,11 @@ } return; } + if (d->proxyRemoteSurface && d->proxyRemoteSurface == surface) { + // A proxy can not have the remote surface as target. + // TODO: do this for all client's surfaces? + return; + } auto *source = d->seat->dragSource()->dragSource(); DataOfferInterface *offer = d->createDataOffer(source); d->drag.surface = surface; @@ -371,6 +383,13 @@ return d->drag.serial; } +void DataDeviceInterface::updateProxy(SurfaceInterface *remote) +{ + Q_D(); + // TODO: connect destroy signal? + d->proxyRemoteSurface = remote; +} + DataDeviceInterface::Private *DataDeviceInterface::d_func() const { return reinterpret_cast(d.data()); Index: src/server/pointer_interface.cpp =================================================================== --- src/server/pointer_interface.cpp +++ src/server/pointer_interface.cpp @@ -27,6 +27,7 @@ #include "display.h" #include "subcompositor_interface.h" #include "surface_interface.h" +#include "datadevice_interface.h" // Wayland #include @@ -232,32 +233,37 @@ // TODO: handle touch connect(parent, &SeatInterface::pointerPosChanged, this, [this] { Q_D(); - if (d->seat->isDragPointer()) { - // handled by DataDevice + if (!d->focusedSurface || !d->resource) { return; } - if (d->focusedSurface && d->resource) { - if (!d->focusedSurface->lockedPointer().isNull() && d->focusedSurface->lockedPointer()->isLocked()) { + if (d->seat->isDragPointer()) { + const auto *originSurface = d->seat->dragSource()->origin(); + const bool proxyRemoteFocused = originSurface->dataProxy() && originSurface == d->focusedSurface; + if (!proxyRemoteFocused) { + // handled by DataDevice return; } - const QPointF pos = d->seat->focusedPointerSurfaceTransformation().map(d->seat->pointerPos()); - auto targetSurface = d->focusedSurface->inputSurfaceAt(pos); - if (!targetSurface) { - targetSurface = d->focusedSurface; - } - if (targetSurface != d->focusedChildSurface.data()) { - const quint32 serial = d->seat->display()->nextSerial(); - d->sendLeave(d->focusedChildSurface.data(), serial); - d->focusedChildSurface = QPointer(targetSurface); - d->sendEnter(targetSurface, pos, serial); - d->sendFrame(); - d->client->flush(); - } else { - const QPointF adjustedPos = pos - surfacePosition(d->focusedChildSurface); - wl_pointer_send_motion(d->resource, d->seat->timestamp(), - wl_fixed_from_double(adjustedPos.x()), wl_fixed_from_double(adjustedPos.y())); - d->sendFrame(); - } + } + if (!d->focusedSurface->lockedPointer().isNull() && d->focusedSurface->lockedPointer()->isLocked()) { + return; + } + const QPointF pos = d->seat->focusedPointerSurfaceTransformation().map(d->seat->pointerPos()); + auto targetSurface = d->focusedSurface->inputSurfaceAt(pos); + if (!targetSurface) { + targetSurface = d->focusedSurface; + } + if (targetSurface != d->focusedChildSurface.data()) { + const quint32 serial = d->seat->display()->nextSerial(); + d->sendLeave(d->focusedChildSurface.data(), serial); + d->focusedChildSurface = QPointer(targetSurface); + d->sendEnter(targetSurface, pos, serial); + d->sendFrame(); + d->client->flush(); + } else { + const QPointF adjustedPos = pos - surfacePosition(d->focusedChildSurface); + wl_pointer_send_motion(d->resource, d->seat->timestamp(), + wl_fixed_from_double(adjustedPos.x()), wl_fixed_from_double(adjustedPos.y())); + d->sendFrame(); } }); } Index: src/server/seat_interface.cpp =================================================================== --- src/server/seat_interface.cpp +++ src/server/seat_interface.cpp @@ -307,9 +307,17 @@ // no implicit grab, abort drag return; } + auto *originSurface = dataDevice->origin(); + const bool proxied = originSurface->dataProxy(); + if (!proxied) { + // origin surface + drag.target = dataDevice; + drag.surface = originSurface; + // TODO: transformation needs to be either pointer or touch + drag.transformation = globalPointer.focus.transformation; + } drag.source = dataDevice; - drag.target = dataDevice; - drag.surface = dragSurface; + drag.sourcePointer = interfaceForSurface(originSurface, pointers); drag.destroyConnection = QObject::connect(dataDevice, &QObject::destroyed, q, [this] { endDrag(display->nextSerial()); @@ -329,7 +337,7 @@ } else { drag.dragSourceDestroyConnection = QMetaObject::Connection(); } - dataDevice->updateDragTarget(dataDevice->origin(), dataDevice->dragImplicitGrabSerial()); + dataDevice->updateDragTarget(proxied ? nullptr : originSurface, dataDevice->dragImplicitGrabSerial()); emit q->dragStarted(); emit q->dragSurfaceChanged(); } @@ -871,11 +879,11 @@ // ignore return; } - if (d->globalPointer.focus.surface) { + if (auto *focusSurface = d->globalPointer.focus.surface) { for (auto it = d->globalPointer.focus.pointers.constBegin(), end = d->globalPointer.focus.pointers.constEnd(); it != end; ++it) { (*it)->buttonPressed(button, serial); } - if (d->globalPointer.focus.surface == d->keys.focus.surface) { + if (focusSurface == d->keys.focus.surface) { // update the focused child surface auto p = focusedPointer(); if (p) { Index: src/server/surface_interface.h =================================================================== --- src/server/surface_interface.h +++ src/server/surface_interface.h @@ -275,6 +275,22 @@ **/ static SurfaceInterface *get(quint32 id, const ClientConnection *client); + /** + * Set @p surface as a data proxy for this SurfaceInterface. This enables + * the proxy to conduct drags on the surface's client behalf. + * + * Setting a data proxy is only allowed when the client owning this surface + * has not created a data device itself. + * @since 5.XX + **/ + void setDataProxy(SurfaceInterface *surface); + /** + * Returns the data proxy of this SurfaceInterface or null if there + * is none set. + * @since 5.XX + **/ + SurfaceInterface* dataProxy() const; + Q_SIGNALS: /** * Emitted whenever the SurfaceInterface got damaged. Index: src/server/surface_interface.cpp =================================================================== --- src/server/surface_interface.cpp +++ src/server/surface_interface.cpp @@ -903,6 +903,18 @@ return !d->idleInhibitors.isEmpty(); } +void SurfaceInterface::setDataProxy(SurfaceInterface *surface) +{ + Q_D(); + d->dataProxy = surface; +} + +SurfaceInterface* SurfaceInterface::dataProxy() const +{ + Q_D(); + return d->dataProxy; +} + SurfaceInterface::Private *SurfaceInterface::d_func() const { return reinterpret_cast(d.data()); Index: src/server/surface_interface_p.h =================================================================== --- src/server/surface_interface_p.h +++ src/server/surface_interface_p.h @@ -103,6 +103,8 @@ QHash outputDestroyedConnections; QVector idleInhibitors; + SurfaceInterface *dataProxy = nullptr; + private: QMetaObject::Connection constrainsOneShotConnection; QMetaObject::Connection constrainsUnboundConnection;