KWin
Loading...
Searching...
No Matches
test_drag_drop.cpp
Go to the documentation of this file.
1/*
2 SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5*/
6// Qt
7#include <QMimeType>
8#include <QSignalSpy>
9#include <QTest>
10// KWin
11#include "wayland/compositor.h"
13#include "wayland/datasource.h"
14#include "wayland/display.h"
15#include "wayland/seat.h"
16#include "wayland/seat_p.h"
17
18#include "KWayland/Client/compositor.h"
19#include "KWayland/Client/connection_thread.h"
20#include "KWayland/Client/datadevice.h"
21#include "KWayland/Client/datadevicemanager.h"
22#include "KWayland/Client/datasource.h"
23#include "KWayland/Client/event_queue.h"
24#include "KWayland/Client/pointer.h"
25#include "KWayland/Client/registry.h"
26#include "KWayland/Client/seat.h"
27#include "KWayland/Client/shm_pool.h"
28#include "KWayland/Client/surface.h"
29#include "KWayland/Client/touch.h"
30
31using namespace std::literals;
32
33class TestDragAndDrop : public QObject
34{
35 Q_OBJECT
36
37private Q_SLOTS:
38 void init();
39 void cleanup();
40
41 void testPointerDragAndDrop();
42 void testTouchDragAndDrop();
43 void testDragAndDropWithCancelByDestroyDataSource();
44 void testPointerEventsIgnored();
45
46private:
47 KWayland::Client::Surface *createSurface();
48 KWin::SurfaceInterface *getServerSurface();
49
50 KWin::Display *m_display = nullptr;
51 KWin::CompositorInterface *m_compositorInterface = nullptr;
52 KWin::DataDeviceManagerInterface *m_dataDeviceManagerInterface = nullptr;
53 KWin::SeatInterface *m_seatInterface = nullptr;
54 KWayland::Client::ConnectionThread *m_connection = nullptr;
55 KWayland::Client::Compositor *m_compositor = nullptr;
56 KWayland::Client::EventQueue *m_queue = nullptr;
57 KWayland::Client::DataDevice *m_dataDevice = nullptr;
58 KWayland::Client::DataSource *m_dataSource = nullptr;
59 QThread *m_thread = nullptr;
60 KWayland::Client::Registry *m_registry = nullptr;
61 KWayland::Client::Seat *m_seat = nullptr;
62 KWayland::Client::Pointer *m_pointer = nullptr;
63 KWayland::Client::Touch *m_touch = nullptr;
64 KWayland::Client::DataDeviceManager *m_ddm = nullptr;
65 KWayland::Client::ShmPool *m_shm = nullptr;
66};
67
68static const QString s_socketName = QStringLiteral("kwayland-test-wayland-drag-n-drop-0");
69
70void TestDragAndDrop::init()
71{
72 using namespace KWin;
73 delete m_display;
74 m_display = new KWin::Display(this);
75 m_display->addSocketName(s_socketName);
76 m_display->start();
77 QVERIFY(m_display->isRunning());
78
79 // setup connection
80 m_connection = new KWayland::Client::ConnectionThread;
81 QSignalSpy connectedSpy(m_connection, &KWayland::Client::ConnectionThread::connected);
82 m_connection->setSocketName(s_socketName);
83
84 m_compositorInterface = new CompositorInterface(m_display, m_display);
85 m_seatInterface = new SeatInterface(m_display, m_display);
86 m_seatInterface->setHasPointer(true);
87 m_seatInterface->setHasTouch(true);
88 m_dataDeviceManagerInterface = new DataDeviceManagerInterface(m_display, m_display);
89 m_display->createShm();
90
91 m_thread = new QThread(this);
92 m_connection->moveToThread(m_thread);
93 m_thread->start();
94
95 m_connection->initConnection();
96 QVERIFY(connectedSpy.wait());
97
98 m_queue = new KWayland::Client::EventQueue(this);
99 QVERIFY(!m_queue->isValid());
100 m_queue->setup(m_connection);
101 QVERIFY(m_queue->isValid());
102
103 m_registry = new KWayland::Client::Registry();
104 QSignalSpy interfacesAnnouncedSpy(m_registry, &KWayland::Client::Registry::interfaceAnnounced);
105
106 QVERIFY(!m_registry->eventQueue());
107 m_registry->setEventQueue(m_queue);
108 QCOMPARE(m_registry->eventQueue(), m_queue);
109 m_registry->create(m_connection);
110 QVERIFY(m_registry->isValid());
111 m_registry->setup();
112
113 QVERIFY(interfacesAnnouncedSpy.wait());
114#define CREATE(variable, factory, iface) \
115 variable = \
116 m_registry->create##factory(m_registry->interface(KWayland::Client::Registry::Interface::iface).name, m_registry->interface(KWayland::Client::Registry::Interface::iface).version, this); \
117 QVERIFY(variable);
118
119 CREATE(m_compositor, Compositor, Compositor)
120 CREATE(m_seat, Seat, Seat)
121 CREATE(m_ddm, DataDeviceManager, DataDeviceManager)
122 CREATE(m_shm, ShmPool, Shm)
123
124#undef CREATE
125
126 QSignalSpy pointerSpy(m_seat, &KWayland::Client::Seat::hasPointerChanged);
127 QVERIFY(pointerSpy.wait());
128 m_pointer = m_seat->createPointer(m_seat);
129 QVERIFY(m_pointer->isValid());
130 m_touch = m_seat->createTouch(m_seat);
131 QVERIFY(m_touch->isValid());
132 m_dataDevice = m_ddm->getDataDevice(m_seat, this);
133 QVERIFY(m_dataDevice->isValid());
134 m_dataSource = m_ddm->createDataSource(this);
135 QVERIFY(m_dataSource->isValid());
136 m_dataSource->offer(QStringLiteral("text/plain"));
137}
138
139void TestDragAndDrop::cleanup()
140{
141#define DELETE(name) \
142 if (name) { \
143 delete name; \
144 name = nullptr; \
145 }
146 DELETE(m_dataSource)
147 DELETE(m_dataDevice)
148 DELETE(m_shm)
149 DELETE(m_compositor)
150 DELETE(m_ddm)
151 DELETE(m_seat)
152 DELETE(m_queue)
153 DELETE(m_registry)
154#undef DELETE
155 if (m_thread) {
156 m_thread->quit();
157 m_thread->wait();
158 delete m_thread;
159 m_thread = nullptr;
160 }
161 delete m_connection;
162 m_connection = nullptr;
163
164 delete m_display;
165 m_display = nullptr;
166}
167
168KWayland::Client::Surface *TestDragAndDrop::createSurface()
169{
170 auto s = m_compositor->createSurface();
171
172 QImage img(QSize(100, 200), QImage::Format_RGB32);
173 img.fill(Qt::red);
174 s->attachBuffer(m_shm->createBuffer(img));
175 s->damage(QRect(0, 0, 100, 200));
176 s->commit(KWayland::Client::Surface::CommitFlag::None);
177 return s;
178}
179
180KWin::SurfaceInterface *TestDragAndDrop::getServerSurface()
181{
182 using namespace KWin;
183 QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
184 if (!surfaceCreatedSpy.isValid()) {
185 return nullptr;
186 }
187 if (!surfaceCreatedSpy.wait(500)) {
188 return nullptr;
189 }
190 return surfaceCreatedSpy.first().first().value<SurfaceInterface *>();
191}
192
193void TestDragAndDrop::testPointerDragAndDrop()
194{
195 // this test verifies the very basic drag and drop on one surface, an enter, a move and the drop
196 using namespace KWin;
197 // first create a window
198 std::unique_ptr<KWayland::Client::Surface> s(createSurface());
199 auto serverSurface = getServerSurface();
200 QVERIFY(serverSurface);
201
202 QSignalSpy dataSourceSelectedActionChangedSpy(m_dataSource, &KWayland::Client::DataSource::selectedDragAndDropActionChanged);
203
204 auto timestamp = 2ms;
205
206 // now we need to pass pointer focus to the Surface and simulate a button press
207 QSignalSpy buttonPressSpy(m_pointer, &KWayland::Client::Pointer::buttonStateChanged);
208 m_seatInterface->setTimestamp(timestamp++);
209 m_seatInterface->notifyPointerEnter(serverSurface, QPointF(0, 0));
210 m_seatInterface->notifyPointerButton(1, PointerButtonState::Pressed);
211 m_seatInterface->notifyPointerFrame();
212 QVERIFY(buttonPressSpy.wait());
213 QCOMPARE(buttonPressSpy.first().at(1).value<quint32>(), quint32(2));
214
215 // add some signal spies for client side
216 QSignalSpy dragEnteredSpy(m_dataDevice, &KWayland::Client::DataDevice::dragEntered);
217 QSignalSpy dragMotionSpy(m_dataDevice, &KWayland::Client::DataDevice::dragMotion);
218 QSignalSpy pointerMotionSpy(m_pointer, &KWayland::Client::Pointer::motion);
219 QSignalSpy sourceDropSpy(m_dataSource, &KWayland::Client::DataSource::dragAndDropPerformed);
220
221 // now we can start the drag and drop
222 QSignalSpy dragStartedSpy(m_seatInterface, &SeatInterface::dragStarted);
223 m_dataSource->setDragAndDropActions(KWayland::Client::DataDeviceManager::DnDAction::Copy | KWayland::Client::DataDeviceManager::DnDAction::Move);
224 m_dataDevice->startDrag(buttonPressSpy.first().first().value<quint32>(), m_dataSource, s.get());
225 QVERIFY(dragStartedSpy.wait());
226 QCOMPARE(m_seatInterface->dragSurface(), serverSurface);
227 QCOMPARE(m_seatInterface->dragSurfaceTransformation(), QMatrix4x4());
228 QVERIFY(!m_seatInterface->dragIcon());
229 QCOMPARE(SeatInterfacePrivate::get(m_seatInterface)->drag.dragImplicitGrabSerial, buttonPressSpy.first().first().value<quint32>());
230 QVERIFY(dragEnteredSpy.wait());
231 QCOMPARE(dragEnteredSpy.count(), 1);
232 QCOMPARE(dragEnteredSpy.first().first().value<quint32>(), m_display->serial());
233 QCOMPARE(dragEnteredSpy.first().last().toPointF(), QPointF(0, 0));
234 QCOMPARE(m_dataDevice->dragSurface().data(), s.get());
235 auto offer = m_dataDevice->dragOffer();
236 QVERIFY(offer);
237 QCOMPARE(offer->selectedDragAndDropAction(), KWayland::Client::DataDeviceManager::DnDAction::None);
238 QSignalSpy offerActionChangedSpy(offer, &KWayland::Client::DataOffer::selectedDragAndDropActionChanged);
239 QCOMPARE(m_dataDevice->dragOffer()->offeredMimeTypes().count(), 1);
240 QCOMPARE(m_dataDevice->dragOffer()->offeredMimeTypes().first().name(), QStringLiteral("text/plain"));
241 QTRY_COMPARE(offer->sourceDragAndDropActions(), KWayland::Client::DataDeviceManager::DnDAction::Copy | KWayland::Client::DataDeviceManager::DnDAction::Move);
242 offer->accept(QStringLiteral("text/plain"), dragEnteredSpy.last().at(0).toUInt());
243 offer->setDragAndDropActions(KWayland::Client::DataDeviceManager::DnDAction::Copy | KWayland::Client::DataDeviceManager::DnDAction::Move, KWayland::Client::DataDeviceManager::DnDAction::Move);
244 QVERIFY(offerActionChangedSpy.wait());
245 QCOMPARE(offerActionChangedSpy.count(), 1);
246 QCOMPARE(offer->selectedDragAndDropAction(), KWayland::Client::DataDeviceManager::DnDAction::Move);
247 QCOMPARE(dataSourceSelectedActionChangedSpy.count(), 1);
248 QCOMPARE(m_dataSource->selectedDragAndDropAction(), KWayland::Client::DataDeviceManager::DnDAction::Move);
249
250 // simulate motion
251 m_seatInterface->setTimestamp(timestamp++);
252 m_seatInterface->notifyPointerMotion(QPointF(3, 3));
253 m_seatInterface->notifyPointerFrame();
254 QVERIFY(dragMotionSpy.wait());
255 QCOMPARE(dragMotionSpy.count(), 1);
256 QCOMPARE(dragMotionSpy.first().first().toPointF(), QPointF(3, 3));
257 QCOMPARE(dragMotionSpy.first().last().toUInt(), 3u);
258
259 // simulate drop
260 QSignalSpy serverDragEndedSpy(m_seatInterface, &SeatInterface::dragEnded);
261 QSignalSpy droppedSpy(m_dataDevice, &KWayland::Client::DataDevice::dropped);
262 m_seatInterface->setTimestamp(timestamp++);
263 m_seatInterface->notifyPointerButton(1, PointerButtonState::Released);
264 m_seatInterface->notifyPointerFrame();
265 QVERIFY(sourceDropSpy.isEmpty());
266 QVERIFY(droppedSpy.wait());
267 QCOMPARE(sourceDropSpy.count(), 1);
268 QCOMPARE(serverDragEndedSpy.count(), 1);
269
270 QSignalSpy finishedSpy(m_dataSource, &KWayland::Client::DataSource::dragAndDropFinished);
271 offer->dragAndDropFinished();
272 QVERIFY(finishedSpy.wait());
273 delete offer;
274
275 // verify that we did not get any further input events
276 QVERIFY(pointerMotionSpy.isEmpty());
277}
278
279void TestDragAndDrop::testTouchDragAndDrop()
280{
281 // this test verifies the very basic drag and drop on one surface, an enter, a move and the drop
282 using namespace KWin;
283 // first create a window
284 std::unique_ptr<KWayland::Client::Surface> s(createSurface());
285 s->setSize(QSize(100, 100));
286 auto serverSurface = getServerSurface();
287 QVERIFY(serverSurface);
288
289 QSignalSpy dataSourceSelectedActionChangedSpy(m_dataSource, &KWayland::Client::DataSource::selectedDragAndDropActionChanged);
290
291 auto timestamp = 2ms;
292
293 // now we need to pass touch focus to the Surface and simulate a touch down
294 QSignalSpy sequenceStartedSpy(m_touch, &KWayland::Client::Touch::sequenceStarted);
295 QSignalSpy pointAddedSpy(m_touch, &KWayland::Client::Touch::pointAdded);
296 m_seatInterface->setFocusedTouchSurface(serverSurface);
297 m_seatInterface->setTimestamp(timestamp++);
298 const qint32 touchId = 0;
299 m_seatInterface->notifyTouchDown(touchId, QPointF(50, 50));
300 QVERIFY(sequenceStartedSpy.wait());
301
302 std::unique_ptr<KWayland::Client::TouchPoint> tp(sequenceStartedSpy.first().at(0).value<KWayland::Client::TouchPoint *>());
303 QVERIFY(tp != nullptr);
304 QCOMPARE(tp->time(), quint32(2));
305
306 // add some signal spies for client side
307 QSignalSpy dragEnteredSpy(m_dataDevice, &KWayland::Client::DataDevice::dragEntered);
308 QSignalSpy dragMotionSpy(m_dataDevice, &KWayland::Client::DataDevice::dragMotion);
309 QSignalSpy touchMotionSpy(m_touch, &KWayland::Client::Touch::pointMoved);
310 QSignalSpy sourceDropSpy(m_dataSource, &KWayland::Client::DataSource::dragAndDropPerformed);
311
312 // now we can start the drag and drop
313 QSignalSpy dragStartedSpy(m_seatInterface, &SeatInterface::dragStarted);
314 m_dataSource->setDragAndDropActions(KWayland::Client::DataDeviceManager::DnDAction::Copy | KWayland::Client::DataDeviceManager::DnDAction::Move);
315 m_dataDevice->startDrag(tp->downSerial(), m_dataSource, s.get());
316 QVERIFY(dragStartedSpy.wait());
317 QCOMPARE(m_seatInterface->dragSurface(), serverSurface);
318 QCOMPARE(m_seatInterface->dragSurfaceTransformation(), QMatrix4x4());
319 QVERIFY(!m_seatInterface->dragIcon());
320 QCOMPARE(SeatInterfacePrivate::get(m_seatInterface)->drag.dragImplicitGrabSerial, tp->downSerial());
321 QVERIFY(dragEnteredSpy.wait());
322 QCOMPARE(dragEnteredSpy.count(), 1);
323 QCOMPARE(dragEnteredSpy.first().first().value<quint32>(), m_display->serial());
324 QCOMPARE(dragEnteredSpy.first().last().toPointF(), QPointF(50.0, 50.0));
325 QCOMPARE(m_dataDevice->dragSurface().data(), s.get());
326 auto offer = m_dataDevice->dragOffer();
327 QVERIFY(offer);
328 QCOMPARE(offer->selectedDragAndDropAction(), KWayland::Client::DataDeviceManager::DnDAction::None);
329 QSignalSpy offerActionChangedSpy(offer, &KWayland::Client::DataOffer::selectedDragAndDropActionChanged);
330 QCOMPARE(m_dataDevice->dragOffer()->offeredMimeTypes().count(), 1);
331 QCOMPARE(m_dataDevice->dragOffer()->offeredMimeTypes().first().name(), QStringLiteral("text/plain"));
332 QTRY_COMPARE(offer->sourceDragAndDropActions(), KWayland::Client::DataDeviceManager::DnDAction::Copy | KWayland::Client::DataDeviceManager::DnDAction::Move);
333 offer->accept(QStringLiteral("text/plain"), dragEnteredSpy.last().at(0).toUInt());
334 offer->setDragAndDropActions(KWayland::Client::DataDeviceManager::DnDAction::Copy | KWayland::Client::DataDeviceManager::DnDAction::Move, KWayland::Client::DataDeviceManager::DnDAction::Move);
335 QVERIFY(offerActionChangedSpy.wait());
336 QCOMPARE(offerActionChangedSpy.count(), 1);
337 QCOMPARE(offer->selectedDragAndDropAction(), KWayland::Client::DataDeviceManager::DnDAction::Move);
338 QCOMPARE(dataSourceSelectedActionChangedSpy.count(), 1);
339 QCOMPARE(m_dataSource->selectedDragAndDropAction(), KWayland::Client::DataDeviceManager::DnDAction::Move);
340
341 // simulate motion
342 m_seatInterface->setTimestamp(timestamp++);
343 m_seatInterface->notifyTouchMotion(touchId, QPointF(75, 75));
344 QVERIFY(dragMotionSpy.wait());
345 QCOMPARE(dragMotionSpy.count(), 1);
346 QCOMPARE(dragMotionSpy.first().first().toPointF(), QPointF(75, 75));
347 QCOMPARE(dragMotionSpy.first().last().toUInt(), 3u);
348
349 // simulate drop
350 QSignalSpy serverDragEndedSpy(m_seatInterface, &SeatInterface::dragEnded);
351 QSignalSpy droppedSpy(m_dataDevice, &KWayland::Client::DataDevice::dropped);
352 m_seatInterface->setTimestamp(timestamp++);
353 m_seatInterface->notifyTouchUp(touchId);
354 QVERIFY(sourceDropSpy.isEmpty());
355 QVERIFY(droppedSpy.wait());
356 QCOMPARE(sourceDropSpy.count(), 1);
357 QCOMPARE(serverDragEndedSpy.count(), 1);
358
359 QSignalSpy finishedSpy(m_dataSource, &KWayland::Client::DataSource::dragAndDropFinished);
360 offer->dragAndDropFinished();
361 QVERIFY(finishedSpy.wait());
362 delete offer;
363
364 // verify that we did not get any further input events
365 QVERIFY(touchMotionSpy.isEmpty());
366 QCOMPARE(pointAddedSpy.count(), 0);
367}
368
369void TestDragAndDrop::testDragAndDropWithCancelByDestroyDataSource()
370{
371 // this test simulates the problem from BUG 389221
372 using namespace KWin;
373 // first create a window
374 std::unique_ptr<KWayland::Client::Surface> s(createSurface());
375 auto serverSurface = getServerSurface();
376 QVERIFY(serverSurface);
377
378 QSignalSpy dataSourceSelectedActionChangedSpy(m_dataSource, &KWayland::Client::DataSource::selectedDragAndDropActionChanged);
379
380 auto timestamp = 2ms;
381
382 // now we need to pass pointer focus to the Surface and simulate a button press
383 QSignalSpy buttonPressSpy(m_pointer, &KWayland::Client::Pointer::buttonStateChanged);
384 m_seatInterface->setTimestamp(timestamp++);
385 m_seatInterface->notifyPointerEnter(serverSurface, QPointF(0, 0));
386 m_seatInterface->notifyPointerButton(1, PointerButtonState::Pressed);
387 m_seatInterface->notifyPointerFrame();
388 QVERIFY(buttonPressSpy.wait());
389 QCOMPARE(buttonPressSpy.first().at(1).value<quint32>(), quint32(2));
390
391 QSignalSpy pointerLeftSpy(m_pointer, &KWayland::Client::Pointer::left);
392
393 // add some signal spies for client side
394 QSignalSpy dragEnteredSpy(m_dataDevice, &KWayland::Client::DataDevice::dragEntered);
395 QSignalSpy dragMotionSpy(m_dataDevice, &KWayland::Client::DataDevice::dragMotion);
396 QSignalSpy pointerMotionSpy(m_pointer, &KWayland::Client::Pointer::motion);
397 QSignalSpy dragLeftSpy(m_dataDevice, &KWayland::Client::DataDevice::dragLeft);
398
399 // now we can start the drag and drop
400 QSignalSpy dragStartedSpy(m_seatInterface, &SeatInterface::dragStarted);
401 m_dataSource->setDragAndDropActions(KWayland::Client::DataDeviceManager::DnDAction::Copy | KWayland::Client::DataDeviceManager::DnDAction::Move);
402 m_dataDevice->startDrag(buttonPressSpy.first().first().value<quint32>(), m_dataSource, s.get());
403 QVERIFY(dragStartedSpy.wait());
404
405 QCOMPARE(m_seatInterface->dragSurface(), serverSurface);
406 QCOMPARE(m_seatInterface->dragSurfaceTransformation(), QMatrix4x4());
407 QVERIFY(!m_seatInterface->dragIcon());
408 QCOMPARE(SeatInterfacePrivate::get(m_seatInterface)->drag.dragImplicitGrabSerial, buttonPressSpy.first().first().value<quint32>());
409 QVERIFY(pointerLeftSpy.wait());
410 QVERIFY(dragEnteredSpy.count() || dragEnteredSpy.wait());
411 QCOMPARE(dragEnteredSpy.count(), 1);
412 QCOMPARE(dragEnteredSpy.first().first().value<quint32>(), m_display->serial());
413 QCOMPARE(dragEnteredSpy.first().last().toPointF(), QPointF(0, 0));
414 QCOMPARE(m_dataDevice->dragSurface().data(), s.get());
415 auto offer = m_dataDevice->dragOffer();
416 QVERIFY(offer);
417 QCOMPARE(offer->selectedDragAndDropAction(), KWayland::Client::DataDeviceManager::DnDAction::None);
418 QSignalSpy offerActionChangedSpy(offer, &KWayland::Client::DataOffer::selectedDragAndDropActionChanged);
419 QCOMPARE(m_dataDevice->dragOffer()->offeredMimeTypes().count(), 1);
420 QCOMPARE(m_dataDevice->dragOffer()->offeredMimeTypes().first().name(), QStringLiteral("text/plain"));
421 QTRY_COMPARE(offer->sourceDragAndDropActions(), KWayland::Client::DataDeviceManager::DnDAction::Copy | KWayland::Client::DataDeviceManager::DnDAction::Move);
422 offer->accept(QStringLiteral("text/plain"), dragEnteredSpy.last().at(0).toUInt());
423 offer->setDragAndDropActions(KWayland::Client::DataDeviceManager::DnDAction::Copy | KWayland::Client::DataDeviceManager::DnDAction::Move, KWayland::Client::DataDeviceManager::DnDAction::Move);
424 QVERIFY(offerActionChangedSpy.wait());
425 QCOMPARE(offerActionChangedSpy.count(), 1);
426 QCOMPARE(offer->selectedDragAndDropAction(), KWayland::Client::DataDeviceManager::DnDAction::Move);
427 QCOMPARE(dataSourceSelectedActionChangedSpy.count(), 1);
428 QCOMPARE(m_dataSource->selectedDragAndDropAction(), KWayland::Client::DataDeviceManager::DnDAction::Move);
429
430 // simulate motion
431 m_seatInterface->setTimestamp(timestamp++);
432 m_seatInterface->notifyPointerMotion(QPointF(3, 3));
433 m_seatInterface->notifyPointerFrame();
434 QVERIFY(dragMotionSpy.wait());
435 QCOMPARE(dragMotionSpy.count(), 1);
436 QCOMPARE(dragMotionSpy.first().first().toPointF(), QPointF(3, 3));
437 QCOMPARE(dragMotionSpy.first().last().toUInt(), 3u);
438
439 // now delete the DataSource
440 delete m_dataSource;
441 m_dataSource = nullptr;
442 QSignalSpy serverDragEndedSpy(m_seatInterface, &SeatInterface::dragEnded);
443 QVERIFY(dragLeftSpy.isEmpty());
444 QVERIFY(dragLeftSpy.wait());
445 QTRY_COMPARE(dragLeftSpy.count(), 1);
446 QTRY_COMPARE(serverDragEndedSpy.count(), 1);
447
448 // simulate drop
449 QSignalSpy droppedSpy(m_dataDevice, &KWayland::Client::DataDevice::dropped);
450 m_seatInterface->setTimestamp(timestamp++);
451 m_seatInterface->notifyPointerButton(1, PointerButtonState::Released);
452 m_seatInterface->notifyPointerFrame();
453 QVERIFY(!droppedSpy.wait(500));
454
455 // verify that we did not get any further input events
456 QVERIFY(pointerMotionSpy.isEmpty());
457}
458
459void TestDragAndDrop::testPointerEventsIgnored()
460{
461 // this test verifies that all pointer events are ignored on the focused Pointer device during drag
462 using namespace KWin;
463 // first create a window
464 std::unique_ptr<KWayland::Client::Surface> s(createSurface());
465 auto serverSurface = getServerSurface();
466 QVERIFY(serverSurface);
467
468 // pass it pointer focus
469 m_seatInterface->notifyPointerEnter(serverSurface, QPointF(0, 0));
470
471 // create signal spies for all the pointer events
472 QSignalSpy pointerEnteredSpy(m_pointer, &KWayland::Client::Pointer::entered);
473 QSignalSpy pointerLeftSpy(m_pointer, &KWayland::Client::Pointer::left);
474 QSignalSpy pointerMotionSpy(m_pointer, &KWayland::Client::Pointer::motion);
475 QSignalSpy axisSpy(m_pointer, &KWayland::Client::Pointer::axisChanged);
476 QSignalSpy buttonSpy(m_pointer, &KWayland::Client::Pointer::buttonStateChanged);
477 QSignalSpy dragEnteredSpy(m_dataDevice, &KWayland::Client::DataDevice::dragEntered);
478
479 // first simulate a few things
480 auto timestamp = 1ms;
481 m_seatInterface->setTimestamp(timestamp++);
482 m_seatInterface->notifyPointerMotion(QPointF(10, 10));
483 m_seatInterface->setTimestamp(timestamp++);
484 m_seatInterface->notifyPointerAxis(Qt::Vertical, 5, 120, PointerAxisSource::Wheel);
485 m_seatInterface->notifyPointerFrame();
486 // verify that we have those
487 QVERIFY(axisSpy.wait());
488 QCOMPARE(axisSpy.count(), 1);
489 QCOMPARE(pointerMotionSpy.count(), 1);
490 QCOMPARE(pointerEnteredSpy.count(), 1);
491 QVERIFY(buttonSpy.isEmpty());
492 QVERIFY(pointerLeftSpy.isEmpty());
493
494 // let's start the drag
495 m_seatInterface->setTimestamp(timestamp++);
496 m_seatInterface->notifyPointerButton(1, PointerButtonState::Pressed);
497 m_seatInterface->notifyPointerFrame();
498 QVERIFY(buttonSpy.wait());
499 QCOMPARE(buttonSpy.count(), 1);
500 m_dataDevice->startDrag(buttonSpy.first().first().value<quint32>(), m_dataSource, s.get());
501 QVERIFY(dragEnteredSpy.wait());
502
503 // now simulate all the possible pointer interactions
504 m_seatInterface->setTimestamp(timestamp++);
505 m_seatInterface->notifyPointerButton(2, PointerButtonState::Pressed);
506 m_seatInterface->notifyPointerFrame();
507 m_seatInterface->setTimestamp(timestamp++);
508 m_seatInterface->notifyPointerButton(2, PointerButtonState::Released);
509 m_seatInterface->notifyPointerFrame();
510 m_seatInterface->setTimestamp(timestamp++);
511 m_seatInterface->notifyPointerAxis(Qt::Vertical, 5, 1, PointerAxisSource::Wheel);
512 m_seatInterface->notifyPointerFrame();
513 m_seatInterface->setTimestamp(timestamp++);
514 m_seatInterface->notifyPointerAxis(Qt::Vertical, 5, 1, PointerAxisSource::Wheel);
515 m_seatInterface->notifyPointerFrame();
516 m_seatInterface->setTimestamp(timestamp++);
517 m_seatInterface->notifyPointerLeave();
518 m_seatInterface->notifyPointerFrame();
519 m_seatInterface->setTimestamp(timestamp++);
520 m_seatInterface->notifyPointerEnter(serverSurface, m_seatInterface->pointerPos());
521 m_seatInterface->notifyPointerFrame();
522 m_seatInterface->setTimestamp(timestamp++);
523 m_seatInterface->notifyPointerMotion(QPointF(50, 50));
524 m_seatInterface->notifyPointerFrame();
525
526 // last but not least, simulate the drop
527 QSignalSpy cancelledSpy(m_dataSource, &KWayland::Client::DataSource::cancelled);
528 m_seatInterface->setTimestamp(timestamp++);
529 m_seatInterface->notifyPointerButton(1, PointerButtonState::Released);
530 m_seatInterface->notifyPointerFrame();
531 QVERIFY(cancelledSpy.wait());
532
533 // all the changes should have been ignored
534 QCOMPARE(axisSpy.count(), 1);
535 QCOMPARE(pointerMotionSpy.count(), 1);
536 QCOMPARE(pointerEnteredSpy.count(), 1);
537 QCOMPARE(pointerLeftSpy.count(), 1);
538}
539
540QTEST_GUILESS_MAIN(TestDragAndDrop)
541#include "test_drag_drop.moc"
Represents the Global for wl_data_device_manager interface.
Class holding the Wayland server display loop.
Definition display.h:34
void createShm()
Definition display.cpp:128
quint32 serial()
Definition display.cpp:139
bool addSocketName(const QString &name=QString())
Definition display.cpp:68
bool isRunning() const
Definition display.cpp:144
bool start()
Definition display.cpp:92
Represents a Seat on the Wayland Display.
Definition seat.h:134
void setFocusedTouchSurface(SurfaceInterface *surface, const QPointF &surfacePosition=QPointF())
Definition seat.cpp:1023
void setHasTouch(bool has)
Definition seat.cpp:372
QPointF pointerPos() const
Definition seat.cpp:437
void setTimestamp(std::chrono::microseconds time)
Definition seat.cpp:488
SurfaceInterface * dragSurface() const
Definition seat.cpp:1213
void notifyPointerMotion(const QPointF &pos)
Definition seat.cpp:442
void setHasPointer(bool has)
Definition seat.cpp:357
void notifyTouchMotion(qint32 id, const QPointF &globalPosition)
Definition seat.cpp:1094
void notifyPointerEnter(SurfaceInterface *surface, const QPointF &position, const QPointF &surfacePosition=QPointF())
Definition seat.cpp:555
void notifyPointerLeave()
Definition seat.cpp:604
void notifyPointerAxis(Qt::Orientation orientation, qreal delta, qint32 deltaV120, PointerAxisSource source, PointerAxisRelativeDirection direction=PointerAxisRelativeDirection::Normal)
Definition seat.cpp:691
void notifyPointerFrame()
Definition seat.cpp:744
void notifyTouchUp(qint32 id)
Definition seat.cpp:1133
QMatrix4x4 dragSurfaceTransformation() const
Definition seat.cpp:1208
void notifyPointerButton(quint32 button, PointerButtonState state)
Definition seat.cpp:712
DragAndDropIcon * dragIcon() const
Definition seat.cpp:1387
void notifyTouchDown(qint32 id, const QPointF &globalPosition)
Definition seat.cpp:1060
Resource representing a wl_surface.
Definition surface.h:80
#define DELETE(name)
#define CREATE(variable, factory, iface)