13#include "wayland/datasource.h"
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"
31using namespace std::literals;
41 void testPointerDragAndDrop();
42 void testTouchDragAndDrop();
43 void testDragAndDropWithCancelByDestroyDataSource();
44 void testPointerEventsIgnored();
47 KWayland::Client::Surface *createSurface();
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;
68static const QString s_socketName = QStringLiteral(
"kwayland-test-wayland-drag-n-drop-0");
70void TestDragAndDrop::init()
80 m_connection =
new KWayland::Client::ConnectionThread;
81 QSignalSpy connectedSpy(m_connection, &KWayland::Client::ConnectionThread::connected);
82 m_connection->setSocketName(s_socketName);
91 m_thread =
new QThread(
this);
92 m_connection->moveToThread(m_thread);
95 m_connection->initConnection();
96 QVERIFY(connectedSpy.wait());
98 m_queue =
new KWayland::Client::EventQueue(
this);
99 QVERIFY(!m_queue->isValid());
100 m_queue->setup(m_connection);
101 QVERIFY(m_queue->isValid());
103 m_registry =
new KWayland::Client::Registry();
104 QSignalSpy interfacesAnnouncedSpy(m_registry, &KWayland::Client::Registry::interfaceAnnounced);
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());
113 QVERIFY(interfacesAnnouncedSpy.wait());
114#define CREATE(variable, factory, iface) \
116 m_registry->create##factory(m_registry->interface(KWayland::Client::Registry::Interface::iface).name, m_registry->interface(KWayland::Client::Registry::Interface::iface).version, this); \
120 CREATE(m_seat, Seat, Seat)
121 CREATE(m_ddm, DataDeviceManager, DataDeviceManager)
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"));
139void TestDragAndDrop::cleanup()
141#define DELETE(name) \
162 m_connection =
nullptr;
168KWayland::Client::Surface *TestDragAndDrop::createSurface()
170 auto s = m_compositor->createSurface();
172 QImage img(QSize(100, 200), QImage::Format_RGB32);
174 s->attachBuffer(m_shm->createBuffer(img));
175 s->damage(QRect(0, 0, 100, 200));
176 s->commit(KWayland::Client::Surface::CommitFlag::None);
182 using namespace KWin;
183 QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
184 if (!surfaceCreatedSpy.isValid()) {
187 if (!surfaceCreatedSpy.wait(500)) {
193void TestDragAndDrop::testPointerDragAndDrop()
196 using namespace KWin;
198 std::unique_ptr<KWayland::Client::Surface> s(createSurface());
199 auto serverSurface = getServerSurface();
200 QVERIFY(serverSurface);
202 QSignalSpy dataSourceSelectedActionChangedSpy(m_dataSource, &KWayland::Client::DataSource::selectedDragAndDropActionChanged);
204 auto timestamp = 2ms;
207 QSignalSpy buttonPressSpy(m_pointer, &KWayland::Client::Pointer::buttonStateChanged);
212 QVERIFY(buttonPressSpy.wait());
213 QCOMPARE(buttonPressSpy.first().at(1).value<quint32>(), quint32(2));
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);
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);
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();
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);
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);
260 QSignalSpy serverDragEndedSpy(m_seatInterface, &SeatInterface::dragEnded);
261 QSignalSpy droppedSpy(m_dataDevice, &KWayland::Client::DataDevice::dropped);
265 QVERIFY(sourceDropSpy.isEmpty());
266 QVERIFY(droppedSpy.wait());
267 QCOMPARE(sourceDropSpy.count(), 1);
268 QCOMPARE(serverDragEndedSpy.count(), 1);
270 QSignalSpy finishedSpy(m_dataSource, &KWayland::Client::DataSource::dragAndDropFinished);
271 offer->dragAndDropFinished();
272 QVERIFY(finishedSpy.wait());
276 QVERIFY(pointerMotionSpy.isEmpty());
279void TestDragAndDrop::testTouchDragAndDrop()
282 using namespace KWin;
284 std::unique_ptr<KWayland::Client::Surface> s(createSurface());
285 s->setSize(QSize(100, 100));
286 auto serverSurface = getServerSurface();
287 QVERIFY(serverSurface);
289 QSignalSpy dataSourceSelectedActionChangedSpy(m_dataSource, &KWayland::Client::DataSource::selectedDragAndDropActionChanged);
291 auto timestamp = 2ms;
294 QSignalSpy sequenceStartedSpy(m_touch, &KWayland::Client::Touch::sequenceStarted);
295 QSignalSpy pointAddedSpy(m_touch, &KWayland::Client::Touch::pointAdded);
298 const qint32 touchId = 0;
300 QVERIFY(sequenceStartedSpy.wait());
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));
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);
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);
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();
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);
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);
350 QSignalSpy serverDragEndedSpy(m_seatInterface, &SeatInterface::dragEnded);
351 QSignalSpy droppedSpy(m_dataDevice, &KWayland::Client::DataDevice::dropped);
354 QVERIFY(sourceDropSpy.isEmpty());
355 QVERIFY(droppedSpy.wait());
356 QCOMPARE(sourceDropSpy.count(), 1);
357 QCOMPARE(serverDragEndedSpy.count(), 1);
359 QSignalSpy finishedSpy(m_dataSource, &KWayland::Client::DataSource::dragAndDropFinished);
360 offer->dragAndDropFinished();
361 QVERIFY(finishedSpy.wait());
365 QVERIFY(touchMotionSpy.isEmpty());
366 QCOMPARE(pointAddedSpy.count(), 0);
369void TestDragAndDrop::testDragAndDropWithCancelByDestroyDataSource()
372 using namespace KWin;
374 std::unique_ptr<KWayland::Client::Surface> s(createSurface());
375 auto serverSurface = getServerSurface();
376 QVERIFY(serverSurface);
378 QSignalSpy dataSourceSelectedActionChangedSpy(m_dataSource, &KWayland::Client::DataSource::selectedDragAndDropActionChanged);
380 auto timestamp = 2ms;
383 QSignalSpy buttonPressSpy(m_pointer, &KWayland::Client::Pointer::buttonStateChanged);
388 QVERIFY(buttonPressSpy.wait());
389 QCOMPARE(buttonPressSpy.first().at(1).value<quint32>(), quint32(2));
391 QSignalSpy pointerLeftSpy(m_pointer, &KWayland::Client::Pointer::left);
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);
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());
405 QCOMPARE(m_seatInterface->
dragSurface(), serverSurface);
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();
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);
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);
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);
449 QSignalSpy droppedSpy(m_dataDevice, &KWayland::Client::DataDevice::dropped);
453 QVERIFY(!droppedSpy.wait(500));
456 QVERIFY(pointerMotionSpy.isEmpty());
459void TestDragAndDrop::testPointerEventsIgnored()
462 using namespace KWin;
464 std::unique_ptr<KWayland::Client::Surface> s(createSurface());
465 auto serverSurface = getServerSurface();
466 QVERIFY(serverSurface);
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);
480 auto timestamp = 1ms;
484 m_seatInterface->
notifyPointerAxis(Qt::Vertical, 5, 120, PointerAxisSource::Wheel);
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());
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());
511 m_seatInterface->
notifyPointerAxis(Qt::Vertical, 5, 1, PointerAxisSource::Wheel);
514 m_seatInterface->
notifyPointerAxis(Qt::Vertical, 5, 1, PointerAxisSource::Wheel);
527 QSignalSpy cancelledSpy(m_dataSource, &KWayland::Client::DataSource::cancelled);
531 QVERIFY(cancelledSpy.wait());
534 QCOMPARE(axisSpy.count(), 1);
535 QCOMPARE(pointerMotionSpy.count(), 1);
536 QCOMPARE(pointerEnteredSpy.count(), 1);
537 QCOMPARE(pointerLeftSpy.count(), 1);
541#include "test_drag_drop.moc"
Represents the Global for wl_data_device_manager interface.
Class holding the Wayland server display loop.
bool addSocketName(const QString &name=QString())
Represents a Seat on the Wayland Display.
void setFocusedTouchSurface(SurfaceInterface *surface, const QPointF &surfacePosition=QPointF())
void setHasTouch(bool has)
QPointF pointerPos() const
void setTimestamp(std::chrono::microseconds time)
SurfaceInterface * dragSurface() const
void notifyPointerMotion(const QPointF &pos)
void setHasPointer(bool has)
void notifyTouchMotion(qint32 id, const QPointF &globalPosition)
void notifyPointerEnter(SurfaceInterface *surface, const QPointF &position, const QPointF &surfacePosition=QPointF())
void notifyPointerLeave()
void notifyPointerAxis(Qt::Orientation orientation, qreal delta, qint32 deltaV120, PointerAxisSource source, PointerAxisRelativeDirection direction=PointerAxisRelativeDirection::Normal)
void notifyPointerFrame()
void notifyTouchUp(qint32 id)
QMatrix4x4 dragSurfaceTransformation() const
void notifyPointerButton(quint32 button, PointerButtonState state)
DragAndDropIcon * dragIcon() const
void notifyTouchDown(qint32 id, const QPointF &globalPosition)
Resource representing a wl_surface.
#define CREATE(variable, factory, iface)