File indexing completed on 2024-11-10 04:56:21
0001 /* 0002 SPDX-FileCopyrightText: 2020 David Edmundson <davidedmundson@kde.org> 0003 SPDX-FileCopyrightText: 2021 David Redondo <kde@david-redondo.de> 0004 0005 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0006 */ 0007 0008 // Qt 0009 #include <QHash> 0010 #include <QSignalSpy> 0011 #include <QTest> 0012 #include <QThread> 0013 0014 // WaylandServer 0015 #include "wayland/compositor.h" 0016 #include "wayland/datacontroldevice_v1.h" 0017 #include "wayland/datacontroldevicemanager_v1.h" 0018 #include "wayland/datacontrolsource_v1.h" 0019 #include "wayland/display.h" 0020 #include "wayland/seat.h" 0021 0022 #include <KWayland/Client/compositor.h> 0023 #include <KWayland/Client/connection_thread.h> 0024 #include <KWayland/Client/event_queue.h> 0025 #include <KWayland/Client/registry.h> 0026 #include <KWayland/Client/seat.h> 0027 0028 #include "qwayland-wlr-data-control-unstable-v1.h" 0029 0030 using namespace KWin; 0031 0032 // Faux-client API for tests 0033 0034 Q_DECLARE_OPAQUE_POINTER(::zwlr_data_control_offer_v1 *) 0035 Q_DECLARE_METATYPE(::zwlr_data_control_offer_v1 *) 0036 0037 class DataControlDeviceManager : public QObject, public QtWayland::zwlr_data_control_manager_v1 0038 { 0039 Q_OBJECT 0040 }; 0041 0042 class DataControlOffer : public QObject, public QtWayland::zwlr_data_control_offer_v1 0043 { 0044 Q_OBJECT 0045 public: 0046 ~DataControlOffer() 0047 { 0048 destroy(); 0049 } 0050 QStringList receivedOffers() 0051 { 0052 return m_receivedOffers; 0053 } 0054 0055 protected: 0056 virtual void zwlr_data_control_offer_v1_offer(const QString &mime_type) override 0057 { 0058 m_receivedOffers << mime_type; 0059 } 0060 0061 private: 0062 QStringList m_receivedOffers; 0063 }; 0064 0065 class DataControlDevice : public QObject, public QtWayland::zwlr_data_control_device_v1 0066 { 0067 Q_OBJECT 0068 public: 0069 ~DataControlDevice() 0070 { 0071 destroy(); 0072 } 0073 Q_SIGNALS: 0074 void dataControlOffer(DataControlOffer *offer); // our event receives a new ID, so we make a new object 0075 void selection(struct ::zwlr_data_control_offer_v1 *id); 0076 void primary_selection(struct ::zwlr_data_control_offer_v1 *id); 0077 0078 protected: 0079 void zwlr_data_control_device_v1_data_offer(struct ::zwlr_data_control_offer_v1 *id) override 0080 { 0081 auto offer = new DataControlOffer; 0082 offer->init(id); 0083 Q_EMIT dataControlOffer(offer); 0084 } 0085 0086 void zwlr_data_control_device_v1_selection(struct ::zwlr_data_control_offer_v1 *id) override 0087 { 0088 Q_EMIT selection(id); 0089 } 0090 0091 void zwlr_data_control_device_v1_primary_selection(struct ::zwlr_data_control_offer_v1 *id) override 0092 { 0093 Q_EMIT primary_selection(id); 0094 } 0095 }; 0096 0097 class DataControlSource : public QObject, public QtWayland::zwlr_data_control_source_v1 0098 { 0099 Q_OBJECT 0100 public: 0101 ~DataControlSource() 0102 { 0103 destroy(); 0104 } 0105 0106 public: 0107 }; 0108 0109 class TestDataSource : public AbstractDataSource 0110 { 0111 Q_OBJECT 0112 public: 0113 TestDataSource() 0114 : AbstractDataSource(nullptr) 0115 { 0116 } 0117 ~TestDataSource() 0118 { 0119 Q_EMIT aboutToBeDestroyed(); 0120 } 0121 void requestData(const QString &mimeType, qint32 fd) override 0122 { 0123 }; 0124 void cancel() override{}; 0125 QStringList mimeTypes() const override 0126 { 0127 return {"text/test1", "text/test2"}; 0128 } 0129 }; 0130 0131 // The test itself 0132 0133 class DataControlInterfaceTest : public QObject 0134 { 0135 Q_OBJECT 0136 0137 private Q_SLOTS: 0138 void init(); 0139 void cleanup(); 0140 void testCopyToControl(); 0141 void testCopyToControlPrimarySelection(); 0142 void testCopyFromControl(); 0143 void testCopyFromControlPrimarySelection(); 0144 void testKlipperCase(); 0145 0146 private: 0147 KWayland::Client::ConnectionThread *m_connection; 0148 KWayland::Client::EventQueue *m_queue; 0149 KWayland::Client::Compositor *m_clientCompositor; 0150 KWayland::Client::Seat *m_clientSeat = nullptr; 0151 0152 QThread *m_thread; 0153 KWin::Display *m_display; 0154 SeatInterface *m_seat; 0155 CompositorInterface *m_serverCompositor; 0156 0157 DataControlDeviceManagerV1Interface *m_dataControlDeviceManagerInterface; 0158 0159 DataControlDeviceManager *m_dataControlDeviceManager; 0160 0161 QList<SurfaceInterface *> m_surfaces; 0162 }; 0163 0164 static const QString s_socketName = QStringLiteral("kwin-wayland-datacontrol-test-0"); 0165 0166 void DataControlInterfaceTest::init() 0167 { 0168 m_display = new KWin::Display(); 0169 m_display->addSocketName(s_socketName); 0170 m_display->start(); 0171 QVERIFY(m_display->isRunning()); 0172 0173 m_seat = new SeatInterface(m_display, this); 0174 m_serverCompositor = new CompositorInterface(m_display, this); 0175 m_dataControlDeviceManagerInterface = new DataControlDeviceManagerV1Interface(m_display, this); 0176 0177 // setup connection 0178 m_connection = new KWayland::Client::ConnectionThread; 0179 QSignalSpy connectedSpy(m_connection, &KWayland::Client::ConnectionThread::connected); 0180 m_connection->setSocketName(s_socketName); 0181 0182 m_thread = new QThread(this); 0183 m_connection->moveToThread(m_thread); 0184 m_thread->start(); 0185 0186 m_connection->initConnection(); 0187 QVERIFY(connectedSpy.wait()); 0188 QVERIFY(!m_connection->connections().isEmpty()); 0189 0190 m_queue = new KWayland::Client::EventQueue(this); 0191 QVERIFY(!m_queue->isValid()); 0192 m_queue->setup(m_connection); 0193 QVERIFY(m_queue->isValid()); 0194 0195 KWayland::Client::Registry registry; 0196 connect(®istry, &KWayland::Client::Registry::interfaceAnnounced, this, [this, ®istry](const QByteArray &interface, quint32 name, quint32 version) { 0197 if (interface == "zwlr_data_control_manager_v1") { 0198 m_dataControlDeviceManager = new DataControlDeviceManager; 0199 m_dataControlDeviceManager->init(registry.registry(), name, version); 0200 } 0201 }); 0202 connect(®istry, &KWayland::Client::Registry::seatAnnounced, this, [this, ®istry](quint32 name, quint32 version) { 0203 m_clientSeat = registry.createSeat(name, version); 0204 }); 0205 registry.setEventQueue(m_queue); 0206 QSignalSpy compositorSpy(®istry, &KWayland::Client::Registry::compositorAnnounced); 0207 registry.create(m_connection->display()); 0208 QVERIFY(registry.isValid()); 0209 registry.setup(); 0210 wl_display_flush(m_connection->display()); 0211 0212 QVERIFY(compositorSpy.wait()); 0213 m_clientCompositor = registry.createCompositor(compositorSpy.first().first().value<quint32>(), compositorSpy.first().last().value<quint32>(), this); 0214 QVERIFY(m_clientCompositor->isValid()); 0215 0216 QVERIFY(m_dataControlDeviceManager); 0217 } 0218 0219 void DataControlInterfaceTest::cleanup() 0220 { 0221 #define CLEANUP(variable) \ 0222 if (variable) { \ 0223 delete variable; \ 0224 variable = nullptr; \ 0225 } 0226 CLEANUP(m_dataControlDeviceManager) 0227 CLEANUP(m_clientSeat) 0228 CLEANUP(m_clientCompositor) 0229 CLEANUP(m_queue) 0230 if (m_connection) { 0231 m_connection->deleteLater(); 0232 m_connection = nullptr; 0233 } 0234 if (m_thread) { 0235 m_thread->quit(); 0236 m_thread->wait(); 0237 delete m_thread; 0238 m_thread = nullptr; 0239 } 0240 CLEANUP(m_display) 0241 #undef CLEANUP 0242 0243 // these are the children of the display 0244 m_seat = nullptr; 0245 m_serverCompositor = nullptr; 0246 } 0247 0248 void DataControlInterfaceTest::testCopyToControl() 0249 { 0250 // we set a dummy data source on the seat using abstract client directly 0251 // then confirm we receive the offer despite not having a surface 0252 0253 std::unique_ptr<DataControlDevice> dataControlDevice(new DataControlDevice); 0254 dataControlDevice->init(m_dataControlDeviceManager->get_data_device(*m_clientSeat)); 0255 0256 QSignalSpy newOfferSpy(dataControlDevice.get(), &DataControlDevice::dataControlOffer); 0257 QSignalSpy selectionSpy(dataControlDevice.get(), &DataControlDevice::selection); 0258 0259 std::unique_ptr<TestDataSource> testSelection(new TestDataSource); 0260 m_seat->setSelection(testSelection.get()); 0261 0262 // selection will be sent after we've been sent a new offer object and the mimes have been sent to that object 0263 selectionSpy.wait(); 0264 0265 QCOMPARE(newOfferSpy.count(), 1); 0266 std::unique_ptr<DataControlOffer> offer(newOfferSpy.first().first().value<DataControlOffer *>()); 0267 QCOMPARE(selectionSpy.first().first().value<struct ::zwlr_data_control_offer_v1 *>(), offer->object()); 0268 0269 QCOMPARE(offer->receivedOffers().count(), 2); 0270 QCOMPARE(offer->receivedOffers()[0], "text/test1"); 0271 QCOMPARE(offer->receivedOffers()[1], "text/test2"); 0272 } 0273 0274 void DataControlInterfaceTest::testCopyToControlPrimarySelection() 0275 { 0276 // we set a dummy data source on the seat using abstract client directly 0277 // then confirm we receive the offer despite not having a surface 0278 0279 std::unique_ptr<DataControlDevice> dataControlDevice(new DataControlDevice); 0280 dataControlDevice->init(m_dataControlDeviceManager->get_data_device(*m_clientSeat)); 0281 0282 QSignalSpy newOfferSpy(dataControlDevice.get(), &DataControlDevice::dataControlOffer); 0283 QSignalSpy selectionSpy(dataControlDevice.get(), &DataControlDevice::primary_selection); 0284 0285 std::unique_ptr<TestDataSource> testSelection(new TestDataSource); 0286 m_seat->setPrimarySelection(testSelection.get()); 0287 0288 // selection will be sent after we've been sent a new offer object and the mimes have been sent to that object 0289 selectionSpy.wait(); 0290 0291 QCOMPARE(newOfferSpy.count(), 1); 0292 std::unique_ptr<DataControlOffer> offer(newOfferSpy.first().first().value<DataControlOffer *>()); 0293 QCOMPARE(selectionSpy.first().first().value<struct ::zwlr_data_control_offer_v1 *>(), offer->object()); 0294 0295 QCOMPARE(offer->receivedOffers().count(), 2); 0296 QCOMPARE(offer->receivedOffers()[0], "text/test1"); 0297 QCOMPARE(offer->receivedOffers()[1], "text/test2"); 0298 } 0299 0300 void DataControlInterfaceTest::testCopyFromControl() 0301 { 0302 // we create a data device and set a selection 0303 // then confirm the server sees the new selection 0304 QSignalSpy serverSelectionChangedSpy(m_seat, &SeatInterface::selectionChanged); 0305 0306 std::unique_ptr<DataControlDevice> dataControlDevice(new DataControlDevice); 0307 dataControlDevice->init(m_dataControlDeviceManager->get_data_device(*m_clientSeat)); 0308 0309 std::unique_ptr<DataControlSource> source(new DataControlSource); 0310 source->init(m_dataControlDeviceManager->create_data_source()); 0311 source->offer("cheese/test1"); 0312 source->offer("cheese/test2"); 0313 0314 dataControlDevice->set_selection(source->object()); 0315 0316 serverSelectionChangedSpy.wait(); 0317 QVERIFY(m_seat->selection()); 0318 QCOMPARE(m_seat->selection()->mimeTypes(), QStringList({"cheese/test1", "cheese/test2"})); 0319 } 0320 0321 void DataControlInterfaceTest::testCopyFromControlPrimarySelection() 0322 { 0323 // we create a data device and set a selection 0324 // then confirm the server sees the new selection 0325 QSignalSpy serverSelectionChangedSpy(m_seat, &SeatInterface::primarySelectionChanged); 0326 0327 std::unique_ptr<DataControlDevice> dataControlDevice(new DataControlDevice); 0328 dataControlDevice->init(m_dataControlDeviceManager->get_data_device(*m_clientSeat)); 0329 0330 std::unique_ptr<DataControlSource> source(new DataControlSource); 0331 source->init(m_dataControlDeviceManager->create_data_source()); 0332 source->offer("cheese/test1"); 0333 source->offer("cheese/test2"); 0334 0335 dataControlDevice->set_primary_selection(source->object()); 0336 0337 serverSelectionChangedSpy.wait(); 0338 QVERIFY(m_seat->primarySelection()); 0339 QCOMPARE(m_seat->primarySelection()->mimeTypes(), QStringList({"cheese/test1", "cheese/test2"})); 0340 } 0341 0342 void DataControlInterfaceTest::testKlipperCase() 0343 { 0344 // This tests the setup of klipper's real world operation and a race with a common pattern seen between clients and klipper 0345 // The client's behaviour is faked with direct access to the seat 0346 0347 std::unique_ptr<DataControlDevice> dataControlDevice(new DataControlDevice); 0348 dataControlDevice->init(m_dataControlDeviceManager->get_data_device(*m_clientSeat)); 0349 0350 QSignalSpy newOfferSpy(dataControlDevice.get(), &DataControlDevice::dataControlOffer); 0351 QSignalSpy selectionSpy(dataControlDevice.get(), &DataControlDevice::selection); 0352 QSignalSpy serverSelectionChangedSpy(m_seat, &SeatInterface::selectionChanged); 0353 0354 // Client A has a data source 0355 std::unique_ptr<TestDataSource> testSelection(new TestDataSource); 0356 m_seat->setSelection(testSelection.get()); 0357 0358 // klipper gets it 0359 selectionSpy.wait(); 0360 0361 // Client A deletes it 0362 testSelection.reset(); 0363 0364 // klipper gets told 0365 selectionSpy.wait(); 0366 0367 // Client A sets something else 0368 std::unique_ptr<TestDataSource> testSelection2(new TestDataSource); 0369 m_seat->setSelection(testSelection2.get()); 0370 0371 // Meanwhile klipper updates with the old content 0372 std::unique_ptr<DataControlSource> source(new DataControlSource); 0373 source->init(m_dataControlDeviceManager->create_data_source()); 0374 source->offer("fromKlipper/test1"); 0375 source->offer("application/x-kde-onlyReplaceEmpty"); 0376 0377 dataControlDevice->set_selection(source->object()); 0378 0379 QVERIFY(!serverSelectionChangedSpy.wait(10)); 0380 QCOMPARE(m_seat->selection(), testSelection2.get()); 0381 } 0382 0383 QTEST_GUILESS_MAIN(DataControlInterfaceTest) 0384 0385 #include "test_datacontrol_interface.moc"