File indexing completed on 2024-11-10 04:56:15
0001 /* 0002 SPDX-FileCopyrightText: 2014 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 0011 #include "wayland/compositor.h" 0012 #include "wayland/datadevicemanager.h" 0013 #include "wayland/datasource.h" 0014 #include "wayland/display.h" 0015 #include "wayland/pointer.h" 0016 #include "wayland/seat.h" 0017 #include "wayland/surface.h" 0018 0019 // KWayland 0020 #include "KWayland/Client/compositor.h" 0021 #include "KWayland/Client/connection_thread.h" 0022 #include "KWayland/Client/datadevice.h" 0023 #include "KWayland/Client/datadevicemanager.h" 0024 #include "KWayland/Client/datasource.h" 0025 #include "KWayland/Client/event_queue.h" 0026 #include "KWayland/Client/keyboard.h" 0027 #include "KWayland/Client/pointer.h" 0028 #include "KWayland/Client/registry.h" 0029 #include "KWayland/Client/seat.h" 0030 #include "KWayland/Client/surface.h" 0031 0032 // Wayland 0033 #include <wayland-client.h> 0034 0035 #include <unistd.h> 0036 0037 class TestDataDevice : public QObject 0038 { 0039 Q_OBJECT 0040 private Q_SLOTS: 0041 void init(); 0042 void cleanup(); 0043 0044 void testCreate(); 0045 void testDrag_data(); 0046 void testDrag(); 0047 void testDragInternally_data(); 0048 void testDragInternally(); 0049 void testSetSelection(); 0050 void testSendSelectionOnSeat(); 0051 void testReplaceSource(); 0052 0053 private: 0054 KWin::Display *m_display = nullptr; 0055 KWin::DataDeviceManagerInterface *m_dataDeviceManagerInterface = nullptr; 0056 KWin::CompositorInterface *m_compositorInterface = nullptr; 0057 KWin::SeatInterface *m_seatInterface = nullptr; 0058 KWayland::Client::ConnectionThread *m_connection = nullptr; 0059 KWayland::Client::DataDeviceManager *m_dataDeviceManager = nullptr; 0060 KWayland::Client::Compositor *m_compositor = nullptr; 0061 KWayland::Client::Seat *m_seat = nullptr; 0062 KWayland::Client::EventQueue *m_queue = nullptr; 0063 QThread *m_thread = nullptr; 0064 }; 0065 0066 static const QString s_socketName = QStringLiteral("kwayland-test-wayland-datadevice-0"); 0067 0068 void TestDataDevice::init() 0069 { 0070 qRegisterMetaType<KWin::DataSourceInterface *>(); 0071 using namespace KWin; 0072 delete m_display; 0073 m_display = new KWin::Display(this); 0074 m_display->addSocketName(s_socketName); 0075 m_display->start(); 0076 QVERIFY(m_display->isRunning()); 0077 0078 // setup connection 0079 m_connection = new KWayland::Client::ConnectionThread; 0080 QSignalSpy connectedSpy(m_connection, &KWayland::Client::ConnectionThread::connected); 0081 m_connection->setSocketName(s_socketName); 0082 0083 m_thread = new QThread(this); 0084 m_connection->moveToThread(m_thread); 0085 m_thread->start(); 0086 0087 m_connection->initConnection(); 0088 QVERIFY(connectedSpy.wait()); 0089 0090 m_queue = new KWayland::Client::EventQueue(this); 0091 QVERIFY(!m_queue->isValid()); 0092 m_queue->setup(m_connection); 0093 QVERIFY(m_queue->isValid()); 0094 0095 KWayland::Client::Registry registry; 0096 QSignalSpy dataDeviceManagerSpy(®istry, &KWayland::Client::Registry::dataDeviceManagerAnnounced); 0097 QSignalSpy seatSpy(®istry, &KWayland::Client::Registry::seatAnnounced); 0098 QSignalSpy compositorSpy(®istry, &KWayland::Client::Registry::compositorAnnounced); 0099 QVERIFY(!registry.eventQueue()); 0100 registry.setEventQueue(m_queue); 0101 QCOMPARE(registry.eventQueue(), m_queue); 0102 registry.create(m_connection->display()); 0103 QVERIFY(registry.isValid()); 0104 registry.setup(); 0105 0106 m_dataDeviceManagerInterface = new DataDeviceManagerInterface(m_display, m_display); 0107 0108 QVERIFY(dataDeviceManagerSpy.wait()); 0109 m_dataDeviceManager = 0110 registry.createDataDeviceManager(dataDeviceManagerSpy.first().first().value<quint32>(), dataDeviceManagerSpy.first().last().value<quint32>(), this); 0111 0112 m_seatInterface = new SeatInterface(m_display, m_display); 0113 m_seatInterface->setHasPointer(true); 0114 0115 QVERIFY(seatSpy.wait()); 0116 m_seat = registry.createSeat(seatSpy.first().first().value<quint32>(), seatSpy.first().last().value<quint32>(), this); 0117 QVERIFY(m_seat->isValid()); 0118 QSignalSpy pointerChangedSpy(m_seat, &KWayland::Client::Seat::hasPointerChanged); 0119 QVERIFY(pointerChangedSpy.wait()); 0120 0121 m_compositorInterface = new CompositorInterface(m_display, m_display); 0122 QVERIFY(compositorSpy.wait()); 0123 m_compositor = registry.createCompositor(compositorSpy.first().first().value<quint32>(), compositorSpy.first().last().value<quint32>(), this); 0124 QVERIFY(m_compositor->isValid()); 0125 } 0126 0127 void TestDataDevice::cleanup() 0128 { 0129 if (m_dataDeviceManager) { 0130 delete m_dataDeviceManager; 0131 m_dataDeviceManager = nullptr; 0132 } 0133 if (m_seat) { 0134 delete m_seat; 0135 m_seat = nullptr; 0136 } 0137 if (m_compositor) { 0138 delete m_compositor; 0139 m_compositor = nullptr; 0140 } 0141 if (m_queue) { 0142 delete m_queue; 0143 m_queue = nullptr; 0144 } 0145 if (m_thread) { 0146 m_thread->quit(); 0147 m_thread->wait(); 0148 delete m_thread; 0149 m_thread = nullptr; 0150 } 0151 delete m_connection; 0152 m_connection = nullptr; 0153 0154 delete m_display; 0155 m_display = nullptr; 0156 } 0157 0158 void TestDataDevice::testCreate() 0159 { 0160 using namespace KWin; 0161 0162 QSignalSpy dataDeviceCreatedSpy(m_dataDeviceManagerInterface, &KWin::DataDeviceManagerInterface::dataDeviceCreated); 0163 0164 std::unique_ptr<KWayland::Client::DataDevice> dataDevice(m_dataDeviceManager->getDataDevice(m_seat)); 0165 QVERIFY(dataDevice->isValid()); 0166 0167 QVERIFY(dataDeviceCreatedSpy.wait()); 0168 QCOMPARE(dataDeviceCreatedSpy.count(), 1); 0169 auto deviceInterface = dataDeviceCreatedSpy.first().first().value<DataDeviceInterface *>(); 0170 QVERIFY(deviceInterface); 0171 QCOMPARE(deviceInterface->seat(), m_seatInterface); 0172 QVERIFY(!deviceInterface->selection()); 0173 0174 // this will probably fail, we need to make a selection client side 0175 QVERIFY(!m_seatInterface->selection()); 0176 m_seatInterface->setSelection(deviceInterface->selection()); 0177 QCOMPARE(m_seatInterface->selection(), deviceInterface->selection()); 0178 0179 // and destroy 0180 QSignalSpy destroyedSpy(deviceInterface, &QObject::destroyed); 0181 dataDevice.reset(); 0182 QVERIFY(destroyedSpy.wait()); 0183 QVERIFY(!m_seatInterface->selection()); 0184 } 0185 0186 void TestDataDevice::testDrag_data() 0187 { 0188 QTest::addColumn<bool>("hasGrab"); 0189 QTest::addColumn<bool>("hasPointerFocus"); 0190 QTest::addColumn<bool>("success"); 0191 0192 QTest::newRow("grab and focus") << true << true << true; 0193 QTest::newRow("no grab") << false << true << false; 0194 QTest::newRow("no focus") << true << false << false; 0195 QTest::newRow("no grab, no focus") << false << false << false; 0196 } 0197 0198 void TestDataDevice::testDrag() 0199 { 0200 using namespace KWin; 0201 std::unique_ptr<KWayland::Client::Pointer> pointer(m_seat->createPointer()); 0202 0203 QSignalSpy dataDeviceCreatedSpy(m_dataDeviceManagerInterface, &KWin::DataDeviceManagerInterface::dataDeviceCreated); 0204 0205 std::unique_ptr<KWayland::Client::DataDevice> dataDevice(m_dataDeviceManager->getDataDevice(m_seat)); 0206 QVERIFY(dataDevice->isValid()); 0207 0208 QVERIFY(dataDeviceCreatedSpy.wait()); 0209 QCOMPARE(dataDeviceCreatedSpy.count(), 1); 0210 auto deviceInterface = dataDeviceCreatedSpy.first().first().value<DataDeviceInterface *>(); 0211 QVERIFY(deviceInterface); 0212 0213 QSignalSpy dataSourceCreatedSpy(m_dataDeviceManagerInterface, &KWin::DataDeviceManagerInterface::dataSourceCreated); 0214 0215 std::unique_ptr<KWayland::Client::DataSource> dataSource(m_dataDeviceManager->createDataSource()); 0216 QVERIFY(dataSource->isValid()); 0217 0218 QVERIFY(dataSourceCreatedSpy.wait()); 0219 QCOMPARE(dataSourceCreatedSpy.count(), 1); 0220 auto sourceInterface = dataSourceCreatedSpy.first().first().value<DataSourceInterface *>(); 0221 QVERIFY(sourceInterface); 0222 0223 QSignalSpy surfaceCreatedSpy(m_compositorInterface, &KWin::CompositorInterface::surfaceCreated); 0224 0225 std::unique_ptr<KWayland::Client::Surface> surface(m_compositor->createSurface()); 0226 QVERIFY(surface->isValid()); 0227 0228 QVERIFY(surfaceCreatedSpy.wait()); 0229 QCOMPARE(surfaceCreatedSpy.count(), 1); 0230 auto surfaceInterface = surfaceCreatedSpy.first().first().value<SurfaceInterface *>(); 0231 0232 // now we have all we need to start a drag operation 0233 QSignalSpy dragStartedSpy(deviceInterface, &KWin::DataDeviceInterface::dragStarted); 0234 0235 // first we need to fake the pointer enter 0236 QFETCH(bool, hasGrab); 0237 QFETCH(bool, hasPointerFocus); 0238 QFETCH(bool, success); 0239 if (!hasGrab) { 0240 // in case we don't have grab, still generate a pointer serial to make it more interesting 0241 m_seatInterface->notifyPointerButton(Qt::LeftButton, PointerButtonState::Pressed); 0242 m_seatInterface->notifyPointerFrame(); 0243 } 0244 if (hasPointerFocus) { 0245 m_seatInterface->notifyPointerEnter(surfaceInterface, QPointF(0, 0)); 0246 } 0247 if (hasGrab) { 0248 m_seatInterface->notifyPointerButton(Qt::LeftButton, PointerButtonState::Pressed); 0249 m_seatInterface->notifyPointerFrame(); 0250 } 0251 0252 // TODO: This test would be better, if it could also test that a client trying to guess 0253 // the last serial of a different client can't start a drag. 0254 const quint32 pointerButtonSerial = success ? m_seatInterface->pointerButtonSerial(Qt::LeftButton) : 0; 0255 0256 QCoreApplication::processEvents(); 0257 // finally start the drag 0258 dataDevice->startDrag(pointerButtonSerial, dataSource.get(), surface.get()); 0259 QCOMPARE(dragStartedSpy.wait(500), success); 0260 QCOMPARE(!dragStartedSpy.isEmpty(), success); 0261 QCOMPARE(m_seatInterface->dragSource(), success ? sourceInterface : nullptr); 0262 QCOMPARE(m_seatInterface->dragSurface(), success ? surfaceInterface : nullptr); 0263 QVERIFY(!m_seatInterface->dragIcon()); 0264 } 0265 0266 void TestDataDevice::testDragInternally_data() 0267 { 0268 QTest::addColumn<bool>("hasGrab"); 0269 QTest::addColumn<bool>("hasPointerFocus"); 0270 QTest::addColumn<bool>("success"); 0271 0272 QTest::newRow("grab and focus") << true << true << true; 0273 QTest::newRow("no grab") << false << true << false; 0274 QTest::newRow("no focus") << true << false << false; 0275 QTest::newRow("no grab, no focus") << false << false << false; 0276 } 0277 0278 void TestDataDevice::testDragInternally() 0279 { 0280 using namespace KWin; 0281 std::unique_ptr<KWayland::Client::Pointer> pointer(m_seat->createPointer()); 0282 0283 QSignalSpy dataDeviceCreatedSpy(m_dataDeviceManagerInterface, &KWin::DataDeviceManagerInterface::dataDeviceCreated); 0284 0285 std::unique_ptr<KWayland::Client::DataDevice> dataDevice(m_dataDeviceManager->getDataDevice(m_seat)); 0286 QVERIFY(dataDevice->isValid()); 0287 0288 QVERIFY(dataDeviceCreatedSpy.wait()); 0289 QCOMPARE(dataDeviceCreatedSpy.count(), 1); 0290 auto deviceInterface = dataDeviceCreatedSpy.first().first().value<DataDeviceInterface *>(); 0291 QVERIFY(deviceInterface); 0292 0293 QSignalSpy surfaceCreatedSpy(m_compositorInterface, &KWin::CompositorInterface::surfaceCreated); 0294 0295 std::unique_ptr<KWayland::Client::Surface> surface(m_compositor->createSurface()); 0296 QVERIFY(surface->isValid()); 0297 0298 QVERIFY(surfaceCreatedSpy.wait()); 0299 QCOMPARE(surfaceCreatedSpy.count(), 1); 0300 auto surfaceInterface = surfaceCreatedSpy.first().first().value<SurfaceInterface *>(); 0301 0302 std::unique_ptr<KWayland::Client::Surface> iconSurface(m_compositor->createSurface()); 0303 QVERIFY(iconSurface->isValid()); 0304 0305 QVERIFY(surfaceCreatedSpy.wait()); 0306 QCOMPARE(surfaceCreatedSpy.count(), 2); 0307 auto iconSurfaceInterface = surfaceCreatedSpy.last().first().value<SurfaceInterface *>(); 0308 0309 // now we have all we need to start a drag operation 0310 QSignalSpy dragStartedSpy(deviceInterface, &KWin::DataDeviceInterface::dragStarted); 0311 0312 // first we need to fake the pointer enter 0313 QFETCH(bool, hasGrab); 0314 QFETCH(bool, hasPointerFocus); 0315 QFETCH(bool, success); 0316 if (!hasGrab) { 0317 // in case we don't have grab, still generate a pointer serial to make it more interesting 0318 m_seatInterface->notifyPointerButton(Qt::LeftButton, PointerButtonState::Pressed); 0319 m_seatInterface->notifyPointerFrame(); 0320 } 0321 if (hasPointerFocus) { 0322 m_seatInterface->notifyPointerEnter(surfaceInterface, QPointF(0, 0)); 0323 } 0324 if (hasGrab) { 0325 m_seatInterface->notifyPointerButton(Qt::LeftButton, PointerButtonState::Pressed); 0326 m_seatInterface->notifyPointerFrame(); 0327 } 0328 0329 // TODO: This test would be better, if it could also test that a client trying to guess 0330 // the last serial of a different client can't start a drag. 0331 const quint32 pointerButtonSerial = success ? m_seatInterface->pointerButtonSerial(Qt::LeftButton) : 0; 0332 0333 QCoreApplication::processEvents(); 0334 // finally start the internal drag 0335 dataDevice->startDragInternally(pointerButtonSerial, surface.get(), iconSurface.get()); 0336 QCOMPARE(dragStartedSpy.wait(500), success); 0337 QCOMPARE(!dragStartedSpy.isEmpty(), success); 0338 QVERIFY(!m_seatInterface->dragSource()); 0339 QCOMPARE(m_seatInterface->dragSurface(), success ? surfaceInterface : nullptr); 0340 0341 if (success) { 0342 QCOMPARE(m_seatInterface->dragIcon()->surface(), iconSurfaceInterface); 0343 } else { 0344 QCOMPARE(m_seatInterface->dragIcon(), nullptr); 0345 } 0346 } 0347 0348 void TestDataDevice::testSetSelection() 0349 { 0350 using namespace KWin; 0351 std::unique_ptr<KWayland::Client::Pointer> pointer(m_seat->createPointer()); 0352 0353 QSignalSpy dataDeviceCreatedSpy(m_dataDeviceManagerInterface, &KWin::DataDeviceManagerInterface::dataDeviceCreated); 0354 0355 std::unique_ptr<KWayland::Client::DataDevice> dataDevice(m_dataDeviceManager->getDataDevice(m_seat)); 0356 QVERIFY(dataDevice->isValid()); 0357 0358 QVERIFY(dataDeviceCreatedSpy.wait()); 0359 QCOMPARE(dataDeviceCreatedSpy.count(), 1); 0360 auto deviceInterface = dataDeviceCreatedSpy.first().first().value<DataDeviceInterface *>(); 0361 QVERIFY(deviceInterface); 0362 0363 QSignalSpy dataSourceCreatedSpy(m_dataDeviceManagerInterface, &KWin::DataDeviceManagerInterface::dataSourceCreated); 0364 0365 std::unique_ptr<KWayland::Client::DataSource> dataSource(m_dataDeviceManager->createDataSource()); 0366 QVERIFY(dataSource->isValid()); 0367 dataSource->offer(QStringLiteral("text/plain")); 0368 0369 QVERIFY(dataSourceCreatedSpy.wait()); 0370 QCOMPARE(dataSourceCreatedSpy.count(), 1); 0371 auto sourceInterface = dataSourceCreatedSpy.first().first().value<DataSourceInterface *>(); 0372 QVERIFY(sourceInterface); 0373 0374 // everything setup, now we can test setting the selection 0375 QSignalSpy selectionChangedSpy(deviceInterface, &KWin::DataDeviceInterface::selectionChanged); 0376 0377 QVERIFY(!deviceInterface->selection()); 0378 dataDevice->setSelection(1, dataSource.get()); 0379 QVERIFY(selectionChangedSpy.wait()); 0380 QCOMPARE(selectionChangedSpy.count(), 1); 0381 QCOMPARE(selectionChangedSpy.first().first().value<DataSourceInterface *>(), sourceInterface); 0382 QCOMPARE(deviceInterface->selection(), sourceInterface); 0383 0384 // send selection to datadevice 0385 QSignalSpy selectionOfferedSpy(dataDevice.get(), &KWayland::Client::DataDevice::selectionOffered); 0386 deviceInterface->sendSelection(deviceInterface->selection()); 0387 QVERIFY(selectionOfferedSpy.wait()); 0388 QCOMPARE(selectionOfferedSpy.count(), 1); 0389 auto dataOffer = selectionOfferedSpy.first().first().value<KWayland::Client::DataOffer *>(); 0390 QVERIFY(dataOffer); 0391 QCOMPARE(dataOffer->offeredMimeTypes().count(), 1); 0392 QCOMPARE(dataOffer->offeredMimeTypes().first().name(), QStringLiteral("text/plain")); 0393 0394 // sending a new mimetype to the selection, should be announced in the offer 0395 QSignalSpy mimeTypeAddedSpy(dataOffer, &KWayland::Client::DataOffer::mimeTypeOffered); 0396 dataSource->offer(QStringLiteral("text/html")); 0397 QVERIFY(mimeTypeAddedSpy.wait()); 0398 QCOMPARE(mimeTypeAddedSpy.count(), 1); 0399 QCOMPARE(mimeTypeAddedSpy.first().first().toString(), QStringLiteral("text/html")); 0400 QCOMPARE(dataOffer->offeredMimeTypes().count(), 2); 0401 QCOMPARE(dataOffer->offeredMimeTypes().first().name(), QStringLiteral("text/plain")); 0402 QCOMPARE(dataOffer->offeredMimeTypes().last().name(), QStringLiteral("text/html")); 0403 0404 // now clear the selection 0405 dataDevice->clearSelection(1); 0406 QVERIFY(selectionChangedSpy.wait()); 0407 QCOMPARE(selectionChangedSpy.count(), 2); 0408 QVERIFY(!deviceInterface->selection()); 0409 0410 // set another selection 0411 dataDevice->setSelection(2, dataSource.get()); 0412 QVERIFY(selectionChangedSpy.wait()); 0413 // now unbind the dataDevice 0414 QSignalSpy unboundSpy(deviceInterface, &QObject::destroyed); 0415 dataDevice.reset(); 0416 QVERIFY(unboundSpy.wait()); 0417 } 0418 0419 void TestDataDevice::testSendSelectionOnSeat() 0420 { 0421 // this test verifies that the selection is sent when setting a focused keyboard 0422 using namespace KWin; 0423 // first add keyboard support to Seat 0424 QSignalSpy keyboardChangedSpy(m_seat, &KWayland::Client::Seat::hasKeyboardChanged); 0425 m_seatInterface->setHasKeyboard(true); 0426 QVERIFY(keyboardChangedSpy.wait()); 0427 // now create DataDevice, Keyboard and a Surface 0428 QSignalSpy dataDeviceCreatedSpy(m_dataDeviceManagerInterface, &DataDeviceManagerInterface::dataDeviceCreated); 0429 std::unique_ptr<KWayland::Client::DataDevice> dataDevice(m_dataDeviceManager->getDataDevice(m_seat)); 0430 QVERIFY(dataDevice->isValid()); 0431 QVERIFY(dataDeviceCreatedSpy.wait()); 0432 auto serverDataDevice = dataDeviceCreatedSpy.first().first().value<DataDeviceInterface *>(); 0433 QVERIFY(serverDataDevice); 0434 std::unique_ptr<KWayland::Client::Keyboard> keyboard(m_seat->createKeyboard()); 0435 QVERIFY(keyboard->isValid()); 0436 QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); 0437 std::unique_ptr<KWayland::Client::Surface> surface(m_compositor->createSurface()); 0438 QVERIFY(surface->isValid()); 0439 QVERIFY(surfaceCreatedSpy.wait()); 0440 0441 auto serverSurface = surfaceCreatedSpy.first().first().value<SurfaceInterface *>(); 0442 QVERIFY(serverSurface); 0443 m_seatInterface->setFocusedKeyboardSurface(serverSurface); 0444 0445 // now set the selection 0446 std::unique_ptr<KWayland::Client::DataSource> dataSource(m_dataDeviceManager->createDataSource()); 0447 QVERIFY(dataSource->isValid()); 0448 dataSource->offer(QStringLiteral("text/plain")); 0449 dataDevice->setSelection(1, dataSource.get()); 0450 // we should get a selection offered for that on the data device 0451 QSignalSpy selectionOfferedSpy(dataDevice.get(), &KWayland::Client::DataDevice::selectionOffered); 0452 QVERIFY(selectionOfferedSpy.wait()); 0453 QCOMPARE(selectionOfferedSpy.count(), 1); 0454 0455 // now unfocus the keyboard 0456 m_seatInterface->setFocusedKeyboardSurface(nullptr); 0457 // if setting the same surface again, we should get another offer 0458 m_seatInterface->setFocusedKeyboardSurface(serverSurface); 0459 QVERIFY(selectionOfferedSpy.wait()); 0460 QCOMPARE(selectionOfferedSpy.count(), 2); 0461 0462 // now let's try to destroy the data device and set a focused keyboard just while the data device is being destroyedd 0463 m_seatInterface->setFocusedKeyboardSurface(nullptr); 0464 QSignalSpy unboundSpy(serverDataDevice, &QObject::destroyed); 0465 dataDevice.reset(); 0466 QVERIFY(unboundSpy.wait()); 0467 m_seatInterface->setFocusedKeyboardSurface(serverSurface); 0468 } 0469 0470 void TestDataDevice::testReplaceSource() 0471 { 0472 // this test verifies that replacing a data source cancels the previous source 0473 using namespace KWin; 0474 // first add keyboard support to Seat 0475 QSignalSpy keyboardChangedSpy(m_seat, &KWayland::Client::Seat::hasKeyboardChanged); 0476 m_seatInterface->setHasKeyboard(true); 0477 QVERIFY(keyboardChangedSpy.wait()); 0478 // now create DataDevice, Keyboard and a Surface 0479 QSignalSpy dataDeviceCreatedSpy(m_dataDeviceManagerInterface, &DataDeviceManagerInterface::dataDeviceCreated); 0480 std::unique_ptr<KWayland::Client::DataDevice> dataDevice(m_dataDeviceManager->getDataDevice(m_seat)); 0481 QVERIFY(dataDevice->isValid()); 0482 QVERIFY(dataDeviceCreatedSpy.wait()); 0483 auto serverDataDevice = dataDeviceCreatedSpy.first().first().value<DataDeviceInterface *>(); 0484 QVERIFY(serverDataDevice); 0485 std::unique_ptr<KWayland::Client::Keyboard> keyboard(m_seat->createKeyboard()); 0486 QVERIFY(keyboard->isValid()); 0487 QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); 0488 std::unique_ptr<KWayland::Client::Surface> surface(m_compositor->createSurface()); 0489 QVERIFY(surface->isValid()); 0490 QVERIFY(surfaceCreatedSpy.wait()); 0491 0492 auto serverSurface = surfaceCreatedSpy.first().first().value<SurfaceInterface *>(); 0493 QVERIFY(serverSurface); 0494 m_seatInterface->setFocusedKeyboardSurface(serverSurface); 0495 0496 // now set the selection 0497 std::unique_ptr<KWayland::Client::DataSource> dataSource(m_dataDeviceManager->createDataSource()); 0498 QVERIFY(dataSource->isValid()); 0499 dataSource->offer(QStringLiteral("text/plain")); 0500 dataDevice->setSelection(1, dataSource.get()); 0501 QSignalSpy sourceCancelledSpy(dataSource.get(), &KWayland::Client::DataSource::cancelled); 0502 // we should get a selection offered for that on the data device 0503 QSignalSpy selectionOfferedSpy(dataDevice.get(), &KWayland::Client::DataDevice::selectionOffered); 0504 QVERIFY(selectionOfferedSpy.wait()); 0505 QCOMPARE(selectionOfferedSpy.count(), 1); 0506 0507 // create a second data source and replace previous one 0508 std::unique_ptr<KWayland::Client::DataSource> dataSource2(m_dataDeviceManager->createDataSource()); 0509 QVERIFY(dataSource2->isValid()); 0510 dataSource2->offer(QStringLiteral("text/plain")); 0511 QSignalSpy sourceCancelled2Spy(dataSource2.get(), &KWayland::Client::DataSource::cancelled); 0512 dataDevice->setSelection(1, dataSource2.get()); 0513 QCOMPARE(selectionOfferedSpy.count(), 1); 0514 QVERIFY(sourceCancelledSpy.wait()); 0515 QCOMPARE(selectionOfferedSpy.count(), 2); 0516 QVERIFY(sourceCancelled2Spy.isEmpty()); 0517 0518 // replace the data source with itself, ensure that it did not get cancelled 0519 dataDevice->setSelection(1, dataSource2.get()); 0520 QVERIFY(!sourceCancelled2Spy.wait(500)); 0521 QCOMPARE(selectionOfferedSpy.count(), 2); 0522 QVERIFY(sourceCancelled2Spy.isEmpty()); 0523 0524 // create a new DataDevice and replace previous one 0525 std::unique_ptr<KWayland::Client::DataDevice> dataDevice2(m_dataDeviceManager->getDataDevice(m_seat)); 0526 QVERIFY(dataDevice2->isValid()); 0527 std::unique_ptr<KWayland::Client::DataSource> dataSource3(m_dataDeviceManager->createDataSource()); 0528 QVERIFY(dataSource3->isValid()); 0529 dataSource3->offer(QStringLiteral("text/plain")); 0530 dataDevice2->setSelection(1, dataSource3.get()); 0531 QVERIFY(sourceCancelled2Spy.wait()); 0532 0533 // try to crash by first destroying dataSource3 and setting a new DataSource 0534 std::unique_ptr<KWayland::Client::DataSource> dataSource4(m_dataDeviceManager->createDataSource()); 0535 QVERIFY(dataSource4->isValid()); 0536 dataSource4->offer(QStringLiteral("text/plain")); 0537 dataSource3.reset(); 0538 dataDevice2->setSelection(1, dataSource4.get()); 0539 QVERIFY(selectionOfferedSpy.wait()); 0540 0541 auto dataOffer = selectionOfferedSpy.last()[0].value<KWayland::Client::DataOffer *>(); 0542 0543 // try to crash by destroying the data source, then requesting data 0544 dataSource4.reset(); 0545 int pipeFds[2] = {0, 0}; 0546 QVERIFY(pipe(pipeFds) == 0); 0547 0548 dataOffer->receive(QStringLiteral("text/plain"), pipeFds[1]); 0549 close(pipeFds[1]); 0550 0551 // spin the event loop, nothing should explode 0552 QTest::qWait(10); 0553 0554 close(pipeFds[0]); 0555 } 0556 0557 QTEST_GUILESS_MAIN(TestDataDevice) 0558 #include "test_datadevice.moc"