[plasma/kwin] src/wayland: Support more than one wl_data_device

Vlad Zahorodnii <[email protected]>
Newsgroups gmane.comp.kde.cvs
Message-ID <[email protected]>
Git commit e07d87d69916defcdc78ee2cb559ff9363ab2b5f by Vlad Zahorodnii, on behalf of Martin Stransky.
Committed on 10/08/2026 at 10:38.
Pushed by vladz into branch 'master'.

Support more than one wl_data_device

KWin uses only first data_device and ignores any other.
This patch allows to create and use more wl_data_devices per wl_seat.

BUG: 521494

M  +37   -22   src/wayland/seat.cpp
M  +1    -1    src/wayland/seat_p.h

https://invent.kde.org/plasma/kwin/-/commit/e07d87d69916defcdc78ee2cb559ff9363ab2b5f

diff --git a/src/wayland/seat.cpp b/src/wayland/seat.cpp
index 095ca86c24e..549627cd73e 100644
--- a/src/wayland/seat.cpp
+++ b/src/wayland/seat.cpp
@@ -163,6 +163,7 @@ void SeatInterfacePrivate::registerDataDevice(DataDeviceInterface *dataDevice)
     auto dataDeviceCleanup = [this, dataDevice] {
         dataDevices.removeOne(dataDevice);
         globalDataDevice.selections.removeOne(dataDevice);
+        drag.targets.removeOne(dataDevice);
     };
     QObject::connect(dataDevice, &QObject::destroyed, q, dataDeviceCleanup);
     QObject::connect(dataDevice, &DataDeviceInterface::selectionChanged, q, [this](DataSourceInterface *source, quint32 serial) {
@@ -195,13 +196,16 @@ void SeatInterface::endDrag()
 
     QObject::disconnect(d->drag.dragSourceDestroyConnection);
 
-    AbstractDropHandler *dragTargetDevice = d->drag.target.data();
+    const auto dragTargets = d->drag.targets;
     AbstractDataSource *dragSource = d->drag.source;
+    const bool hasTarget = !dragTargets.isEmpty();
 
     if (dragSource) {
-        if (dragTargetDevice && dragSource->isAccepted() && dragSource->selectedDndAction() != DnDAction::None) {
+        if (hasTarget && dragSource->isAccepted() && dragSource->selectedDndAction() != DnDAction::None) {
             Q_EMIT dragDropped();
-            dragTargetDevice->drop();
+            for (const auto &target : dragTargets) {
+                target->drop();
+            }
             dragSource->dropPerformed();
         } else {
             dragSource->dropPerformed();
@@ -209,8 +213,8 @@ void SeatInterface::endDrag()
         }
     }
 
-    if (dragTargetDevice) {
-        dragTargetDevice->updateDragTarget(nullptr, QPointF(), 0);
+    for (const auto &target : dragTargets) {
+        target->updateDragTarget(nullptr, QPointF(), 0);
     }
 
     d->drag = {};
@@ -227,9 +231,8 @@ void SeatInterface::cancelDrag()
     if (d->drag.source) {
         d->drag.source->dndCancelled();
     }
-    if (d->drag.target) {
-        d->drag.target->updateDragTarget(nullptr, QPointF(), 0);
-        d->drag.target = nullptr;
+    for (const auto &target : std::as_const(d->drag.targets)) {
+        target->updateDragTarget(nullptr, QPointF(), 0);
     }
     d->drag = {};
     Q_EMIT dragEnded();
@@ -472,22 +475,30 @@ void SeatInterface::setDragTarget(AbstractDropHandler *dropTarget,
         return;
     }
     const quint32 serial = d->display->nextSerial();
-    if (d->drag.target) {
-        d->drag.target->updateDragTarget(nullptr, QPointF(), serial);
+    for (const auto &target : std::as_const(d->drag.targets)) {
+        target->updateDragTarget(nullptr, QPointF(), serial);
     }
+    d->drag.targets.clear();
 
-    // TODO: technically we can have multiple data devices
-    // and we should send the drag to all of them, but that seems overly complicated
-    // in practice so far the only case for multiple data devices is for clipboard overriding
-    d->drag.target = dropTarget;
+    if (surface) {
+        const auto dataDevices = d->dataDevicesForSurface(surface);
+        for (auto device : dataDevices) {
+            d->drag.targets.append(device);
+        }
+        if (d->drag.targets.isEmpty() && dropTarget) {
+            d->drag.targets.append(dropTarget);
+        }
+    }
 
-    if (d->drag.target) {
+    if (!d->drag.targets.isEmpty()) {
         d->drag.surface = surface;
         d->drag.transformation = inputTransformation;
         if (d->dragInhibitsPointer(d->globalPointer.focus.surface)) {
             notifyPointerLeave();
         }
-        d->drag.target->updateDragTarget(surface, globalPosition, serial);
+        for (const auto &target : std::as_const(d->drag.targets)) {
+            target->updateDragTarget(surface, globalPosition, serial);
+        }
     } else {
         d->drag.surface = nullptr;
     }
@@ -501,8 +512,8 @@ QPointF SeatInterface::dragPosition() const
 void SeatInterface::notifyDragMotion(const QPointF &position)
 {
     d->drag.position = position;
-    if (d->drag.target) {
-        d->drag.target->motion(position);
+    for (const auto &target : std::as_const(d->drag.targets)) {
+        target->motion(position);
     }
     Q_EMIT dragMoved(position);
 }
@@ -1293,14 +1304,18 @@ bool SeatInterfacePrivate::startDrag(Drag::Mode mode, AbstractDataSource *dragSo
     }
     drag.dragIcon = dragIcon;
 
-    if (!dataDevicesForSurface(originSurface).isEmpty()) {
-        drag.target = dataDevicesForSurface(originSurface)[0];
+    const auto devices = dataDevicesForSurface(originSurface);
+    for (auto device : devices) {
+        drag.targets.append(device);
     }
-    if (drag.target) {
+    if (!drag.targets.isEmpty()) {
         if (dragInhibitsPointer(originSurface)) {
             q->notifyPointerLeave();
         }
-        drag.target->updateDragTarget(originSurface, drag.position, display->nextSerial());
+        const quint32 serial = display->nextSerial();
+        for (const auto &target : std::as_const(drag.targets)) {
+            target->updateDragTarget(originSurface, drag.position, serial);
+        }
     }
     Q_EMIT q->dragStarted();
     return true;
diff --git a/src/wayland/seat_p.h b/src/wayland/seat_p.h
index ae664f31f13..6c4ee1bee58 100644
--- a/src/wayland/seat_p.h
+++ b/src/wayland/seat_p.h
@@ -135,7 +135,7 @@ public:
         Mode mode = Mode::None;
         AbstractDataSource *source = nullptr;
         QPointer<SurfaceInterface> surface;
-        QPointer<AbstractDropHandler> target;
+        QList<AbstractDropHandler *> targets;
         QPointer<DragAndDropIcon> dragIcon;
         QPointF position;
         QMatrix4x4 transformation;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.