KWin
Loading...
Searching...
No Matches
test_datadevice.cpp
Go to the documentation of this file.
1/*
2 SPDX-FileCopyrightText: 2014 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
11#include "wayland/compositor.h"
13#include "wayland/datasource.h"
14#include "wayland/display.h"
15#include "wayland/pointer.h"
16#include "wayland/seat.h"
17#include "wayland/surface.h"
18
19// KWayland
20#include "KWayland/Client/compositor.h"
21#include "KWayland/Client/connection_thread.h"
22#include "KWayland/Client/datadevice.h"
23#include "KWayland/Client/datadevicemanager.h"
24#include "KWayland/Client/datasource.h"
25#include "KWayland/Client/event_queue.h"
26#include "KWayland/Client/keyboard.h"
27#include "KWayland/Client/pointer.h"
28#include "KWayland/Client/registry.h"
29#include "KWayland/Client/seat.h"
30#include "KWayland/Client/surface.h"
31
32// Wayland
33#include <wayland-client.h>
34
35#include <unistd.h>
36
37class TestDataDevice : public QObject
38{
39 Q_OBJECT
40private Q_SLOTS:
41 void init();
42 void cleanup();
43
44 void testCreate();
45 void testDrag_data();
46 void testDrag();
47 void testDragInternally_data();
48 void testDragInternally();
49 void testSetSelection();
50 void testSendSelectionOnSeat();
51 void testReplaceSource();
52
53private:
54 KWin::Display *m_display = nullptr;
55 KWin::DataDeviceManagerInterface *m_dataDeviceManagerInterface = nullptr;
56 KWin::CompositorInterface *m_compositorInterface = nullptr;
57 KWin::SeatInterface *m_seatInterface = nullptr;
58 KWayland::Client::ConnectionThread *m_connection = nullptr;
59 KWayland::Client::DataDeviceManager *m_dataDeviceManager = nullptr;
60 KWayland::Client::Compositor *m_compositor = nullptr;
61 KWayland::Client::Seat *m_seat = nullptr;
62 KWayland::Client::EventQueue *m_queue = nullptr;
63 QThread *m_thread = nullptr;
64};
65
66static const QString s_socketName = QStringLiteral("kwayland-test-wayland-datadevice-0");
67
68void TestDataDevice::init()
69{
70 qRegisterMetaType<KWin::DataSourceInterface *>();
71 using namespace KWin;
72 delete m_display;
73 m_display = new KWin::Display(this);
74 m_display->addSocketName(s_socketName);
75 m_display->start();
76 QVERIFY(m_display->isRunning());
77
78 // setup connection
79 m_connection = new KWayland::Client::ConnectionThread;
80 QSignalSpy connectedSpy(m_connection, &KWayland::Client::ConnectionThread::connected);
81 m_connection->setSocketName(s_socketName);
82
83 m_thread = new QThread(this);
84 m_connection->moveToThread(m_thread);
85 m_thread->start();
86
87 m_connection->initConnection();
88 QVERIFY(connectedSpy.wait());
89
90 m_queue = new KWayland::Client::EventQueue(this);
91 QVERIFY(!m_queue->isValid());
92 m_queue->setup(m_connection);
93 QVERIFY(m_queue->isValid());
94
95 KWayland::Client::Registry registry;
96 QSignalSpy dataDeviceManagerSpy(&registry, &KWayland::Client::Registry::dataDeviceManagerAnnounced);
97 QSignalSpy seatSpy(&registry, &KWayland::Client::Registry::seatAnnounced);
98 QSignalSpy compositorSpy(&registry, &KWayland::Client::Registry::compositorAnnounced);
99 QVERIFY(!registry.eventQueue());
100 registry.setEventQueue(m_queue);
101 QCOMPARE(registry.eventQueue(), m_queue);
102 registry.create(m_connection->display());
103 QVERIFY(registry.isValid());
104 registry.setup();
105
106 m_dataDeviceManagerInterface = new DataDeviceManagerInterface(m_display, m_display);
107
108 QVERIFY(dataDeviceManagerSpy.wait());
109 m_dataDeviceManager =
110 registry.createDataDeviceManager(dataDeviceManagerSpy.first().first().value<quint32>(), dataDeviceManagerSpy.first().last().value<quint32>(), this);
111
112 m_seatInterface = new SeatInterface(m_display, m_display);
113 m_seatInterface->setHasPointer(true);
114
115 QVERIFY(seatSpy.wait());
116 m_seat = registry.createSeat(seatSpy.first().first().value<quint32>(), seatSpy.first().last().value<quint32>(), this);
117 QVERIFY(m_seat->isValid());
118 QSignalSpy pointerChangedSpy(m_seat, &KWayland::Client::Seat::hasPointerChanged);
119 QVERIFY(pointerChangedSpy.wait());
120
121 m_compositorInterface = new CompositorInterface(m_display, m_display);
122 QVERIFY(compositorSpy.wait());
123 m_compositor = registry.createCompositor(compositorSpy.first().first().value<quint32>(), compositorSpy.first().last().value<quint32>(), this);
124 QVERIFY(m_compositor->isValid());
125}
126
127void TestDataDevice::cleanup()
128{
129 if (m_dataDeviceManager) {
130 delete m_dataDeviceManager;
131 m_dataDeviceManager = nullptr;
132 }
133 if (m_seat) {
134 delete m_seat;
135 m_seat = nullptr;
136 }
137 if (m_compositor) {
138 delete m_compositor;
139 m_compositor = nullptr;
140 }
141 if (m_queue) {
142 delete m_queue;
143 m_queue = nullptr;
144 }
145 if (m_thread) {
146 m_thread->quit();
147 m_thread->wait();
148 delete m_thread;
149 m_thread = nullptr;
150 }
151 delete m_connection;
152 m_connection = nullptr;
153
154 delete m_display;
155 m_display = nullptr;
156}
157
158void TestDataDevice::testCreate()
159{
160 using namespace KWin;
161
162 QSignalSpy dataDeviceCreatedSpy(m_dataDeviceManagerInterface, &KWin::DataDeviceManagerInterface::dataDeviceCreated);
163
164 std::unique_ptr<KWayland::Client::DataDevice> dataDevice(m_dataDeviceManager->getDataDevice(m_seat));
165 QVERIFY(dataDevice->isValid());
166
167 QVERIFY(dataDeviceCreatedSpy.wait());
168 QCOMPARE(dataDeviceCreatedSpy.count(), 1);
169 auto deviceInterface = dataDeviceCreatedSpy.first().first().value<DataDeviceInterface *>();
170 QVERIFY(deviceInterface);
171 QCOMPARE(deviceInterface->seat(), m_seatInterface);
172 QVERIFY(!deviceInterface->selection());
173
174 // this will probably fail, we need to make a selection client side
175 QVERIFY(!m_seatInterface->selection());
176 m_seatInterface->setSelection(deviceInterface->selection());
177 QCOMPARE(m_seatInterface->selection(), deviceInterface->selection());
178
179 // and destroy
180 QSignalSpy destroyedSpy(deviceInterface, &QObject::destroyed);
181 dataDevice.reset();
182 QVERIFY(destroyedSpy.wait());
183 QVERIFY(!m_seatInterface->selection());
184}
185
186void TestDataDevice::testDrag_data()
187{
188 QTest::addColumn<bool>("hasGrab");
189 QTest::addColumn<bool>("hasPointerFocus");
190 QTest::addColumn<bool>("success");
191
192 QTest::newRow("grab and focus") << true << true << true;
193 QTest::newRow("no grab") << false << true << false;
194 QTest::newRow("no focus") << true << false << false;
195 QTest::newRow("no grab, no focus") << false << false << false;
196}
197
198void TestDataDevice::testDrag()
199{
200 using namespace KWin;
201 std::unique_ptr<KWayland::Client::Pointer> pointer(m_seat->createPointer());
202
203 QSignalSpy dataDeviceCreatedSpy(m_dataDeviceManagerInterface, &KWin::DataDeviceManagerInterface::dataDeviceCreated);
204
205 std::unique_ptr<KWayland::Client::DataDevice> dataDevice(m_dataDeviceManager->getDataDevice(m_seat));
206 QVERIFY(dataDevice->isValid());
207
208 QVERIFY(dataDeviceCreatedSpy.wait());
209 QCOMPARE(dataDeviceCreatedSpy.count(), 1);
210 auto deviceInterface = dataDeviceCreatedSpy.first().first().value<DataDeviceInterface *>();
211 QVERIFY(deviceInterface);
212
213 QSignalSpy dataSourceCreatedSpy(m_dataDeviceManagerInterface, &KWin::DataDeviceManagerInterface::dataSourceCreated);
214
215 std::unique_ptr<KWayland::Client::DataSource> dataSource(m_dataDeviceManager->createDataSource());
216 QVERIFY(dataSource->isValid());
217
218 QVERIFY(dataSourceCreatedSpy.wait());
219 QCOMPARE(dataSourceCreatedSpy.count(), 1);
220 auto sourceInterface = dataSourceCreatedSpy.first().first().value<DataSourceInterface *>();
221 QVERIFY(sourceInterface);
222
223 QSignalSpy surfaceCreatedSpy(m_compositorInterface, &KWin::CompositorInterface::surfaceCreated);
224
225 std::unique_ptr<KWayland::Client::Surface> surface(m_compositor->createSurface());
226 QVERIFY(surface->isValid());
227
228 QVERIFY(surfaceCreatedSpy.wait());
229 QCOMPARE(surfaceCreatedSpy.count(), 1);
230 auto surfaceInterface = surfaceCreatedSpy.first().first().value<SurfaceInterface *>();
231
232 // now we have all we need to start a drag operation
233 QSignalSpy dragStartedSpy(deviceInterface, &KWin::DataDeviceInterface::dragStarted);
234
235 // first we need to fake the pointer enter
236 QFETCH(bool, hasGrab);
237 QFETCH(bool, hasPointerFocus);
238 QFETCH(bool, success);
239 if (!hasGrab) {
240 // in case we don't have grab, still generate a pointer serial to make it more interesting
241 m_seatInterface->notifyPointerButton(Qt::LeftButton, PointerButtonState::Pressed);
242 m_seatInterface->notifyPointerFrame();
243 }
244 if (hasPointerFocus) {
245 m_seatInterface->notifyPointerEnter(surfaceInterface, QPointF(0, 0));
246 }
247 if (hasGrab) {
248 m_seatInterface->notifyPointerButton(Qt::LeftButton, PointerButtonState::Pressed);
249 m_seatInterface->notifyPointerFrame();
250 }
251
252 // TODO: This test would be better, if it could also test that a client trying to guess
253 // the last serial of a different client can't start a drag.
254 const quint32 pointerButtonSerial = success ? m_seatInterface->pointerButtonSerial(Qt::LeftButton) : 0;
255
256 QCoreApplication::processEvents();
257 // finally start the drag
258 dataDevice->startDrag(pointerButtonSerial, dataSource.get(), surface.get());
259 QCOMPARE(dragStartedSpy.wait(500), success);
260 QCOMPARE(!dragStartedSpy.isEmpty(), success);
261 QCOMPARE(m_seatInterface->dragSource(), success ? sourceInterface : nullptr);
262 QCOMPARE(m_seatInterface->dragSurface(), success ? surfaceInterface : nullptr);
263 QVERIFY(!m_seatInterface->dragIcon());
264}
265
266void TestDataDevice::testDragInternally_data()
267{
268 QTest::addColumn<bool>("hasGrab");
269 QTest::addColumn<bool>("hasPointerFocus");
270 QTest::addColumn<bool>("success");
271
272 QTest::newRow("grab and focus") << true << true << true;
273 QTest::newRow("no grab") << false << true << false;
274 QTest::newRow("no focus") << true << false << false;
275 QTest::newRow("no grab, no focus") << false << false << false;
276}
277
278void TestDataDevice::testDragInternally()
279{
280 using namespace KWin;
281 std::unique_ptr<KWayland::Client::Pointer> pointer(m_seat->createPointer());
282
283 QSignalSpy dataDeviceCreatedSpy(m_dataDeviceManagerInterface, &KWin::DataDeviceManagerInterface::dataDeviceCreated);
284
285 std::unique_ptr<KWayland::Client::DataDevice> dataDevice(m_dataDeviceManager->getDataDevice(m_seat));
286 QVERIFY(dataDevice->isValid());
287
288 QVERIFY(dataDeviceCreatedSpy.wait());
289 QCOMPARE(dataDeviceCreatedSpy.count(), 1);
290 auto deviceInterface = dataDeviceCreatedSpy.first().first().value<DataDeviceInterface *>();
291 QVERIFY(deviceInterface);
292
293 QSignalSpy surfaceCreatedSpy(m_compositorInterface, &KWin::CompositorInterface::surfaceCreated);
294
295 std::unique_ptr<KWayland::Client::Surface> surface(m_compositor->createSurface());
296 QVERIFY(surface->isValid());
297
298 QVERIFY(surfaceCreatedSpy.wait());
299 QCOMPARE(surfaceCreatedSpy.count(), 1);
300 auto surfaceInterface = surfaceCreatedSpy.first().first().value<SurfaceInterface *>();
301
302 std::unique_ptr<KWayland::Client::Surface> iconSurface(m_compositor->createSurface());
303 QVERIFY(iconSurface->isValid());
304
305 QVERIFY(surfaceCreatedSpy.wait());
306 QCOMPARE(surfaceCreatedSpy.count(), 2);
307 auto iconSurfaceInterface = surfaceCreatedSpy.last().first().value<SurfaceInterface *>();
308
309 // now we have all we need to start a drag operation
310 QSignalSpy dragStartedSpy(deviceInterface, &KWin::DataDeviceInterface::dragStarted);
311
312 // first we need to fake the pointer enter
313 QFETCH(bool, hasGrab);
314 QFETCH(bool, hasPointerFocus);
315 QFETCH(bool, success);
316 if (!hasGrab) {
317 // in case we don't have grab, still generate a pointer serial to make it more interesting
318 m_seatInterface->notifyPointerButton(Qt::LeftButton, PointerButtonState::Pressed);
319 m_seatInterface->notifyPointerFrame();
320 }
321 if (hasPointerFocus) {
322 m_seatInterface->notifyPointerEnter(surfaceInterface, QPointF(0, 0));
323 }
324 if (hasGrab) {
325 m_seatInterface->notifyPointerButton(Qt::LeftButton, PointerButtonState::Pressed);
326 m_seatInterface->notifyPointerFrame();
327 }
328
329 // TODO: This test would be better, if it could also test that a client trying to guess
330 // the last serial of a different client can't start a drag.
331 const quint32 pointerButtonSerial = success ? m_seatInterface->pointerButtonSerial(Qt::LeftButton) : 0;
332
333 QCoreApplication::processEvents();
334 // finally start the internal drag
335 dataDevice->startDragInternally(pointerButtonSerial, surface.get(), iconSurface.get());
336 QCOMPARE(dragStartedSpy.wait(500), success);
337 QCOMPARE(!dragStartedSpy.isEmpty(), success);
338 QVERIFY(!m_seatInterface->dragSource());
339 QCOMPARE(m_seatInterface->dragSurface(), success ? surfaceInterface : nullptr);
340
341 if (success) {
342 QCOMPARE(m_seatInterface->dragIcon()->surface(), iconSurfaceInterface);
343 } else {
344 QCOMPARE(m_seatInterface->dragIcon(), nullptr);
345 }
346}
347
348void TestDataDevice::testSetSelection()
349{
350 using namespace KWin;
351 std::unique_ptr<KWayland::Client::Pointer> pointer(m_seat->createPointer());
352
353 QSignalSpy dataDeviceCreatedSpy(m_dataDeviceManagerInterface, &KWin::DataDeviceManagerInterface::dataDeviceCreated);
354
355 std::unique_ptr<KWayland::Client::DataDevice> dataDevice(m_dataDeviceManager->getDataDevice(m_seat));
356 QVERIFY(dataDevice->isValid());
357
358 QVERIFY(dataDeviceCreatedSpy.wait());
359 QCOMPARE(dataDeviceCreatedSpy.count(), 1);
360 auto deviceInterface = dataDeviceCreatedSpy.first().first().value<DataDeviceInterface *>();
361 QVERIFY(deviceInterface);
362
363 QSignalSpy dataSourceCreatedSpy(m_dataDeviceManagerInterface, &KWin::DataDeviceManagerInterface::dataSourceCreated);
364
365 std::unique_ptr<KWayland::Client::DataSource> dataSource(m_dataDeviceManager->createDataSource());
366 QVERIFY(dataSource->isValid());
367 dataSource->offer(QStringLiteral("text/plain"));
368
369 QVERIFY(dataSourceCreatedSpy.wait());
370 QCOMPARE(dataSourceCreatedSpy.count(), 1);
371 auto sourceInterface = dataSourceCreatedSpy.first().first().value<DataSourceInterface *>();
372 QVERIFY(sourceInterface);
373
374 // everything setup, now we can test setting the selection
375 QSignalSpy selectionChangedSpy(deviceInterface, &KWin::DataDeviceInterface::selectionChanged);
376
377 QVERIFY(!deviceInterface->selection());
378 dataDevice->setSelection(1, dataSource.get());
379 QVERIFY(selectionChangedSpy.wait());
380 QCOMPARE(selectionChangedSpy.count(), 1);
381 QCOMPARE(selectionChangedSpy.first().first().value<DataSourceInterface *>(), sourceInterface);
382 QCOMPARE(deviceInterface->selection(), sourceInterface);
383
384 // send selection to datadevice
385 QSignalSpy selectionOfferedSpy(dataDevice.get(), &KWayland::Client::DataDevice::selectionOffered);
386 deviceInterface->sendSelection(deviceInterface->selection());
387 QVERIFY(selectionOfferedSpy.wait());
388 QCOMPARE(selectionOfferedSpy.count(), 1);
389 auto dataOffer = selectionOfferedSpy.first().first().value<KWayland::Client::DataOffer *>();
390 QVERIFY(dataOffer);
391 QCOMPARE(dataOffer->offeredMimeTypes().count(), 1);
392 QCOMPARE(dataOffer->offeredMimeTypes().first().name(), QStringLiteral("text/plain"));
393
394 // sending a new mimetype to the selection, should be announced in the offer
395 QSignalSpy mimeTypeAddedSpy(dataOffer, &KWayland::Client::DataOffer::mimeTypeOffered);
396 dataSource->offer(QStringLiteral("text/html"));
397 QVERIFY(mimeTypeAddedSpy.wait());
398 QCOMPARE(mimeTypeAddedSpy.count(), 1);
399 QCOMPARE(mimeTypeAddedSpy.first().first().toString(), QStringLiteral("text/html"));
400 QCOMPARE(dataOffer->offeredMimeTypes().count(), 2);
401 QCOMPARE(dataOffer->offeredMimeTypes().first().name(), QStringLiteral("text/plain"));
402 QCOMPARE(dataOffer->offeredMimeTypes().last().name(), QStringLiteral("text/html"));
403
404 // now clear the selection
405 dataDevice->clearSelection(1);
406 QVERIFY(selectionChangedSpy.wait());
407 QCOMPARE(selectionChangedSpy.count(), 2);
408 QVERIFY(!deviceInterface->selection());
409
410 // set another selection
411 dataDevice->setSelection(2, dataSource.get());
412 QVERIFY(selectionChangedSpy.wait());
413 // now unbind the dataDevice
414 QSignalSpy unboundSpy(deviceInterface, &QObject::destroyed);
415 dataDevice.reset();
416 QVERIFY(unboundSpy.wait());
417}
418
419void TestDataDevice::testSendSelectionOnSeat()
420{
421 // this test verifies that the selection is sent when setting a focused keyboard
422 using namespace KWin;
423 // first add keyboard support to Seat
424 QSignalSpy keyboardChangedSpy(m_seat, &KWayland::Client::Seat::hasKeyboardChanged);
425 m_seatInterface->setHasKeyboard(true);
426 QVERIFY(keyboardChangedSpy.wait());
427 // now create DataDevice, Keyboard and a Surface
428 QSignalSpy dataDeviceCreatedSpy(m_dataDeviceManagerInterface, &DataDeviceManagerInterface::dataDeviceCreated);
429 std::unique_ptr<KWayland::Client::DataDevice> dataDevice(m_dataDeviceManager->getDataDevice(m_seat));
430 QVERIFY(dataDevice->isValid());
431 QVERIFY(dataDeviceCreatedSpy.wait());
432 auto serverDataDevice = dataDeviceCreatedSpy.first().first().value<DataDeviceInterface *>();
433 QVERIFY(serverDataDevice);
434 std::unique_ptr<KWayland::Client::Keyboard> keyboard(m_seat->createKeyboard());
435 QVERIFY(keyboard->isValid());
436 QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
437 std::unique_ptr<KWayland::Client::Surface> surface(m_compositor->createSurface());
438 QVERIFY(surface->isValid());
439 QVERIFY(surfaceCreatedSpy.wait());
440
441 auto serverSurface = surfaceCreatedSpy.first().first().value<SurfaceInterface *>();
442 QVERIFY(serverSurface);
443 m_seatInterface->setFocusedKeyboardSurface(serverSurface);
444
445 // now set the selection
446 std::unique_ptr<KWayland::Client::DataSource> dataSource(m_dataDeviceManager->createDataSource());
447 QVERIFY(dataSource->isValid());
448 dataSource->offer(QStringLiteral("text/plain"));
449 dataDevice->setSelection(1, dataSource.get());
450 // we should get a selection offered for that on the data device
451 QSignalSpy selectionOfferedSpy(dataDevice.get(), &KWayland::Client::DataDevice::selectionOffered);
452 QVERIFY(selectionOfferedSpy.wait());
453 QCOMPARE(selectionOfferedSpy.count(), 1);
454
455 // now unfocus the keyboard
456 m_seatInterface->setFocusedKeyboardSurface(nullptr);
457 // if setting the same surface again, we should get another offer
458 m_seatInterface->setFocusedKeyboardSurface(serverSurface);
459 QVERIFY(selectionOfferedSpy.wait());
460 QCOMPARE(selectionOfferedSpy.count(), 2);
461
462 // now let's try to destroy the data device and set a focused keyboard just while the data device is being destroyedd
463 m_seatInterface->setFocusedKeyboardSurface(nullptr);
464 QSignalSpy unboundSpy(serverDataDevice, &QObject::destroyed);
465 dataDevice.reset();
466 QVERIFY(unboundSpy.wait());
467 m_seatInterface->setFocusedKeyboardSurface(serverSurface);
468}
469
470void TestDataDevice::testReplaceSource()
471{
472 // this test verifies that replacing a data source cancels the previous source
473 using namespace KWin;
474 // first add keyboard support to Seat
475 QSignalSpy keyboardChangedSpy(m_seat, &KWayland::Client::Seat::hasKeyboardChanged);
476 m_seatInterface->setHasKeyboard(true);
477 QVERIFY(keyboardChangedSpy.wait());
478 // now create DataDevice, Keyboard and a Surface
479 QSignalSpy dataDeviceCreatedSpy(m_dataDeviceManagerInterface, &DataDeviceManagerInterface::dataDeviceCreated);
480 std::unique_ptr<KWayland::Client::DataDevice> dataDevice(m_dataDeviceManager->getDataDevice(m_seat));
481 QVERIFY(dataDevice->isValid());
482 QVERIFY(dataDeviceCreatedSpy.wait());
483 auto serverDataDevice = dataDeviceCreatedSpy.first().first().value<DataDeviceInterface *>();
484 QVERIFY(serverDataDevice);
485 std::unique_ptr<KWayland::Client::Keyboard> keyboard(m_seat->createKeyboard());
486 QVERIFY(keyboard->isValid());
487 QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
488 std::unique_ptr<KWayland::Client::Surface> surface(m_compositor->createSurface());
489 QVERIFY(surface->isValid());
490 QVERIFY(surfaceCreatedSpy.wait());
491
492 auto serverSurface = surfaceCreatedSpy.first().first().value<SurfaceInterface *>();
493 QVERIFY(serverSurface);
494 m_seatInterface->setFocusedKeyboardSurface(serverSurface);
495
496 // now set the selection
497 std::unique_ptr<KWayland::Client::DataSource> dataSource(m_dataDeviceManager->createDataSource());
498 QVERIFY(dataSource->isValid());
499 dataSource->offer(QStringLiteral("text/plain"));
500 dataDevice->setSelection(1, dataSource.get());
501 QSignalSpy sourceCancelledSpy(dataSource.get(), &KWayland::Client::DataSource::cancelled);
502 // we should get a selection offered for that on the data device
503 QSignalSpy selectionOfferedSpy(dataDevice.get(), &KWayland::Client::DataDevice::selectionOffered);
504 QVERIFY(selectionOfferedSpy.wait());
505 QCOMPARE(selectionOfferedSpy.count(), 1);
506
507 // create a second data source and replace previous one
508 std::unique_ptr<KWayland::Client::DataSource> dataSource2(m_dataDeviceManager->createDataSource());
509 QVERIFY(dataSource2->isValid());
510 dataSource2->offer(QStringLiteral("text/plain"));
511 QSignalSpy sourceCancelled2Spy(dataSource2.get(), &KWayland::Client::DataSource::cancelled);
512 dataDevice->setSelection(1, dataSource2.get());
513 QCOMPARE(selectionOfferedSpy.count(), 1);
514 QVERIFY(sourceCancelledSpy.wait());
515 QCOMPARE(selectionOfferedSpy.count(), 2);
516 QVERIFY(sourceCancelled2Spy.isEmpty());
517
518 // replace the data source with itself, ensure that it did not get cancelled
519 dataDevice->setSelection(1, dataSource2.get());
520 QVERIFY(!sourceCancelled2Spy.wait(500));
521 QCOMPARE(selectionOfferedSpy.count(), 2);
522 QVERIFY(sourceCancelled2Spy.isEmpty());
523
524 // create a new DataDevice and replace previous one
525 std::unique_ptr<KWayland::Client::DataDevice> dataDevice2(m_dataDeviceManager->getDataDevice(m_seat));
526 QVERIFY(dataDevice2->isValid());
527 std::unique_ptr<KWayland::Client::DataSource> dataSource3(m_dataDeviceManager->createDataSource());
528 QVERIFY(dataSource3->isValid());
529 dataSource3->offer(QStringLiteral("text/plain"));
530 dataDevice2->setSelection(1, dataSource3.get());
531 QVERIFY(sourceCancelled2Spy.wait());
532
533 // try to crash by first destroying dataSource3 and setting a new DataSource
534 std::unique_ptr<KWayland::Client::DataSource> dataSource4(m_dataDeviceManager->createDataSource());
535 QVERIFY(dataSource4->isValid());
536 dataSource4->offer(QStringLiteral("text/plain"));
537 dataSource3.reset();
538 dataDevice2->setSelection(1, dataSource4.get());
539 QVERIFY(selectionOfferedSpy.wait());
540
541 auto dataOffer = selectionOfferedSpy.last()[0].value<KWayland::Client::DataOffer *>();
542
543 // try to crash by destroying the data source, then requesting data
544 dataSource4.reset();
545 int pipeFds[2] = {0, 0};
546 QVERIFY(pipe(pipeFds) == 0);
547
548 dataOffer->receive(QStringLiteral("text/plain"), pipeFds[1]);
549 close(pipeFds[1]);
550
551 // spin the event loop, nothing should explode
552 QTest::qWait(10);
553
554 close(pipeFds[0]);
555}
556
557QTEST_GUILESS_MAIN(TestDataDevice)
558#include "test_datadevice.moc"
void surfaceCreated(KWin::SurfaceInterface *surface)
DataDeviceInterface allows clients to share data by copy-and-paste and drag-and-drop.
Definition datadevice.h:80
void selectionChanged(KWin::DataSourceInterface *)
void dragStarted(AbstractDataSource *source, SurfaceInterface *originSurface, quint32 serial, DragAndDropIcon *dragIcon)
Represents the Global for wl_data_device_manager interface.
void dataDeviceCreated(KWin::DataDeviceInterface *)
void dataSourceCreated(KWin::DataSourceInterface *)
Represents the Resource for the wl_data_source interface.
Definition datasource.h:23
Class holding the Wayland server display loop.
Definition display.h:34
bool addSocketName(const QString &name=QString())
Definition display.cpp:68
bool isRunning() const
Definition display.cpp:144
bool start()
Definition display.cpp:92
SurfaceInterface * surface() const
Represents a Seat on the Wayland Display.
Definition seat.h:134
quint32 pointerButtonSerial(quint32 button) const
Definition seat.cpp:757
void setHasKeyboard(bool has)
Definition seat.cpp:342
SurfaceInterface * dragSurface() const
Definition seat.cpp:1213
void setSelection(AbstractDataSource *selection)
Definition seat.cpp:1283
void setHasPointer(bool has)
Definition seat.cpp:357
void notifyPointerEnter(SurfaceInterface *surface, const QPointF &position, const QPointF &surfacePosition=QPointF())
Definition seat.cpp:555
void notifyPointerFrame()
Definition seat.cpp:744
AbstractDataSource * selection() const
Definition seat.cpp:1278
void notifyPointerButton(quint32 button, PointerButtonState state)
Definition seat.cpp:712
void setFocusedKeyboardSurface(SurfaceInterface *surface)
Definition seat.cpp:915
DragAndDropIcon * dragIcon() const
Definition seat.cpp:1387
AbstractDataSource * dragSource() const
Definition seat.cpp:1218
Resource representing a wl_surface.
Definition surface.h:80