File indexing completed on 2024-11-10 04:56:15

0001 /*
0002     SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005 */
0006 // Qt
0007 #include <QMimeType>
0008 #include <QSignalSpy>
0009 #include <QTest>
0010 // KWin
0011 #include "wayland/compositor.h"
0012 #include "wayland/datadevicemanager.h"
0013 #include "wayland/datasource.h"
0014 #include "wayland/display.h"
0015 #include "wayland/seat.h"
0016 #include "wayland/seat_p.h"
0017 
0018 #include "KWayland/Client/compositor.h"
0019 #include "KWayland/Client/connection_thread.h"
0020 #include "KWayland/Client/datadevice.h"
0021 #include "KWayland/Client/datadevicemanager.h"
0022 #include "KWayland/Client/datasource.h"
0023 #include "KWayland/Client/event_queue.h"
0024 #include "KWayland/Client/pointer.h"
0025 #include "KWayland/Client/registry.h"
0026 #include "KWayland/Client/seat.h"
0027 #include "KWayland/Client/shm_pool.h"
0028 #include "KWayland/Client/surface.h"
0029 #include "KWayland/Client/touch.h"
0030 
0031 using namespace std::literals;
0032 
0033 class TestDragAndDrop : public QObject
0034 {
0035     Q_OBJECT
0036 
0037 private Q_SLOTS:
0038     void init();
0039     void cleanup();
0040 
0041     void testPointerDragAndDrop();
0042     void testTouchDragAndDrop();
0043     void testDragAndDropWithCancelByDestroyDataSource();
0044     void testPointerEventsIgnored();
0045 
0046 private:
0047     KWayland::Client::Surface *createSurface();
0048     KWin::SurfaceInterface *getServerSurface();
0049 
0050     KWin::Display *m_display = nullptr;
0051     KWin::CompositorInterface *m_compositorInterface = nullptr;
0052     KWin::DataDeviceManagerInterface *m_dataDeviceManagerInterface = nullptr;
0053     KWin::SeatInterface *m_seatInterface = nullptr;
0054     KWayland::Client::ConnectionThread *m_connection = nullptr;
0055     KWayland::Client::Compositor *m_compositor = nullptr;
0056     KWayland::Client::EventQueue *m_queue = nullptr;
0057     KWayland::Client::DataDevice *m_dataDevice = nullptr;
0058     KWayland::Client::DataSource *m_dataSource = nullptr;
0059     QThread *m_thread = nullptr;
0060     KWayland::Client::Registry *m_registry = nullptr;
0061     KWayland::Client::Seat *m_seat = nullptr;
0062     KWayland::Client::Pointer *m_pointer = nullptr;
0063     KWayland::Client::Touch *m_touch = nullptr;
0064     KWayland::Client::DataDeviceManager *m_ddm = nullptr;
0065     KWayland::Client::ShmPool *m_shm = nullptr;
0066 };
0067 
0068 static const QString s_socketName = QStringLiteral("kwayland-test-wayland-drag-n-drop-0");
0069 
0070 void TestDragAndDrop::init()
0071 {
0072     using namespace KWin;
0073     delete m_display;
0074     m_display = new KWin::Display(this);
0075     m_display->addSocketName(s_socketName);
0076     m_display->start();
0077     QVERIFY(m_display->isRunning());
0078 
0079     // setup connection
0080     m_connection = new KWayland::Client::ConnectionThread;
0081     QSignalSpy connectedSpy(m_connection, &KWayland::Client::ConnectionThread::connected);
0082     m_connection->setSocketName(s_socketName);
0083 
0084     m_compositorInterface = new CompositorInterface(m_display, m_display);
0085     m_seatInterface = new SeatInterface(m_display, m_display);
0086     m_seatInterface->setHasPointer(true);
0087     m_seatInterface->setHasTouch(true);
0088     m_dataDeviceManagerInterface = new DataDeviceManagerInterface(m_display, m_display);
0089     m_display->createShm();
0090 
0091     m_thread = new QThread(this);
0092     m_connection->moveToThread(m_thread);
0093     m_thread->start();
0094 
0095     m_connection->initConnection();
0096     QVERIFY(connectedSpy.wait());
0097 
0098     m_queue = new KWayland::Client::EventQueue(this);
0099     QVERIFY(!m_queue->isValid());
0100     m_queue->setup(m_connection);
0101     QVERIFY(m_queue->isValid());
0102 
0103     m_registry = new KWayland::Client::Registry();
0104     QSignalSpy interfacesAnnouncedSpy(m_registry, &KWayland::Client::Registry::interfaceAnnounced);
0105 
0106     QVERIFY(!m_registry->eventQueue());
0107     m_registry->setEventQueue(m_queue);
0108     QCOMPARE(m_registry->eventQueue(), m_queue);
0109     m_registry->create(m_connection);
0110     QVERIFY(m_registry->isValid());
0111     m_registry->setup();
0112 
0113     QVERIFY(interfacesAnnouncedSpy.wait());
0114 #define CREATE(variable, factory, iface)                                                                                                                      \
0115     variable =                                                                                                                                                \
0116         m_registry->create##factory(m_registry->interface(KWayland::Client::Registry::Interface::iface).name, m_registry->interface(KWayland::Client::Registry::Interface::iface).version, this); \
0117     QVERIFY(variable);
0118 
0119     CREATE(m_compositor, Compositor, Compositor)
0120     CREATE(m_seat, Seat, Seat)
0121     CREATE(m_ddm, DataDeviceManager, DataDeviceManager)
0122     CREATE(m_shm, ShmPool, Shm)
0123 
0124 #undef CREATE
0125 
0126     QSignalSpy pointerSpy(m_seat, &KWayland::Client::Seat::hasPointerChanged);
0127     QVERIFY(pointerSpy.wait());
0128     m_pointer = m_seat->createPointer(m_seat);
0129     QVERIFY(m_pointer->isValid());
0130     m_touch = m_seat->createTouch(m_seat);
0131     QVERIFY(m_touch->isValid());
0132     m_dataDevice = m_ddm->getDataDevice(m_seat, this);
0133     QVERIFY(m_dataDevice->isValid());
0134     m_dataSource = m_ddm->createDataSource(this);
0135     QVERIFY(m_dataSource->isValid());
0136     m_dataSource->offer(QStringLiteral("text/plain"));
0137 }
0138 
0139 void TestDragAndDrop::cleanup()
0140 {
0141 #define DELETE(name)    \
0142     if (name) {         \
0143         delete name;    \
0144         name = nullptr; \
0145     }
0146     DELETE(m_dataSource)
0147     DELETE(m_dataDevice)
0148     DELETE(m_shm)
0149     DELETE(m_compositor)
0150     DELETE(m_ddm)
0151     DELETE(m_seat)
0152     DELETE(m_queue)
0153     DELETE(m_registry)
0154 #undef DELETE
0155     if (m_thread) {
0156         m_thread->quit();
0157         m_thread->wait();
0158         delete m_thread;
0159         m_thread = nullptr;
0160     }
0161     delete m_connection;
0162     m_connection = nullptr;
0163 
0164     delete m_display;
0165     m_display = nullptr;
0166 }
0167 
0168 KWayland::Client::Surface *TestDragAndDrop::createSurface()
0169 {
0170     auto s = m_compositor->createSurface();
0171 
0172     QImage img(QSize(100, 200), QImage::Format_RGB32);
0173     img.fill(Qt::red);
0174     s->attachBuffer(m_shm->createBuffer(img));
0175     s->damage(QRect(0, 0, 100, 200));
0176     s->commit(KWayland::Client::Surface::CommitFlag::None);
0177     return s;
0178 }
0179 
0180 KWin::SurfaceInterface *TestDragAndDrop::getServerSurface()
0181 {
0182     using namespace KWin;
0183     QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
0184     if (!surfaceCreatedSpy.isValid()) {
0185         return nullptr;
0186     }
0187     if (!surfaceCreatedSpy.wait(500)) {
0188         return nullptr;
0189     }
0190     return surfaceCreatedSpy.first().first().value<SurfaceInterface *>();
0191 }
0192 
0193 void TestDragAndDrop::testPointerDragAndDrop()
0194 {
0195     // this test verifies the very basic drag and drop on one surface, an enter, a move and the drop
0196     using namespace KWin;
0197     // first create a window
0198     std::unique_ptr<KWayland::Client::Surface> s(createSurface());
0199     auto serverSurface = getServerSurface();
0200     QVERIFY(serverSurface);
0201 
0202     QSignalSpy dataSourceSelectedActionChangedSpy(m_dataSource, &KWayland::Client::DataSource::selectedDragAndDropActionChanged);
0203 
0204     auto timestamp = 2ms;
0205 
0206     // now we need to pass pointer focus to the Surface and simulate a button press
0207     QSignalSpy buttonPressSpy(m_pointer, &KWayland::Client::Pointer::buttonStateChanged);
0208     m_seatInterface->setTimestamp(timestamp++);
0209     m_seatInterface->notifyPointerEnter(serverSurface, QPointF(0, 0));
0210     m_seatInterface->notifyPointerButton(1, PointerButtonState::Pressed);
0211     m_seatInterface->notifyPointerFrame();
0212     QVERIFY(buttonPressSpy.wait());
0213     QCOMPARE(buttonPressSpy.first().at(1).value<quint32>(), quint32(2));
0214 
0215     // add some signal spies for client side
0216     QSignalSpy dragEnteredSpy(m_dataDevice, &KWayland::Client::DataDevice::dragEntered);
0217     QSignalSpy dragMotionSpy(m_dataDevice, &KWayland::Client::DataDevice::dragMotion);
0218     QSignalSpy pointerMotionSpy(m_pointer, &KWayland::Client::Pointer::motion);
0219     QSignalSpy sourceDropSpy(m_dataSource, &KWayland::Client::DataSource::dragAndDropPerformed);
0220 
0221     // now we can start the drag and drop
0222     QSignalSpy dragStartedSpy(m_seatInterface, &SeatInterface::dragStarted);
0223     m_dataSource->setDragAndDropActions(KWayland::Client::DataDeviceManager::DnDAction::Copy | KWayland::Client::DataDeviceManager::DnDAction::Move);
0224     m_dataDevice->startDrag(buttonPressSpy.first().first().value<quint32>(), m_dataSource, s.get());
0225     QVERIFY(dragStartedSpy.wait());
0226     QCOMPARE(m_seatInterface->dragSurface(), serverSurface);
0227     QCOMPARE(m_seatInterface->dragSurfaceTransformation(), QMatrix4x4());
0228     QVERIFY(!m_seatInterface->dragIcon());
0229     QCOMPARE(SeatInterfacePrivate::get(m_seatInterface)->drag.dragImplicitGrabSerial, buttonPressSpy.first().first().value<quint32>());
0230     QVERIFY(dragEnteredSpy.wait());
0231     QCOMPARE(dragEnteredSpy.count(), 1);
0232     QCOMPARE(dragEnteredSpy.first().first().value<quint32>(), m_display->serial());
0233     QCOMPARE(dragEnteredSpy.first().last().toPointF(), QPointF(0, 0));
0234     QCOMPARE(m_dataDevice->dragSurface().data(), s.get());
0235     auto offer = m_dataDevice->dragOffer();
0236     QVERIFY(offer);
0237     QCOMPARE(offer->selectedDragAndDropAction(), KWayland::Client::DataDeviceManager::DnDAction::None);
0238     QSignalSpy offerActionChangedSpy(offer, &KWayland::Client::DataOffer::selectedDragAndDropActionChanged);
0239     QCOMPARE(m_dataDevice->dragOffer()->offeredMimeTypes().count(), 1);
0240     QCOMPARE(m_dataDevice->dragOffer()->offeredMimeTypes().first().name(), QStringLiteral("text/plain"));
0241     QTRY_COMPARE(offer->sourceDragAndDropActions(), KWayland::Client::DataDeviceManager::DnDAction::Copy | KWayland::Client::DataDeviceManager::DnDAction::Move);
0242     offer->accept(QStringLiteral("text/plain"), dragEnteredSpy.last().at(0).toUInt());
0243     offer->setDragAndDropActions(KWayland::Client::DataDeviceManager::DnDAction::Copy | KWayland::Client::DataDeviceManager::DnDAction::Move, KWayland::Client::DataDeviceManager::DnDAction::Move);
0244     QVERIFY(offerActionChangedSpy.wait());
0245     QCOMPARE(offerActionChangedSpy.count(), 1);
0246     QCOMPARE(offer->selectedDragAndDropAction(), KWayland::Client::DataDeviceManager::DnDAction::Move);
0247     QCOMPARE(dataSourceSelectedActionChangedSpy.count(), 1);
0248     QCOMPARE(m_dataSource->selectedDragAndDropAction(), KWayland::Client::DataDeviceManager::DnDAction::Move);
0249 
0250     // simulate motion
0251     m_seatInterface->setTimestamp(timestamp++);
0252     m_seatInterface->notifyPointerMotion(QPointF(3, 3));
0253     m_seatInterface->notifyPointerFrame();
0254     QVERIFY(dragMotionSpy.wait());
0255     QCOMPARE(dragMotionSpy.count(), 1);
0256     QCOMPARE(dragMotionSpy.first().first().toPointF(), QPointF(3, 3));
0257     QCOMPARE(dragMotionSpy.first().last().toUInt(), 3u);
0258 
0259     // simulate drop
0260     QSignalSpy serverDragEndedSpy(m_seatInterface, &SeatInterface::dragEnded);
0261     QSignalSpy droppedSpy(m_dataDevice, &KWayland::Client::DataDevice::dropped);
0262     m_seatInterface->setTimestamp(timestamp++);
0263     m_seatInterface->notifyPointerButton(1, PointerButtonState::Released);
0264     m_seatInterface->notifyPointerFrame();
0265     QVERIFY(sourceDropSpy.isEmpty());
0266     QVERIFY(droppedSpy.wait());
0267     QCOMPARE(sourceDropSpy.count(), 1);
0268     QCOMPARE(serverDragEndedSpy.count(), 1);
0269 
0270     QSignalSpy finishedSpy(m_dataSource, &KWayland::Client::DataSource::dragAndDropFinished);
0271     offer->dragAndDropFinished();
0272     QVERIFY(finishedSpy.wait());
0273     delete offer;
0274 
0275     // verify that we did not get any further input events
0276     QVERIFY(pointerMotionSpy.isEmpty());
0277     // the release event is sent primarily for xwayland
0278     QCOMPARE(buttonPressSpy.count(), 2);
0279 }
0280 
0281 void TestDragAndDrop::testTouchDragAndDrop()
0282 {
0283     // this test verifies the very basic drag and drop on one surface, an enter, a move and the drop
0284     using namespace KWin;
0285     // first create a window
0286     std::unique_ptr<KWayland::Client::Surface> s(createSurface());
0287     s->setSize(QSize(100, 100));
0288     auto serverSurface = getServerSurface();
0289     QVERIFY(serverSurface);
0290 
0291     QSignalSpy dataSourceSelectedActionChangedSpy(m_dataSource, &KWayland::Client::DataSource::selectedDragAndDropActionChanged);
0292 
0293     auto timestamp = 2ms;
0294 
0295     // now we need to pass touch focus to the Surface and simulate a touch down
0296     QSignalSpy sequenceStartedSpy(m_touch, &KWayland::Client::Touch::sequenceStarted);
0297     QSignalSpy pointAddedSpy(m_touch, &KWayland::Client::Touch::pointAdded);
0298     m_seatInterface->setFocusedTouchSurface(serverSurface);
0299     m_seatInterface->setTimestamp(timestamp++);
0300     const qint32 touchId = 0;
0301     m_seatInterface->notifyTouchDown(touchId, QPointF(50, 50));
0302     QVERIFY(sequenceStartedSpy.wait());
0303 
0304     std::unique_ptr<KWayland::Client::TouchPoint> tp(sequenceStartedSpy.first().at(0).value<KWayland::Client::TouchPoint *>());
0305     QVERIFY(tp != nullptr);
0306     QCOMPARE(tp->time(), quint32(2));
0307 
0308     // add some signal spies for client side
0309     QSignalSpy dragEnteredSpy(m_dataDevice, &KWayland::Client::DataDevice::dragEntered);
0310     QSignalSpy dragMotionSpy(m_dataDevice, &KWayland::Client::DataDevice::dragMotion);
0311     QSignalSpy touchMotionSpy(m_touch, &KWayland::Client::Touch::pointMoved);
0312     QSignalSpy sourceDropSpy(m_dataSource, &KWayland::Client::DataSource::dragAndDropPerformed);
0313 
0314     // now we can start the drag and drop
0315     QSignalSpy dragStartedSpy(m_seatInterface, &SeatInterface::dragStarted);
0316     m_dataSource->setDragAndDropActions(KWayland::Client::DataDeviceManager::DnDAction::Copy | KWayland::Client::DataDeviceManager::DnDAction::Move);
0317     m_dataDevice->startDrag(tp->downSerial(), m_dataSource, s.get());
0318     QVERIFY(dragStartedSpy.wait());
0319     QCOMPARE(m_seatInterface->dragSurface(), serverSurface);
0320     QCOMPARE(m_seatInterface->dragSurfaceTransformation(), QMatrix4x4());
0321     QVERIFY(!m_seatInterface->dragIcon());
0322     QCOMPARE(SeatInterfacePrivate::get(m_seatInterface)->drag.dragImplicitGrabSerial, tp->downSerial());
0323     QVERIFY(dragEnteredSpy.wait());
0324     QCOMPARE(dragEnteredSpy.count(), 1);
0325     QCOMPARE(dragEnteredSpy.first().first().value<quint32>(), m_display->serial());
0326     QCOMPARE(dragEnteredSpy.first().last().toPointF(), QPointF(50.0, 50.0));
0327     QCOMPARE(m_dataDevice->dragSurface().data(), s.get());
0328     auto offer = m_dataDevice->dragOffer();
0329     QVERIFY(offer);
0330     QCOMPARE(offer->selectedDragAndDropAction(), KWayland::Client::DataDeviceManager::DnDAction::None);
0331     QSignalSpy offerActionChangedSpy(offer, &KWayland::Client::DataOffer::selectedDragAndDropActionChanged);
0332     QCOMPARE(m_dataDevice->dragOffer()->offeredMimeTypes().count(), 1);
0333     QCOMPARE(m_dataDevice->dragOffer()->offeredMimeTypes().first().name(), QStringLiteral("text/plain"));
0334     QTRY_COMPARE(offer->sourceDragAndDropActions(), KWayland::Client::DataDeviceManager::DnDAction::Copy | KWayland::Client::DataDeviceManager::DnDAction::Move);
0335     offer->accept(QStringLiteral("text/plain"), dragEnteredSpy.last().at(0).toUInt());
0336     offer->setDragAndDropActions(KWayland::Client::DataDeviceManager::DnDAction::Copy | KWayland::Client::DataDeviceManager::DnDAction::Move, KWayland::Client::DataDeviceManager::DnDAction::Move);
0337     QVERIFY(offerActionChangedSpy.wait());
0338     QCOMPARE(offerActionChangedSpy.count(), 1);
0339     QCOMPARE(offer->selectedDragAndDropAction(), KWayland::Client::DataDeviceManager::DnDAction::Move);
0340     QCOMPARE(dataSourceSelectedActionChangedSpy.count(), 1);
0341     QCOMPARE(m_dataSource->selectedDragAndDropAction(), KWayland::Client::DataDeviceManager::DnDAction::Move);
0342 
0343     // simulate motion
0344     m_seatInterface->setTimestamp(timestamp++);
0345     m_seatInterface->notifyTouchMotion(touchId, QPointF(75, 75));
0346     QVERIFY(dragMotionSpy.wait());
0347     QCOMPARE(dragMotionSpy.count(), 1);
0348     QCOMPARE(dragMotionSpy.first().first().toPointF(), QPointF(75, 75));
0349     QCOMPARE(dragMotionSpy.first().last().toUInt(), 3u);
0350 
0351     // simulate drop
0352     QSignalSpy serverDragEndedSpy(m_seatInterface, &SeatInterface::dragEnded);
0353     QSignalSpy droppedSpy(m_dataDevice, &KWayland::Client::DataDevice::dropped);
0354     m_seatInterface->setTimestamp(timestamp++);
0355     m_seatInterface->notifyTouchUp(touchId);
0356     QVERIFY(sourceDropSpy.isEmpty());
0357     QVERIFY(droppedSpy.wait());
0358     QCOMPARE(sourceDropSpy.count(), 1);
0359     QCOMPARE(serverDragEndedSpy.count(), 1);
0360 
0361     QSignalSpy finishedSpy(m_dataSource, &KWayland::Client::DataSource::dragAndDropFinished);
0362     offer->dragAndDropFinished();
0363     QVERIFY(finishedSpy.wait());
0364     delete offer;
0365 
0366     // verify that we did not get any further input events
0367     QVERIFY(touchMotionSpy.isEmpty());
0368     QCOMPARE(pointAddedSpy.count(), 0);
0369 }
0370 
0371 void TestDragAndDrop::testDragAndDropWithCancelByDestroyDataSource()
0372 {
0373     // this test simulates the problem from BUG 389221
0374     using namespace KWin;
0375     // first create a window
0376     std::unique_ptr<KWayland::Client::Surface> s(createSurface());
0377     auto serverSurface = getServerSurface();
0378     QVERIFY(serverSurface);
0379 
0380     QSignalSpy dataSourceSelectedActionChangedSpy(m_dataSource, &KWayland::Client::DataSource::selectedDragAndDropActionChanged);
0381 
0382     auto timestamp = 2ms;
0383 
0384     // now we need to pass pointer focus to the Surface and simulate a button press
0385     QSignalSpy buttonPressSpy(m_pointer, &KWayland::Client::Pointer::buttonStateChanged);
0386     m_seatInterface->setTimestamp(timestamp++);
0387     m_seatInterface->notifyPointerEnter(serverSurface, QPointF(0, 0));
0388     m_seatInterface->notifyPointerButton(1, PointerButtonState::Pressed);
0389     m_seatInterface->notifyPointerFrame();
0390     QVERIFY(buttonPressSpy.wait());
0391     QCOMPARE(buttonPressSpy.first().at(1).value<quint32>(), quint32(2));
0392 
0393     // add some signal spies for client side
0394     QSignalSpy dragEnteredSpy(m_dataDevice, &KWayland::Client::DataDevice::dragEntered);
0395     QSignalSpy dragMotionSpy(m_dataDevice, &KWayland::Client::DataDevice::dragMotion);
0396     QSignalSpy pointerMotionSpy(m_pointer, &KWayland::Client::Pointer::motion);
0397     QSignalSpy dragLeftSpy(m_dataDevice, &KWayland::Client::DataDevice::dragLeft);
0398 
0399     // now we can start the drag and drop
0400     QSignalSpy dragStartedSpy(m_seatInterface, &SeatInterface::dragStarted);
0401     m_dataSource->setDragAndDropActions(KWayland::Client::DataDeviceManager::DnDAction::Copy | KWayland::Client::DataDeviceManager::DnDAction::Move);
0402     m_dataDevice->startDrag(buttonPressSpy.first().first().value<quint32>(), m_dataSource, s.get());
0403     QVERIFY(dragStartedSpy.wait());
0404     QCOMPARE(m_seatInterface->dragSurface(), serverSurface);
0405     QCOMPARE(m_seatInterface->dragSurfaceTransformation(), QMatrix4x4());
0406     QVERIFY(!m_seatInterface->dragIcon());
0407     QCOMPARE(SeatInterfacePrivate::get(m_seatInterface)->drag.dragImplicitGrabSerial, buttonPressSpy.first().first().value<quint32>());
0408     QVERIFY(dragEnteredSpy.wait());
0409     QCOMPARE(dragEnteredSpy.count(), 1);
0410     QCOMPARE(dragEnteredSpy.first().first().value<quint32>(), m_display->serial());
0411     QCOMPARE(dragEnteredSpy.first().last().toPointF(), QPointF(0, 0));
0412     QCOMPARE(m_dataDevice->dragSurface().data(), s.get());
0413     auto offer = m_dataDevice->dragOffer();
0414     QVERIFY(offer);
0415     QCOMPARE(offer->selectedDragAndDropAction(), KWayland::Client::DataDeviceManager::DnDAction::None);
0416     QSignalSpy offerActionChangedSpy(offer, &KWayland::Client::DataOffer::selectedDragAndDropActionChanged);
0417     QCOMPARE(m_dataDevice->dragOffer()->offeredMimeTypes().count(), 1);
0418     QCOMPARE(m_dataDevice->dragOffer()->offeredMimeTypes().first().name(), QStringLiteral("text/plain"));
0419     QTRY_COMPARE(offer->sourceDragAndDropActions(), KWayland::Client::DataDeviceManager::DnDAction::Copy | KWayland::Client::DataDeviceManager::DnDAction::Move);
0420     offer->accept(QStringLiteral("text/plain"), dragEnteredSpy.last().at(0).toUInt());
0421     offer->setDragAndDropActions(KWayland::Client::DataDeviceManager::DnDAction::Copy | KWayland::Client::DataDeviceManager::DnDAction::Move, KWayland::Client::DataDeviceManager::DnDAction::Move);
0422     QVERIFY(offerActionChangedSpy.wait());
0423     QCOMPARE(offerActionChangedSpy.count(), 1);
0424     QCOMPARE(offer->selectedDragAndDropAction(), KWayland::Client::DataDeviceManager::DnDAction::Move);
0425     QCOMPARE(dataSourceSelectedActionChangedSpy.count(), 1);
0426     QCOMPARE(m_dataSource->selectedDragAndDropAction(), KWayland::Client::DataDeviceManager::DnDAction::Move);
0427 
0428     // simulate motion
0429     m_seatInterface->setTimestamp(timestamp++);
0430     m_seatInterface->notifyPointerMotion(QPointF(3, 3));
0431     m_seatInterface->notifyPointerFrame();
0432     QVERIFY(dragMotionSpy.wait());
0433     QCOMPARE(dragMotionSpy.count(), 1);
0434     QCOMPARE(dragMotionSpy.first().first().toPointF(), QPointF(3, 3));
0435     QCOMPARE(dragMotionSpy.first().last().toUInt(), 3u);
0436 
0437     // now delete the DataSource
0438     delete m_dataSource;
0439     m_dataSource = nullptr;
0440     QSignalSpy serverDragEndedSpy(m_seatInterface, &SeatInterface::dragEnded);
0441     QVERIFY(dragLeftSpy.isEmpty());
0442     QVERIFY(dragLeftSpy.wait());
0443     QTRY_COMPARE(dragLeftSpy.count(), 1);
0444     QTRY_COMPARE(serverDragEndedSpy.count(), 1);
0445 
0446     // simulate drop
0447     QSignalSpy droppedSpy(m_dataDevice, &KWayland::Client::DataDevice::dropped);
0448     m_seatInterface->setTimestamp(timestamp++);
0449     m_seatInterface->notifyPointerButton(1, PointerButtonState::Released);
0450     m_seatInterface->notifyPointerFrame();
0451     QVERIFY(!droppedSpy.wait(500));
0452 
0453     // verify that we did not get any further input events
0454     QVERIFY(pointerMotionSpy.isEmpty());
0455     QCOMPARE(buttonPressSpy.count(), 2);
0456 }
0457 
0458 void TestDragAndDrop::testPointerEventsIgnored()
0459 {
0460     // this test verifies that all pointer events are ignored on the focused Pointer device during drag
0461     using namespace KWin;
0462     // first create a window
0463     std::unique_ptr<KWayland::Client::Surface> s(createSurface());
0464     auto serverSurface = getServerSurface();
0465     QVERIFY(serverSurface);
0466 
0467     // pass it pointer focus
0468     m_seatInterface->notifyPointerEnter(serverSurface, QPointF(0, 0));
0469 
0470     // create signal spies for all the pointer events
0471     QSignalSpy pointerEnteredSpy(m_pointer, &KWayland::Client::Pointer::entered);
0472     QSignalSpy pointerLeftSpy(m_pointer, &KWayland::Client::Pointer::left);
0473     QSignalSpy pointerMotionSpy(m_pointer, &KWayland::Client::Pointer::motion);
0474     QSignalSpy axisSpy(m_pointer, &KWayland::Client::Pointer::axisChanged);
0475     QSignalSpy buttonSpy(m_pointer, &KWayland::Client::Pointer::buttonStateChanged);
0476     QSignalSpy dragEnteredSpy(m_dataDevice, &KWayland::Client::DataDevice::dragEntered);
0477 
0478     // first simulate a few things
0479     auto timestamp = 1ms;
0480     m_seatInterface->setTimestamp(timestamp++);
0481     m_seatInterface->notifyPointerMotion(QPointF(10, 10));
0482     m_seatInterface->setTimestamp(timestamp++);
0483     m_seatInterface->notifyPointerAxis(Qt::Vertical, 5, 120, PointerAxisSource::Wheel);
0484     m_seatInterface->notifyPointerFrame();
0485     // verify that we have those
0486     QVERIFY(axisSpy.wait());
0487     QCOMPARE(axisSpy.count(), 1);
0488     QCOMPARE(pointerMotionSpy.count(), 1);
0489     QCOMPARE(pointerEnteredSpy.count(), 1);
0490     QVERIFY(buttonSpy.isEmpty());
0491     QVERIFY(pointerLeftSpy.isEmpty());
0492 
0493     // let's start the drag
0494     m_seatInterface->setTimestamp(timestamp++);
0495     m_seatInterface->notifyPointerButton(1, PointerButtonState::Pressed);
0496     m_seatInterface->notifyPointerFrame();
0497     QVERIFY(buttonSpy.wait());
0498     QCOMPARE(buttonSpy.count(), 1);
0499     m_dataDevice->startDrag(buttonSpy.first().first().value<quint32>(), m_dataSource, s.get());
0500     QVERIFY(dragEnteredSpy.wait());
0501 
0502     // now simulate all the possible pointer interactions
0503     m_seatInterface->setTimestamp(timestamp++);
0504     m_seatInterface->notifyPointerButton(2, PointerButtonState::Pressed);
0505     m_seatInterface->notifyPointerFrame();
0506     m_seatInterface->setTimestamp(timestamp++);
0507     m_seatInterface->notifyPointerButton(2, PointerButtonState::Released);
0508     m_seatInterface->notifyPointerFrame();
0509     m_seatInterface->setTimestamp(timestamp++);
0510     m_seatInterface->notifyPointerAxis(Qt::Vertical, 5, 1, PointerAxisSource::Wheel);
0511     m_seatInterface->notifyPointerFrame();
0512     m_seatInterface->setTimestamp(timestamp++);
0513     m_seatInterface->notifyPointerAxis(Qt::Vertical, 5, 1, PointerAxisSource::Wheel);
0514     m_seatInterface->notifyPointerFrame();
0515     m_seatInterface->setTimestamp(timestamp++);
0516     m_seatInterface->notifyPointerLeave();
0517     m_seatInterface->notifyPointerFrame();
0518     m_seatInterface->setTimestamp(timestamp++);
0519     m_seatInterface->notifyPointerEnter(serverSurface, m_seatInterface->pointerPos());
0520     m_seatInterface->notifyPointerFrame();
0521     m_seatInterface->setTimestamp(timestamp++);
0522     m_seatInterface->notifyPointerMotion(QPointF(50, 50));
0523     m_seatInterface->notifyPointerFrame();
0524 
0525     // last but not least, simulate the drop
0526     QSignalSpy cancelledSpy(m_dataSource, &KWayland::Client::DataSource::cancelled);
0527     m_seatInterface->setTimestamp(timestamp++);
0528     m_seatInterface->notifyPointerButton(1, PointerButtonState::Released);
0529     m_seatInterface->notifyPointerFrame();
0530     QVERIFY(cancelledSpy.wait());
0531 
0532     // all the changes should have been ignored
0533     QCOMPARE(axisSpy.count(), 1);
0534     QCOMPARE(pointerMotionSpy.count(), 1);
0535     QCOMPARE(pointerEnteredSpy.count(), 1);
0536     QVERIFY(pointerLeftSpy.isEmpty());
0537     // the release event is sent primary for xwayland, see BUG 465444
0538     QCOMPARE(buttonSpy.count(), 2);
0539 }
0540 
0541 QTEST_GUILESS_MAIN(TestDragAndDrop)
0542 #include "test_drag_drop.moc"