File indexing completed on 2024-11-10 04:56:21
0001 /* 0002 SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org> 0003 SPDX-FileCopyrightText: 2017 Marco Martin <mart@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0006 */ 0007 // Qt 0008 #include <QSignalSpy> 0009 #include <QTest> 0010 // KWin 0011 #include "wayland/compositor.h" 0012 #include "wayland/display.h" 0013 #include "wayland/surface.h" 0014 #include "wayland/xdgforeign_v2.h" 0015 0016 #include "KWayland/Client/compositor.h" 0017 #include "KWayland/Client/connection_thread.h" 0018 #include "KWayland/Client/event_queue.h" 0019 #include "KWayland/Client/region.h" 0020 #include "KWayland/Client/registry.h" 0021 #include "KWayland/Client/surface.h" 0022 #include "KWayland/Client/xdgforeign.h" 0023 0024 class TestForeign : public QObject 0025 { 0026 Q_OBJECT 0027 public: 0028 explicit TestForeign(QObject *parent = nullptr); 0029 private Q_SLOTS: 0030 void init(); 0031 void cleanup(); 0032 0033 void testExport(); 0034 void testDeleteImported(); 0035 void testDeleteChildSurface(); 0036 void testDeleteParentSurface(); 0037 void testDeleteExported(); 0038 void testExportTwoTimes(); 0039 void testImportTwoTimes(); 0040 void testImportInvalidToplevel(); 0041 0042 private: 0043 void doExport(); 0044 0045 KWin::Display *m_display; 0046 QPointer<KWin::CompositorInterface> m_compositorInterface; 0047 KWin::XdgForeignV2Interface *m_foreignInterface; 0048 KWayland::Client::ConnectionThread *m_connection; 0049 KWayland::Client::Compositor *m_compositor; 0050 KWayland::Client::EventQueue *m_queue; 0051 KWayland::Client::XdgExporter *m_exporter; 0052 KWayland::Client::XdgImporter *m_importer; 0053 0054 QPointer<KWayland::Client::Surface> m_exportedSurface; 0055 QPointer<KWin::SurfaceInterface> m_exportedSurfaceInterface; 0056 0057 QPointer<KWayland::Client::XdgExported> m_exported; 0058 QPointer<KWayland::Client::XdgImported> m_imported; 0059 0060 QPointer<KWayland::Client::Surface> m_childSurface; 0061 QPointer<KWin::SurfaceInterface> m_childSurfaceInterface; 0062 0063 QThread *m_thread; 0064 }; 0065 0066 static const QString s_socketName = QStringLiteral("kwayland-test-xdg-foreign-0"); 0067 0068 TestForeign::TestForeign(QObject *parent) 0069 : QObject(parent) 0070 , m_display(nullptr) 0071 , m_compositorInterface(nullptr) 0072 , m_connection(nullptr) 0073 , m_compositor(nullptr) 0074 , m_queue(nullptr) 0075 , m_exporter(nullptr) 0076 , m_importer(nullptr) 0077 , m_thread(nullptr) 0078 { 0079 } 0080 0081 void TestForeign::init() 0082 { 0083 using namespace KWin; 0084 delete m_display; 0085 m_display = new KWin::Display(this); 0086 m_display->addSocketName(s_socketName); 0087 m_display->start(); 0088 QVERIFY(m_display->isRunning()); 0089 0090 qRegisterMetaType<SurfaceInterface *>(); 0091 // setup connection 0092 m_connection = new KWayland::Client::ConnectionThread; 0093 QSignalSpy connectedSpy(m_connection, &KWayland::Client::ConnectionThread::connected); 0094 m_connection->setSocketName(s_socketName); 0095 0096 m_thread = new QThread(this); 0097 m_connection->moveToThread(m_thread); 0098 m_thread->start(); 0099 0100 m_connection->initConnection(); 0101 QVERIFY(connectedSpy.wait()); 0102 0103 m_queue = new KWayland::Client::EventQueue(this); 0104 QVERIFY(!m_queue->isValid()); 0105 m_queue->setup(m_connection); 0106 QVERIFY(m_queue->isValid()); 0107 0108 KWayland::Client::Registry registry; 0109 QSignalSpy compositorSpy(®istry, &KWayland::Client::Registry::compositorAnnounced); 0110 0111 QSignalSpy exporterSpy(®istry, &KWayland::Client::Registry::exporterUnstableV2Announced); 0112 0113 QSignalSpy importerSpy(®istry, &KWayland::Client::Registry::importerUnstableV2Announced); 0114 0115 QVERIFY(!registry.eventQueue()); 0116 registry.setEventQueue(m_queue); 0117 QCOMPARE(registry.eventQueue(), m_queue); 0118 registry.create(m_connection->display()); 0119 QVERIFY(registry.isValid()); 0120 registry.setup(); 0121 0122 m_compositorInterface = new CompositorInterface(m_display, m_display); 0123 QVERIFY(compositorSpy.wait()); 0124 m_compositor = registry.createCompositor(compositorSpy.first().first().value<quint32>(), compositorSpy.first().last().value<quint32>(), this); 0125 0126 m_foreignInterface = new XdgForeignV2Interface(m_display, m_display); 0127 0128 QVERIFY(exporterSpy.wait()); 0129 // Both importer and exporter should have been triggered by now 0130 QCOMPARE(exporterSpy.count(), 1); 0131 QCOMPARE(importerSpy.count(), 1); 0132 0133 m_exporter = registry.createXdgExporter(exporterSpy.first().first().value<quint32>(), exporterSpy.first().last().value<quint32>(), this); 0134 m_importer = registry.createXdgImporter(importerSpy.first().first().value<quint32>(), importerSpy.first().last().value<quint32>(), this); 0135 } 0136 0137 void TestForeign::cleanup() 0138 { 0139 #define CLEANUP(variable) \ 0140 if (variable) { \ 0141 delete variable; \ 0142 variable = nullptr; \ 0143 } 0144 0145 CLEANUP(m_compositor) 0146 CLEANUP(m_exporter) 0147 CLEANUP(m_importer) 0148 CLEANUP(m_queue) 0149 if (m_connection) { 0150 m_connection->deleteLater(); 0151 m_connection = nullptr; 0152 } 0153 if (m_thread) { 0154 m_thread->quit(); 0155 m_thread->wait(); 0156 delete m_thread; 0157 m_thread = nullptr; 0158 } 0159 delete m_display; 0160 m_display = nullptr; 0161 #undef CLEANUP 0162 0163 // these are the children of the display 0164 m_foreignInterface = nullptr; 0165 } 0166 0167 void TestForeign::doExport() 0168 { 0169 QSignalSpy serverSurfaceCreated(m_compositorInterface.data(), &KWin::CompositorInterface::surfaceCreated); 0170 0171 m_exportedSurface = m_compositor->createSurface(); 0172 QVERIFY(serverSurfaceCreated.wait()); 0173 0174 m_exportedSurfaceInterface = serverSurfaceCreated.first().first().value<KWin::SurfaceInterface *>(); 0175 0176 // Export a window 0177 m_exported = m_exporter->exportTopLevel(m_exportedSurface); 0178 QVERIFY(m_exported->handle().isEmpty()); 0179 QSignalSpy doneSpy(m_exported.data(), &KWayland::Client::XdgExported::done); 0180 QVERIFY(doneSpy.wait()); 0181 QVERIFY(!m_exported->handle().isEmpty()); 0182 0183 QSignalSpy transientSpy(m_foreignInterface, &KWin::XdgForeignV2Interface::transientChanged); 0184 0185 // Import the just exported window 0186 m_imported = m_importer->importTopLevel(m_exported->handle()); 0187 QVERIFY(m_imported->isValid()); 0188 0189 QSignalSpy childSurfaceInterfaceCreated(m_compositorInterface.data(), &KWin::CompositorInterface::surfaceCreated); 0190 m_childSurface = m_compositor->createSurface(); 0191 QVERIFY(childSurfaceInterfaceCreated.wait()); 0192 m_childSurfaceInterface = childSurfaceInterfaceCreated.first().first().value<KWin::SurfaceInterface *>(); 0193 m_childSurface->commit(KWayland::Client::Surface::CommitFlag::None); 0194 0195 m_imported->setParentOf(m_childSurface); 0196 QVERIFY(transientSpy.wait()); 0197 0198 QCOMPARE(transientSpy.first().first().value<KWin::SurfaceInterface *>(), m_childSurfaceInterface.data()); 0199 QCOMPARE(transientSpy.first().at(1).value<KWin::SurfaceInterface *>(), m_exportedSurfaceInterface.data()); 0200 0201 // transientFor api 0202 QCOMPARE(m_foreignInterface->transientFor(m_childSurfaceInterface), m_exportedSurfaceInterface.data()); 0203 } 0204 0205 void TestForeign::testExport() 0206 { 0207 doExport(); 0208 } 0209 0210 void TestForeign::testDeleteImported() 0211 { 0212 doExport(); 0213 0214 QSignalSpy transientSpy(m_foreignInterface, &KWin::XdgForeignV2Interface::transientChanged); 0215 0216 m_imported->deleteLater(); 0217 m_imported = nullptr; 0218 0219 QVERIFY(transientSpy.wait()); 0220 0221 QCOMPARE(transientSpy.first().first().value<KWin::SurfaceInterface *>(), m_childSurfaceInterface.data()); 0222 QVERIFY(!transientSpy.first().at(1).value<KWin::SurfaceInterface *>()); 0223 QVERIFY(!m_foreignInterface->transientFor(m_childSurfaceInterface)); 0224 } 0225 0226 void TestForeign::testDeleteChildSurface() 0227 { 0228 doExport(); 0229 0230 QSignalSpy transientSpy(m_foreignInterface, &KWin::XdgForeignV2Interface::transientChanged); 0231 0232 m_childSurface->deleteLater(); 0233 0234 QVERIFY(transientSpy.wait()); 0235 0236 QVERIFY(!transientSpy.first().at(0).value<KWin::SurfaceInterface *>()); 0237 QCOMPARE(transientSpy.first().at(1).value<KWin::SurfaceInterface *>(), m_exportedSurfaceInterface.data()); 0238 } 0239 0240 void TestForeign::testDeleteParentSurface() 0241 { 0242 doExport(); 0243 0244 QSignalSpy transientSpy(m_foreignInterface, &KWin::XdgForeignV2Interface::transientChanged); 0245 m_exportedSurface->deleteLater(); 0246 QVERIFY(transientSpy.wait()); 0247 0248 QCOMPARE(transientSpy.first().first().value<KWin::SurfaceInterface *>(), m_childSurfaceInterface.data()); 0249 QVERIFY(!transientSpy.first().at(1).value<KWin::SurfaceInterface *>()); 0250 QVERIFY(!m_foreignInterface->transientFor(m_childSurfaceInterface)); 0251 } 0252 0253 void TestForeign::testDeleteExported() 0254 { 0255 doExport(); 0256 0257 QSignalSpy transientSpy(m_foreignInterface, &KWin::XdgForeignV2Interface::transientChanged); 0258 QSignalSpy destroyedSpy(m_imported.data(), &KWayland::Client::XdgImported::importedDestroyed); 0259 0260 m_exported->deleteLater(); 0261 m_exported = nullptr; 0262 0263 QVERIFY(transientSpy.wait()); 0264 QVERIFY(destroyedSpy.wait()); 0265 0266 QCOMPARE(transientSpy.first().first().value<KWin::SurfaceInterface *>(), m_childSurfaceInterface.data()); 0267 QVERIFY(!transientSpy.first().at(1).value<KWin::SurfaceInterface *>()); 0268 QVERIFY(!m_foreignInterface->transientFor(m_childSurfaceInterface)); 0269 0270 QVERIFY(!m_imported->isValid()); 0271 } 0272 0273 void TestForeign::testExportTwoTimes() 0274 { 0275 doExport(); 0276 0277 // Export second window 0278 KWayland::Client::XdgExported *exported2 = m_exporter->exportTopLevel(m_exportedSurface); 0279 QVERIFY(exported2->handle().isEmpty()); 0280 QSignalSpy doneSpy(exported2, &KWayland::Client::XdgExported::done); 0281 QVERIFY(doneSpy.wait()); 0282 QVERIFY(!exported2->handle().isEmpty()); 0283 0284 QSignalSpy transientSpy(m_foreignInterface, &KWin::XdgForeignV2Interface::transientChanged); 0285 0286 // Import the just exported window 0287 KWayland::Client::XdgImported *imported2 = m_importer->importTopLevel(exported2->handle()); 0288 QVERIFY(imported2->isValid()); 0289 0290 // create a second child surface 0291 QSignalSpy serverSurfaceCreated(m_compositorInterface.data(), &KWin::CompositorInterface::surfaceCreated); 0292 0293 KWayland::Client::Surface *childSurface2 = m_compositor->createSurface(); 0294 QVERIFY(serverSurfaceCreated.wait()); 0295 0296 KWin::SurfaceInterface *childSurface2Interface = serverSurfaceCreated.first().first().value<KWin::SurfaceInterface *>(); 0297 0298 imported2->setParentOf(childSurface2); 0299 QVERIFY(transientSpy.wait()); 0300 0301 QCOMPARE(transientSpy.first().first().value<KWin::SurfaceInterface *>(), childSurface2Interface); 0302 QCOMPARE(transientSpy.first().at(1).value<KWin::SurfaceInterface *>(), m_exportedSurfaceInterface.data()); 0303 0304 // transientFor api 0305 // check the old relationship is still here 0306 QCOMPARE(m_foreignInterface->transientFor(m_childSurfaceInterface), m_exportedSurfaceInterface.data()); 0307 // check the new relationship 0308 QCOMPARE(m_foreignInterface->transientFor(childSurface2Interface), m_exportedSurfaceInterface.data()); 0309 } 0310 0311 void TestForeign::testImportTwoTimes() 0312 { 0313 doExport(); 0314 0315 QSignalSpy transientSpy(m_foreignInterface, &KWin::XdgForeignV2Interface::transientChanged); 0316 0317 // Import another time the exported window 0318 KWayland::Client::XdgImported *imported2 = m_importer->importTopLevel(m_exported->handle()); 0319 QVERIFY(imported2->isValid()); 0320 0321 // create a second child surface 0322 QSignalSpy serverSurfaceCreated(m_compositorInterface.data(), &KWin::CompositorInterface::surfaceCreated); 0323 0324 KWayland::Client::Surface *childSurface2 = m_compositor->createSurface(); 0325 QVERIFY(serverSurfaceCreated.wait()); 0326 0327 KWin::SurfaceInterface *childSurface2Interface = serverSurfaceCreated.first().first().value<KWin::SurfaceInterface *>(); 0328 0329 imported2->setParentOf(childSurface2); 0330 QVERIFY(transientSpy.wait()); 0331 0332 QCOMPARE(transientSpy.first().first().value<KWin::SurfaceInterface *>(), childSurface2Interface); 0333 QCOMPARE(transientSpy.first().at(1).value<KWin::SurfaceInterface *>(), m_exportedSurfaceInterface.data()); 0334 0335 // transientFor api 0336 // check the old relationship is still here 0337 QCOMPARE(m_foreignInterface->transientFor(m_childSurfaceInterface), m_exportedSurfaceInterface.data()); 0338 // check the new relationship 0339 QCOMPARE(m_foreignInterface->transientFor(childSurface2Interface), m_exportedSurfaceInterface.data()); 0340 } 0341 0342 void TestForeign::testImportInvalidToplevel() 0343 { 0344 // This test verifies that the compositor properly handles the case where a client 0345 // attempts to import a toplevel with an invalid handle. 0346 0347 KWayland::Client::XdgImported *imported = m_importer->importTopLevel(QStringLiteral("foobar")); 0348 QVERIFY(imported->isValid()); 0349 0350 QSignalSpy importedDestroySpy(imported, &KWayland::Client::XdgImported::importedDestroyed); 0351 QVERIFY(importedDestroySpy.wait()); 0352 } 0353 0354 QTEST_GUILESS_MAIN(TestForeign) 0355 #include "test_xdg_foreign.moc"