1 /********************************************************************
2 Copyright © 2016 Martin Gräßlin <mgraesslin@kde.org>
3 Copyright © 2020 Roman Gilg <subdiff@gmail.com>
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) version 3, or any
9 later version accepted by the membership of KDE e.V. (or its
10 successor approved by the membership of KDE e.V.), which shall
11 act as a proxy defined in Section 6 of version 3 of the license.
12
13 This library is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public
19 License along with this library. If not, see <http://www.gnu.org/licenses/>.
20 *********************************************************************/
21 #include <QtTest>
22
23 #include "../../src/client/compositor.h"
24 #include "../../src/client/connection_thread.h"
25 #include "../../src/client/datadevice.h"
26 #include "../../src/client/datadevicemanager.h"
27 #include "../../src/client/datasource.h"
28 #include "../../src/client/event_queue.h"
29 #include "../../src/client/pointer.h"
30 #include "../../src/client/registry.h"
31 #include "../../src/client/seat.h"
32 #include "../../src/client/shm_pool.h"
33 #include "../../src/client/surface.h"
34 #include "../../src/client/touch.h"
35
36 #include "../../server/compositor.h"
37 #include "../../server/data_device.h"
38 #include "../../server/data_device_manager.h"
39 #include "../../server/data_source.h"
40 #include "../../server/display.h"
41 #include "../../server/drag_pool.h"
42 #include "../../server/pointer_pool.h"
43 #include "../../server/seat.h"
44 #include "../../server/surface.h"
45 #include "../../server/touch_pool.h"
46
47 class TestDragAndDrop : public QObject
48 {
49 Q_OBJECT
50
51 private Q_SLOTS:
52 void init();
53 void cleanup();
54
55 void test_pointer();
56 void test_touch();
57 void test_cancel_by_destroyed_data_source();
58 void test_target_removed();
59 void test_pointer_events_ignored();
60
61 private:
62 struct Client;
63 Wrapland::Client::Surface* create_surface(Client& client);
64 Wrapland::Server::Surface* get_server_surface();
65
66 Wrapland::Server::Display* m_display = nullptr;
67 Wrapland::Server::Compositor* m_server_compositor = nullptr;
68 Wrapland::Server::DataDeviceManager* m_server_device_manager = nullptr;
69 Wrapland::Server::Seat* m_server_seat = nullptr;
70
71 struct Client {
72 Wrapland::Client::ConnectionThread* connection = nullptr;
73 QThread* thread = nullptr;
74 Wrapland::Client::EventQueue* queue = nullptr;
75 Wrapland::Client::Compositor* compositor = nullptr;
76 Wrapland::Client::Registry* registry = nullptr;
77 Wrapland::Client::DataDevice* device = nullptr;
78 Wrapland::Server::DataDevice* server_device{nullptr};
79 Wrapland::Client::DataSource* source = nullptr;
80 Wrapland::Client::Seat* seat = nullptr;
81 Wrapland::Client::Pointer* pointer = nullptr;
82 Wrapland::Client::Touch* touch = nullptr;
83 Wrapland::Client::DataDeviceManager* ddm = nullptr;
84 Wrapland::Client::ShmPool* shm = nullptr;
85 } c_1, c_2;
86 Client* clients[2] = {&c_1, &c_2};
87 };
88
89 static const QString s_socketName = QStringLiteral("wrapland-test-wayland-drag-n-drop-0");
90
init()91 void TestDragAndDrop::init()
92 {
93 qRegisterMetaType<Wrapland::Server::DataDevice*>();
94 qRegisterMetaType<Wrapland::Server::Surface*>();
95
96 m_display = new Wrapland::Server::Display(this);
97 m_display->setSocketName(s_socketName);
98 m_display->start();
99
100 m_server_compositor = m_display->createCompositor(m_display);
101 m_server_seat = m_display->createSeat(m_display);
102 m_server_seat->setHasPointer(true);
103 m_server_seat->setHasTouch(true);
104
105 m_server_device_manager = m_display->createDataDeviceManager(m_display);
106 m_display->createShm();
107
108 for (auto client : clients) {
109 // setup connection
110 client->connection = new Wrapland::Client::ConnectionThread;
111 QSignalSpy connected_spy(client->connection,
112 &Wrapland::Client::ConnectionThread::establishedChanged);
113 QVERIFY(connected_spy.isValid());
114 client->connection->setSocketName(s_socketName);
115
116 client->thread = new QThread(this);
117 client->connection->moveToThread(client->thread);
118 client->thread->start();
119
120 client->connection->establishConnection();
121 QVERIFY(connected_spy.count() || connected_spy.wait());
122 QCOMPARE(connected_spy.count(), 1);
123
124 client->queue = new Wrapland::Client::EventQueue(this);
125 QVERIFY(!client->queue->isValid());
126 client->queue->setup(client->connection);
127 QVERIFY(client->queue->isValid());
128
129 client->registry = new Wrapland::Client::Registry();
130 QSignalSpy interfaces_announced_spy(client->registry,
131 &Wrapland::Client::Registry::interfaceAnnounced);
132 QVERIFY(interfaces_announced_spy.isValid());
133
134 QVERIFY(!client->registry->eventQueue());
135 client->registry->setEventQueue(client->queue);
136 QCOMPARE(client->registry->eventQueue(), client->queue);
137 client->registry->create(client->connection);
138 QVERIFY(client->registry->isValid());
139 client->registry->setup();
140
141 QVERIFY(interfaces_announced_spy.wait());
142 #define CREATE(variable, factory, iface) \
143 variable = client->registry->create##factory( \
144 client->registry->interface(Wrapland::Client::Registry::Interface::iface).name, \
145 client->registry->interface(Wrapland::Client::Registry::Interface::iface).version, \
146 this); \
147 QVERIFY(variable);
148
149 CREATE(client->compositor, Compositor, Compositor)
150 CREATE(client->seat, Seat, Seat)
151 CREATE(client->ddm, DataDeviceManager, DataDeviceManager)
152 CREATE(client->shm, ShmPool, Shm)
153
154 #undef CREATE
155
156 QSignalSpy pointerSpy(client->seat, &Wrapland::Client::Seat::hasPointerChanged);
157 QVERIFY(pointerSpy.isValid());
158 QVERIFY(pointerSpy.wait());
159 client->pointer = client->seat->createPointer(client->seat);
160 QVERIFY(client->pointer->isValid());
161 client->touch = client->seat->createTouch(client->seat);
162 QVERIFY(client->touch->isValid());
163
164 QSignalSpy device_created_spy(m_server_device_manager,
165 &Wrapland::Server::DataDeviceManager::deviceCreated);
166 QVERIFY(device_created_spy.isValid());
167 client->device = client->ddm->getDevice(client->seat, this);
168 QVERIFY(client->device->isValid());
169
170 QVERIFY(device_created_spy.wait());
171 QCOMPARE(device_created_spy.count(), 1);
172 client->server_device
173 = device_created_spy.first().first().value<Wrapland::Server::DataDevice*>();
174 QVERIFY(client->server_device);
175
176 client->source = client->ddm->createSource(this);
177 QVERIFY(client->source->isValid());
178 client->source->offer(QStringLiteral("text/plain"));
179 }
180 }
181
cleanup()182 void TestDragAndDrop::cleanup()
183 {
184 for (auto client : clients) {
185 #define DELETE(name) \
186 if (name) { \
187 delete name; \
188 name = nullptr; \
189 }
190 DELETE(client->source)
191 DELETE(client->device)
192 DELETE(client->shm)
193 DELETE(client->compositor)
194 DELETE(client->ddm)
195 DELETE(client->seat)
196 DELETE(client->queue)
197 DELETE(client->registry)
198 #undef DELETE
199 if (client->thread) {
200 client->thread->quit();
201 client->thread->wait();
202 delete client->thread;
203 client->thread = nullptr;
204 }
205 delete client->connection;
206 client->connection = nullptr;
207 }
208
209 delete m_display;
210 m_display = nullptr;
211 }
212
create_surface(Client & client)213 Wrapland::Client::Surface* TestDragAndDrop::create_surface(Client& client)
214 {
215 auto surface = client.compositor->createSurface();
216
217 QImage img(QSize(100, 200), QImage::Format_RGB32);
218 img.fill(Qt::red);
219 surface->attachBuffer(client.shm->createBuffer(img));
220 surface->damage(QRect(0, 0, 100, 200));
221 surface->commit(Wrapland::Client::Surface::CommitFlag::None);
222 return surface;
223 }
224
get_server_surface()225 Wrapland::Server::Surface* TestDragAndDrop::get_server_surface()
226 {
227 QSignalSpy surface_created_spy(m_server_compositor,
228 &Wrapland::Server::Compositor::surfaceCreated);
229 if (!surface_created_spy.isValid()) {
230 return nullptr;
231 }
232 if (!surface_created_spy.wait(500)) {
233 return nullptr;
234 }
235 return surface_created_spy.first().first().value<Wrapland::Server::Surface*>();
236 }
237
test_pointer()238 void TestDragAndDrop::test_pointer()
239 {
240 // This test verifies the very basic drag and drop on one surface, an enter, a move and the
241 // drop.
242
243 // First create a window.
244 std::unique_ptr<Wrapland::Client::Surface> surface(create_surface(c_1));
245 auto server_surface = get_server_surface();
246 QVERIFY(server_surface);
247
248 QSignalSpy source_selected_action_changed_spy(
249 c_1.source, &Wrapland::Client::DataSource::selectedDragAndDropActionChanged);
250 QVERIFY(source_selected_action_changed_spy.isValid());
251
252 // now we need to pass pointer focus to the Surface and simulate a button press
253 QSignalSpy button_press_spy(c_1.pointer, &Wrapland::Client::Pointer::buttonStateChanged);
254 QVERIFY(button_press_spy.isValid());
255 m_server_seat->pointers().set_focused_surface(server_surface);
256 m_server_seat->setTimestamp(2);
257 m_server_seat->pointers().button_pressed(1);
258 QVERIFY(button_press_spy.wait());
259 QCOMPARE(button_press_spy.first().at(1).value<quint32>(), quint32(2));
260
261 // add some signal spies for client side
262 QSignalSpy drag_entered_spy(c_1.device, &Wrapland::Client::DataDevice::dragEntered);
263 QVERIFY(drag_entered_spy.isValid());
264 QSignalSpy drag_motion_spy(c_1.device, &Wrapland::Client::DataDevice::dragMotion);
265 QVERIFY(drag_motion_spy.isValid());
266 QSignalSpy pointer_motion_spy(c_1.pointer, &Wrapland::Client::Pointer::motion);
267 QVERIFY(pointer_motion_spy.isValid());
268 QSignalSpy source_drop_spy(c_1.source, &Wrapland::Client::DataSource::dragAndDropPerformed);
269 QVERIFY(source_drop_spy.isValid());
270
271 // now we can start the drag and drop
272 QSignalSpy drag_started_spy(m_server_seat, &Wrapland::Server::Seat::dragStarted);
273 QVERIFY(drag_started_spy.isValid());
274 c_1.source->setDragAndDropActions(Wrapland::Client::DataDeviceManager::DnDAction::Copy
275 | Wrapland::Client::DataDeviceManager::DnDAction::Move);
276 c_1.device->startDrag(
277 button_press_spy.first().first().value<quint32>(), c_1.source, surface.get());
278 QVERIFY(drag_started_spy.wait());
279
280 auto& server_drags = m_server_seat->drags();
281 QCOMPARE(server_drags.get_target().surface, server_surface);
282 QCOMPARE(server_drags.get_target().transformation, QMatrix4x4());
283 QVERIFY(!server_drags.get_source().dev->icon());
284 QCOMPARE(server_drags.get_source().dev->dragImplicitGrabSerial(),
285 button_press_spy.first().first().value<quint32>());
286 QVERIFY(drag_entered_spy.wait());
287 QCOMPARE(drag_entered_spy.count(), 1);
288 QCOMPARE(drag_entered_spy.first().first().value<quint32>(), m_display->serial());
289 QCOMPARE(drag_entered_spy.first().last().toPointF(), QPointF(0, 0));
290 QCOMPARE(c_1.device->dragSurface().data(), surface.get());
291
292 auto offer = c_1.device->dragOffer();
293 QVERIFY(offer);
294 QCOMPARE(offer->selectedDragAndDropAction(),
295 Wrapland::Client::DataDeviceManager::DnDAction::None);
296 QSignalSpy offer_action_changed_spy(
297 offer, &Wrapland::Client::DataOffer::selectedDragAndDropActionChanged);
298 QVERIFY(offer_action_changed_spy.isValid());
299 QCOMPARE(c_1.device->dragOffer()->offeredMimeTypes().count(), 1);
300 QCOMPARE(c_1.device->dragOffer()->offeredMimeTypes().first().name(),
301 QStringLiteral("text/plain"));
302 QTRY_COMPARE(offer->sourceDragAndDropActions(),
303 Wrapland::Client::DataDeviceManager::DnDAction::Copy
304 | Wrapland::Client::DataDeviceManager::DnDAction::Move);
305
306 offer->setDragAndDropActions(Wrapland::Client::DataDeviceManager::DnDAction::Copy
307 | Wrapland::Client::DataDeviceManager::DnDAction::Move,
308 Wrapland::Client::DataDeviceManager::DnDAction::Move);
309 QVERIFY(offer_action_changed_spy.wait());
310 QCOMPARE(offer_action_changed_spy.count(), 1);
311 QCOMPARE(offer->selectedDragAndDropAction(),
312 Wrapland::Client::DataDeviceManager::DnDAction::Move);
313 QCOMPARE(source_selected_action_changed_spy.count(), 1);
314 QCOMPARE(c_1.source->selectedDragAndDropAction(),
315 Wrapland::Client::DataDeviceManager::DnDAction::Move);
316
317 // Simulate motion.
318 m_server_seat->setTimestamp(3);
319 m_server_seat->pointers().set_position(QPointF(3, 3));
320 QVERIFY(drag_motion_spy.wait());
321 QCOMPARE(drag_motion_spy.count(), 1);
322 QCOMPARE(drag_motion_spy.first().first().toPointF(), QPointF(3, 3));
323 QCOMPARE(drag_motion_spy.first().last().toUInt(), 3u);
324
325 // Simulate drop.
326 QSignalSpy server_drag_ended_spy(m_server_seat, &Wrapland::Server::Seat::dragEnded);
327 QVERIFY(server_drag_ended_spy.isValid());
328 QSignalSpy dropped_spy(c_1.device, &Wrapland::Client::DataDevice::dropped);
329 QVERIFY(dropped_spy.isValid());
330 m_server_seat->setTimestamp(4);
331 m_server_seat->pointers().button_released(1);
332 QVERIFY(source_drop_spy.isEmpty());
333 QVERIFY(dropped_spy.wait());
334 QCOMPARE(source_drop_spy.count(), 1);
335 QCOMPARE(server_drag_ended_spy.count(), 1);
336
337 QSignalSpy finished_spy(c_1.source, &Wrapland::Client::DataSource::dragAndDropFinished);
338 QVERIFY(finished_spy.isValid());
339 offer->dragAndDropFinished();
340 QVERIFY(finished_spy.wait());
341 delete offer;
342
343 // Verify that we did not get any further input events.
344 QVERIFY(pointer_motion_spy.isEmpty());
345 QCOMPARE(button_press_spy.count(), 1);
346 }
347
test_touch()348 void TestDragAndDrop::test_touch()
349 {
350 // This test verifies the very basic drag and drop on one surface, an enter, a move and the
351 // drop.
352
353 // First create a window.
354 std::unique_ptr<Wrapland::Client::Surface> s(create_surface(c_1));
355 s->setSize(QSize(100, 100));
356 auto server_surface = get_server_surface();
357 QVERIFY(server_surface);
358
359 QSignalSpy source_selected_action_changed_spy(
360 c_1.source, &Wrapland::Client::DataSource::selectedDragAndDropActionChanged);
361 QVERIFY(source_selected_action_changed_spy.isValid());
362
363 // now we need to pass touch focus to the Surface and simulate a touch down
364 QSignalSpy sequence_started_spy(c_1.touch, &Wrapland::Client::Touch::sequenceStarted);
365 QVERIFY(sequence_started_spy.isValid());
366 QSignalSpy point_added_spy(c_1.touch, &Wrapland::Client::Touch::pointAdded);
367 QVERIFY(point_added_spy.isValid());
368
369 auto& server_touches = m_server_seat->touches();
370 server_touches.set_focused_surface(server_surface);
371 m_server_seat->setTimestamp(2);
372 auto const touchId = server_touches.touch_down(QPointF(50, 50));
373 QVERIFY(sequence_started_spy.wait());
374
375 auto tp{sequence_started_spy.first().at(0).value<Wrapland::Client::TouchPoint*>()};
376 QVERIFY(tp);
377 QCOMPARE(tp->time(), quint32(2));
378
379 // add some signal spies for client side
380 QSignalSpy drag_entered_spy(c_1.device, &Wrapland::Client::DataDevice::dragEntered);
381 QVERIFY(drag_entered_spy.isValid());
382 QSignalSpy drag_motion_spy(c_1.device, &Wrapland::Client::DataDevice::dragMotion);
383 QVERIFY(drag_motion_spy.isValid());
384 QSignalSpy touch_motion_spy(c_1.touch, &Wrapland::Client::Touch::pointMoved);
385 QVERIFY(touch_motion_spy.isValid());
386 QSignalSpy source_drop_spy(c_1.source, &Wrapland::Client::DataSource::dragAndDropPerformed);
387 QVERIFY(source_drop_spy.isValid());
388
389 // now we can start the drag and drop
390 QSignalSpy drag_started_spy(m_server_seat, &Wrapland::Server::Seat::dragStarted);
391 QVERIFY(drag_started_spy.isValid());
392 c_1.source->setDragAndDropActions(Wrapland::Client::DataDeviceManager::DnDAction::Copy
393 | Wrapland::Client::DataDeviceManager::DnDAction::Move);
394 c_1.device->startDrag(tp->downSerial(), c_1.source, s.get());
395 QVERIFY(drag_started_spy.wait());
396
397 auto& server_drags = m_server_seat->drags();
398 QCOMPARE(server_drags.get_target().surface, server_surface);
399 QCOMPARE(server_drags.get_target().transformation, QMatrix4x4());
400 QVERIFY(!server_drags.get_source().dev->icon());
401 QCOMPARE(server_drags.get_source().dev->dragImplicitGrabSerial(), tp->downSerial());
402 QVERIFY(drag_entered_spy.wait());
403 QCOMPARE(drag_entered_spy.count(), 1);
404 QCOMPARE(drag_entered_spy.first().first().value<quint32>(), m_display->serial());
405 QCOMPARE(drag_entered_spy.first().last().toPointF(), QPointF(0, 0));
406 QCOMPARE(c_1.device->dragSurface().data(), s.get());
407 auto offer = c_1.device->dragOffer();
408 QVERIFY(offer);
409 QCOMPARE(offer->selectedDragAndDropAction(),
410 Wrapland::Client::DataDeviceManager::DnDAction::None);
411 QSignalSpy offer_action_changed_spy(
412 offer, &Wrapland::Client::DataOffer::selectedDragAndDropActionChanged);
413 QVERIFY(offer_action_changed_spy.isValid());
414 QCOMPARE(c_1.device->dragOffer()->offeredMimeTypes().count(), 1);
415 QCOMPARE(c_1.device->dragOffer()->offeredMimeTypes().first().name(),
416 QStringLiteral("text/plain"));
417 QTRY_COMPARE(offer->sourceDragAndDropActions(),
418 Wrapland::Client::DataDeviceManager::DnDAction::Copy
419 | Wrapland::Client::DataDeviceManager::DnDAction::Move);
420 offer->setDragAndDropActions(Wrapland::Client::DataDeviceManager::DnDAction::Copy
421 | Wrapland::Client::DataDeviceManager::DnDAction::Move,
422 Wrapland::Client::DataDeviceManager::DnDAction::Move);
423 QVERIFY(offer_action_changed_spy.wait());
424 QCOMPARE(offer_action_changed_spy.count(), 1);
425 QCOMPARE(offer->selectedDragAndDropAction(),
426 Wrapland::Client::DataDeviceManager::DnDAction::Move);
427 QCOMPARE(source_selected_action_changed_spy.count(), 1);
428 QCOMPARE(c_1.source->selectedDragAndDropAction(),
429 Wrapland::Client::DataDeviceManager::DnDAction::Move);
430
431 // simulate motion
432 m_server_seat->setTimestamp(3);
433 server_touches.touch_move(touchId, QPointF(75, 75));
434 QVERIFY(drag_motion_spy.wait());
435 QCOMPARE(drag_motion_spy.count(), 1);
436 QCOMPARE(drag_motion_spy.first().first().toPointF(), QPointF(75, 75));
437 QCOMPARE(drag_motion_spy.first().last().toUInt(), 3u);
438
439 // simulate drop
440 QSignalSpy server_drag_ended_spy(m_server_seat, &Wrapland::Server::Seat::dragEnded);
441 QVERIFY(server_drag_ended_spy.isValid());
442 QSignalSpy dropped_spy(c_1.device, &Wrapland::Client::DataDevice::dropped);
443 QVERIFY(dropped_spy.isValid());
444 m_server_seat->setTimestamp(4);
445 server_touches.touch_up(touchId);
446 QVERIFY(source_drop_spy.isEmpty());
447 QVERIFY(dropped_spy.wait());
448 QCOMPARE(source_drop_spy.count(), 1);
449 QCOMPARE(server_drag_ended_spy.count(), 1);
450
451 QSignalSpy finished_spy(c_1.source, &Wrapland::Client::DataSource::dragAndDropFinished);
452 QVERIFY(finished_spy.isValid());
453 offer->dragAndDropFinished();
454 QVERIFY(finished_spy.wait());
455 delete offer;
456
457 // verify that we did not get any further input events
458 QVERIFY(touch_motion_spy.isEmpty());
459 QCOMPARE(point_added_spy.count(), 0);
460 }
461
test_cancel_by_destroyed_data_source()462 void TestDragAndDrop::test_cancel_by_destroyed_data_source()
463 {
464 // This test simulates the problem from BUG 389221.
465
466 // First create a window.
467 std::unique_ptr<Wrapland::Client::Surface> s(create_surface(c_1));
468 auto server_surface = get_server_surface();
469 QVERIFY(server_surface);
470
471 QSignalSpy source_selected_action_changed_spy(
472 c_1.source, &Wrapland::Client::DataSource::selectedDragAndDropActionChanged);
473 QVERIFY(source_selected_action_changed_spy.isValid());
474
475 // Now we need to pass pointer focus to the Surface and simulate a button press.
476 QSignalSpy button_press_spy(c_1.pointer, &Wrapland::Client::Pointer::buttonStateChanged);
477 QVERIFY(button_press_spy.isValid());
478 m_server_seat->pointers().set_focused_surface(server_surface);
479 m_server_seat->setTimestamp(2);
480 m_server_seat->pointers().button_pressed(1);
481 QVERIFY(button_press_spy.wait());
482 QCOMPARE(button_press_spy.first().at(1).value<quint32>(), quint32(2));
483
484 // Add some signal spies for client side.
485 QSignalSpy drag_entered_spy(c_1.device, &Wrapland::Client::DataDevice::dragEntered);
486 QVERIFY(drag_entered_spy.isValid());
487 QSignalSpy drag_motion_spy(c_1.device, &Wrapland::Client::DataDevice::dragMotion);
488 QVERIFY(drag_motion_spy.isValid());
489 QSignalSpy pointer_motion_spy(c_1.pointer, &Wrapland::Client::Pointer::motion);
490 QVERIFY(pointer_motion_spy.isValid());
491 QSignalSpy drag_left_spy(c_1.device, &Wrapland::Client::DataDevice::dragLeft);
492 QVERIFY(drag_left_spy.isValid());
493
494 // Now we can start the drag and drop.
495 QSignalSpy drag_started_spy(m_server_seat, &Wrapland::Server::Seat::dragStarted);
496 QVERIFY(drag_started_spy.isValid());
497
498 c_1.source->setDragAndDropActions(Wrapland::Client::DataDeviceManager::DnDAction::Copy
499 | Wrapland::Client::DataDeviceManager::DnDAction::Move);
500 c_1.device->startDrag(button_press_spy.first().first().value<quint32>(), c_1.source, s.get());
501
502 QVERIFY(drag_started_spy.wait());
503
504 auto& server_drags = m_server_seat->drags();
505 QCOMPARE(server_drags.get_target().surface, server_surface);
506 QCOMPARE(server_drags.get_target().transformation, QMatrix4x4());
507 QVERIFY(!server_drags.get_source().dev->icon());
508 QCOMPARE(server_drags.get_source().dev->dragImplicitGrabSerial(),
509 button_press_spy.first().first().value<quint32>());
510
511 QVERIFY(drag_entered_spy.wait());
512 QCOMPARE(drag_entered_spy.count(), 1);
513 QCOMPARE(drag_entered_spy.first().first().value<quint32>(), m_display->serial());
514 QCOMPARE(drag_entered_spy.first().last().toPointF(), QPointF(0, 0));
515 QCOMPARE(c_1.device->dragSurface().data(), s.get());
516
517 auto offer = c_1.device->dragOffer();
518 QVERIFY(offer);
519 QCOMPARE(offer->selectedDragAndDropAction(),
520 Wrapland::Client::DataDeviceManager::DnDAction::None);
521 QSignalSpy offer_action_changed_spy(
522 offer, &Wrapland::Client::DataOffer::selectedDragAndDropActionChanged);
523 QVERIFY(offer_action_changed_spy.isValid());
524 QCOMPARE(c_1.device->dragOffer()->offeredMimeTypes().count(), 1);
525 QCOMPARE(c_1.device->dragOffer()->offeredMimeTypes().first().name(),
526 QStringLiteral("text/plain"));
527 QTRY_COMPARE(offer->sourceDragAndDropActions(),
528 Wrapland::Client::DataDeviceManager::DnDAction::Copy
529 | Wrapland::Client::DataDeviceManager::DnDAction::Move);
530
531 offer->setDragAndDropActions(Wrapland::Client::DataDeviceManager::DnDAction::Copy
532 | Wrapland::Client::DataDeviceManager::DnDAction::Move,
533 Wrapland::Client::DataDeviceManager::DnDAction::Move);
534 QVERIFY(offer_action_changed_spy.wait());
535 QCOMPARE(offer_action_changed_spy.count(), 1);
536 QCOMPARE(offer->selectedDragAndDropAction(),
537 Wrapland::Client::DataDeviceManager::DnDAction::Move);
538 QCOMPARE(source_selected_action_changed_spy.count(), 1);
539 QCOMPARE(c_1.source->selectedDragAndDropAction(),
540 Wrapland::Client::DataDeviceManager::DnDAction::Move);
541
542 // Simulate motion.
543 m_server_seat->setTimestamp(3);
544 m_server_seat->pointers().set_position(QPointF(3, 3));
545 QVERIFY(drag_motion_spy.wait());
546 QCOMPARE(drag_motion_spy.count(), 1);
547 QCOMPARE(drag_motion_spy.first().first().toPointF(), QPointF(3, 3));
548 QCOMPARE(drag_motion_spy.first().last().toUInt(), 3u);
549
550 // Now delete the DataSource.
551 delete c_1.source;
552 c_1.source = nullptr;
553 QSignalSpy server_drag_ended_spy(m_server_seat, &Wrapland::Server::Seat::dragEnded);
554 QVERIFY(server_drag_ended_spy.isValid());
555 QVERIFY(drag_left_spy.isEmpty());
556 QVERIFY(drag_left_spy.wait());
557 QTRY_COMPARE(drag_left_spy.count(), 1);
558 QTRY_COMPARE(server_drag_ended_spy.count(), 1);
559
560 // Simulate drop.
561 QSignalSpy dropped_spy(c_1.device, &Wrapland::Client::DataDevice::dropped);
562 QVERIFY(dropped_spy.isValid());
563 m_server_seat->setTimestamp(4);
564 m_server_seat->pointers().button_released(1);
565 QVERIFY(!dropped_spy.wait(500));
566
567 // Verify that we did not get any further input events.
568 QVERIFY(pointer_motion_spy.isEmpty());
569 QCOMPARE(button_press_spy.count(), 2);
570 }
571
test_target_removed()572 void TestDragAndDrop::test_target_removed()
573 {
574 // Checks that if target goes away mid-drag server handles this correctly.
575
576 std::unique_ptr<Wrapland::Client::Surface> surface_1(create_surface(c_1));
577 auto server_surface_1 = get_server_surface();
578 QVERIFY(server_surface_1);
579 std::unique_ptr<Wrapland::Client::Surface> surface_2(create_surface(c_2));
580 auto server_surface_2 = get_server_surface();
581 QVERIFY(server_surface_2);
582
583 QSignalSpy source_selected_action_changed_spy(
584 c_1.source, &Wrapland::Client::DataSource::selectedDragAndDropActionChanged);
585 QVERIFY(source_selected_action_changed_spy.isValid());
586
587 // Now we need to pass pointer focus to the Surface and simulate a button press.
588 QSignalSpy button_press_spy(c_1.pointer, &Wrapland::Client::Pointer::buttonStateChanged);
589 QVERIFY(button_press_spy.isValid());
590 m_server_seat->pointers().set_focused_surface(server_surface_1);
591 m_server_seat->setTimestamp(2);
592 m_server_seat->pointers().button_pressed(1);
593 QVERIFY(button_press_spy.wait());
594 QCOMPARE(button_press_spy.first().at(1).value<quint32>(), quint32(2));
595
596 // Now we can start the drag and drop.
597 QSignalSpy drag_started_spy(m_server_seat, &Wrapland::Server::Seat::dragStarted);
598 QVERIFY(drag_started_spy.isValid());
599
600 c_1.source->setDragAndDropActions(Wrapland::Client::DataDeviceManager::DnDAction::Copy
601 | Wrapland::Client::DataDeviceManager::DnDAction::Move);
602 c_1.device->startDrag(
603 button_press_spy.first().first().value<quint32>(), c_1.source, surface_1.get());
604
605 QVERIFY(drag_started_spy.wait());
606
607 auto& server_drags = m_server_seat->drags();
608 QCOMPARE(server_drags.get_target().surface, server_surface_1);
609 QCOMPARE(server_drags.get_target().transformation, QMatrix4x4());
610 QVERIFY(!server_drags.get_source().dev->icon());
611 QCOMPARE(server_drags.get_source().dev->dragImplicitGrabSerial(),
612 button_press_spy.first().first().value<quint32>());
613
614 QSignalSpy drag_entered_spy(c_2.device, &Wrapland::Client::DataDevice::dragEntered);
615 QVERIFY(drag_entered_spy.isValid());
616
617 // Now move drag to the second client.
618 server_drags.set_target(server_surface_2);
619 QCOMPARE(server_drags.get_target().surface, server_surface_2);
620
621 QVERIFY(drag_entered_spy.wait());
622 QCOMPARE(drag_entered_spy.count(), 1);
623 QCOMPARE(drag_entered_spy.first().first().value<quint32>(), m_display->serial());
624 QCOMPARE(drag_entered_spy.first().last().toPointF(), QPointF(0, 0));
625 QCOMPARE(c_2.device->dragSurface().data(), surface_2.get());
626
627 auto offer = c_2.device->dragOffer();
628 QVERIFY(offer);
629 QCOMPARE(offer->selectedDragAndDropAction(),
630 Wrapland::Client::DataDeviceManager::DnDAction::None);
631 QSignalSpy offer_action_changed_spy(
632 offer, &Wrapland::Client::DataOffer::selectedDragAndDropActionChanged);
633 QVERIFY(offer_action_changed_spy.isValid());
634 QCOMPARE(c_2.device->dragOffer()->offeredMimeTypes().count(), 1);
635 QCOMPARE(c_2.device->dragOffer()->offeredMimeTypes().first().name(),
636 QStringLiteral("text/plain"));
637 QTRY_COMPARE(offer->sourceDragAndDropActions(),
638 Wrapland::Client::DataDeviceManager::DnDAction::Copy
639 | Wrapland::Client::DataDeviceManager::DnDAction::Move);
640
641 offer->setDragAndDropActions(Wrapland::Client::DataDeviceManager::DnDAction::Copy
642 | Wrapland::Client::DataDeviceManager::DnDAction::Move,
643 Wrapland::Client::DataDeviceManager::DnDAction::Move);
644 QVERIFY(offer_action_changed_spy.wait());
645 QCOMPARE(offer_action_changed_spy.count(), 1);
646 QCOMPARE(offer->selectedDragAndDropAction(),
647 Wrapland::Client::DataDeviceManager::DnDAction::Move);
648 QVERIFY(source_selected_action_changed_spy.count() == 1
649 || source_selected_action_changed_spy.wait());
650 QCOMPARE(source_selected_action_changed_spy.count(), 1);
651 QCOMPARE(c_1.source->selectedDragAndDropAction(),
652 Wrapland::Client::DataDeviceManager::DnDAction::Move);
653
654 // Now delete the second client's data device.
655 QSignalSpy device_destroyed_spy(c_2.server_device,
656 &Wrapland::Server::DataDevice::resourceDestroyed);
657 QVERIFY(device_destroyed_spy.isValid());
658 delete c_2.device;
659 c_2.device = nullptr;
660 QVERIFY(device_destroyed_spy.wait());
661
662 // Simulate drop.
663 QSignalSpy dropped_spy(c_1.source, &Wrapland::Client::DataSource::dragAndDropPerformed);
664 QVERIFY(dropped_spy.isValid());
665 m_server_seat->setTimestamp(4);
666 m_server_seat->pointers().button_released(1);
667 QVERIFY(dropped_spy.wait(500));
668 }
669
test_pointer_events_ignored()670 void TestDragAndDrop::test_pointer_events_ignored()
671 {
672 // This test verifies that all pointer events are ignored on the focused Pointer device during
673 // drag.
674
675 // First create a window.
676 std::unique_ptr<Wrapland::Client::Surface> s(create_surface(c_1));
677 auto server_surface = get_server_surface();
678 QVERIFY(server_surface);
679
680 // pass it pointer focus
681 m_server_seat->pointers().set_focused_surface(server_surface);
682
683 // create signal spies for all the pointer events
684 QSignalSpy pointer_entered_spy(c_1.pointer, &Wrapland::Client::Pointer::entered);
685 QVERIFY(pointer_entered_spy.isValid());
686 QSignalSpy pointer_left_spy(c_1.pointer, &Wrapland::Client::Pointer::left);
687 QVERIFY(pointer_left_spy.isValid());
688 QSignalSpy pointer_motion_spy(c_1.pointer, &Wrapland::Client::Pointer::motion);
689 QVERIFY(pointer_motion_spy.isValid());
690 QSignalSpy axis_spy(c_1.pointer, &Wrapland::Client::Pointer::axisChanged);
691 QVERIFY(axis_spy.isValid());
692 QSignalSpy button_spy(c_1.pointer, &Wrapland::Client::Pointer::buttonStateChanged);
693 QVERIFY(button_spy.isValid());
694 QSignalSpy drag_entered_spy(c_1.device, &Wrapland::Client::DataDevice::dragEntered);
695 QVERIFY(drag_entered_spy.isValid());
696
697 // first simulate a few things
698 quint32 timestamp = 1;
699 m_server_seat->setTimestamp(timestamp++);
700 m_server_seat->pointers().set_position(QPointF(10, 10));
701 m_server_seat->setTimestamp(timestamp++);
702 m_server_seat->pointers().send_axis(Qt::Vertical, 5);
703 // verify that we have those
704 QVERIFY(axis_spy.wait());
705 QCOMPARE(axis_spy.count(), 1);
706 QCOMPARE(pointer_motion_spy.count(), 1);
707 QCOMPARE(pointer_entered_spy.count(), 1);
708 QVERIFY(button_spy.isEmpty());
709 QVERIFY(pointer_left_spy.isEmpty());
710
711 // let's start the drag
712 m_server_seat->setTimestamp(timestamp++);
713 m_server_seat->pointers().button_pressed(1);
714 QVERIFY(button_spy.wait());
715 QCOMPARE(button_spy.count(), 1);
716 c_1.device->startDrag(button_spy.first().first().value<quint32>(), c_1.source, s.get());
717 QVERIFY(drag_entered_spy.wait());
718
719 // now simulate all the possible pointer interactions
720 m_server_seat->setTimestamp(timestamp++);
721 m_server_seat->pointers().button_pressed(2);
722 m_server_seat->setTimestamp(timestamp++);
723 m_server_seat->pointers().button_released(2);
724 m_server_seat->setTimestamp(timestamp++);
725 m_server_seat->pointers().send_axis(Qt::Vertical, 5);
726 m_server_seat->setTimestamp(timestamp++);
727 m_server_seat->pointers().send_axis(Qt::Horizontal, 5);
728 m_server_seat->setTimestamp(timestamp++);
729 m_server_seat->pointers().set_focused_surface(nullptr);
730 m_server_seat->setTimestamp(timestamp++);
731 m_server_seat->pointers().set_focused_surface(server_surface);
732 m_server_seat->setTimestamp(timestamp++);
733 m_server_seat->pointers().set_position(QPointF(50, 50));
734
735 // last but not least, simulate the drop
736 QSignalSpy dropped_spy(c_1.device, &Wrapland::Client::DataDevice::dropped);
737 QVERIFY(dropped_spy.isValid());
738 m_server_seat->setTimestamp(timestamp++);
739 m_server_seat->pointers().button_released(1);
740 QVERIFY(dropped_spy.wait());
741
742 // all the changes should have been ignored
743 QCOMPARE(axis_spy.count(), 1);
744 QCOMPARE(pointer_motion_spy.count(), 1);
745 QCOMPARE(pointer_entered_spy.count(), 1);
746 QCOMPARE(button_spy.count(), 1);
747 QVERIFY(pointer_left_spy.isEmpty());
748 }
749
750 QTEST_GUILESS_MAIN(TestDragAndDrop)
751 #include "drag_and_drop.moc"
752