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"