File: upstream_1a6871de_wayland-Fix-focused-surface-check-in-wl-data-device-start-drag.patch

package info (click to toggle)
kwin 4%3A6.3.6-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 56,600 kB
  • sloc: cpp: 241,670; xml: 3,228; javascript: 2,214; ansic: 775; sh: 67; python: 15; makefile: 8
file content (162 lines) | stat: -rw-r--r-- 8,172 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
From 1a6871de0d17c7f65da8b96a703326437d736c17 Mon Sep 17 00:00:00 2001
From: Vlad Zahorodnii <vlad.zahorodnii@kde.org>
Date: Thu, 29 May 2025 16:13:23 +0000
Subject: [PATCH] wayland: Fix focused surface check in
 wl_data_device.start_drag

SeatInterface::focusedPointerSurface() contains the main surface, but the
client can specify a subsurface.

BUG: 497031


(cherry picked from commit 40379821ee527ad7666de916f890852578c3a591)

Co-authored-by: Vlad Zahorodnii <vlad.zahorodnii@kde.org>
---
 autotests/wayland/client/test_drag_drop.cpp | 95 +++++++++++++++++++++
 src/wayland/datadevice.cpp                  |  2 +-
 src/wayland/seat.cpp                        |  1 -
 3 files changed, 96 insertions(+), 2 deletions(-)

diff --git a/autotests/wayland/client/test_drag_drop.cpp b/autotests/wayland/client/test_drag_drop.cpp
index 86e033b4251..d2ea5b97804 100644
--- a/autotests/wayland/client/test_drag_drop.cpp
+++ b/autotests/wayland/client/test_drag_drop.cpp
@@ -42,6 +42,7 @@ private Q_SLOTS:
     void cleanup();
 
     void testPointerDragAndDrop();
+    void testPointerSubsurfaceDragAndDrop();
     void testTouchDragAndDrop();
     void testTouchSubsurfacesDragAndDrop();
     void testDragAndDropWithCancelByDestroyDataSource();
@@ -293,6 +294,100 @@ void TestDragAndDrop::testPointerDragAndDrop()
     QVERIFY(pointerMotionSpy.isEmpty());
 }
 
+void TestDragAndDrop::testPointerSubsurfaceDragAndDrop()
+{
+    // this test verifies drag and drop from a subsurface works
+    using namespace KWin;
+
+    std::unique_ptr<KWayland::Client::Surface> parentSurface(createSurface());
+    parentSurface->setSize(QSize(100, 100));
+    auto parentServerSurface = getServerSurface();
+    QVERIFY(parentServerSurface);
+
+    std::unique_ptr<KWayland::Client::Surface> childSurface(createSurface());
+    childSurface->setSize(QSize(100, 100));
+    std::unique_ptr<KWayland::Client::SubSurface> subSurface(createSubSurface(childSurface.get(), parentSurface.get()));
+    QVERIFY(subSurface);
+    subSurface->setPosition({0, 0});
+    auto childServerSurface = getServerSurface();
+    QVERIFY(childServerSurface);
+
+    QSignalSpy dataSourceSelectedActionChangedSpy(m_dataSource, &KWayland::Client::DataSource::selectedDragAndDropActionChanged);
+    auto timestamp = 2ms;
+
+    // now we need to pass pointer focus to the Surface and simulate a button press
+    QSignalSpy buttonPressSpy(m_pointer, &KWayland::Client::Pointer::buttonStateChanged);
+    m_seatInterface->setTimestamp(timestamp++);
+    m_seatInterface->notifyPointerEnter(parentServerSurface, QPointF(0, 0));
+    m_seatInterface->notifyPointerButton(1, PointerButtonState::Pressed);
+    m_seatInterface->notifyPointerFrame();
+    QVERIFY(buttonPressSpy.wait());
+    QCOMPARE(buttonPressSpy.first().at(1).value<quint32>(), 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 pointerMotionSpy(m_pointer, &KWayland::Client::Pointer::motion);
+    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(buttonPressSpy.first().first().value<quint32>(), m_dataSource, childSurface.get());
+    QVERIFY(dragStartedSpy.wait());
+    QCOMPARE(m_seatInterface->dragSurface(), parentServerSurface);
+    QCOMPARE(m_seatInterface->dragSurfaceTransformation(), QMatrix4x4());
+    QVERIFY(!m_seatInterface->dragIcon());
+    QCOMPARE(SeatInterfacePrivate::get(m_seatInterface)->drag.dragImplicitGrabSerial, buttonPressSpy.first().first().value<quint32>());
+    QVERIFY(dragEnteredSpy.wait());
+    QCOMPARE(dragEnteredSpy.count(), 1);
+    QCOMPARE(dragEnteredSpy.first().first().value<quint32>(), m_display->serial());
+    QCOMPARE(dragEnteredSpy.first().last().toPointF(), QPointF(0, 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->notifyPointerMotion(QPointF(3, 3));
+    m_seatInterface->notifyPointerFrame();
+    QVERIFY(dragMotionSpy.wait());
+    QCOMPARE(dragMotionSpy.count(), 1);
+    QCOMPARE(dragMotionSpy.first().first().toPointF(), QPointF(3, 3));
+    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->notifyPointerButton(1, PointerButtonState::Released);
+    m_seatInterface->notifyPointerFrame();
+    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(pointerMotionSpy.isEmpty());
+}
+
 void TestDragAndDrop::testTouchSubsurfacesDragAndDrop()
 {
     // this test verifies the very basic drag and drop on one surface, an enter, a move and the drop
diff --git a/src/wayland/datadevice.cpp b/src/wayland/datadevice.cpp
index c28f4265c15..1becb8290df 100644
--- a/src/wayland/datadevice.cpp
+++ b/src/wayland/datadevice.cpp
@@ -84,7 +84,7 @@ void DataDeviceInterfacePrivate::data_device_start_drag(Resource *resource,
                                                         wl_resource *iconResource,
                                                         uint32_t serial)
 {
-    SurfaceInterface *focusSurface = SurfaceInterface::get(originResource);
+    SurfaceInterface *focusSurface = SurfaceInterface::get(originResource)->mainSurface();
     DataSourceInterface *dataSource = nullptr;
     if (sourceResource) {
         dataSource = DataSourceInterface::get(sourceResource);
diff --git a/src/wayland/seat.cpp b/src/wayland/seat.cpp
index ff828913528..5520255a8a1 100644
--- a/src/wayland/seat.cpp
+++ b/src/wayland/seat.cpp
@@ -1297,7 +1297,6 @@ 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;
-- 
GitLab