KWin
Loading...
Searching...
No Matches
test_xdg_foreign.cpp
Go to the documentation of this file.
1/*
2 SPDX-FileCopyrightText: 2014 Martin Gräßlin <mgraesslin@kde.org>
3 SPDX-FileCopyrightText: 2017 Marco Martin <mart@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6*/
7// Qt
8#include <QSignalSpy>
9#include <QTest>
10// KWin
11#include "wayland/compositor.h"
12#include "wayland/display.h"
13#include "wayland/surface.h"
15
16#include "KWayland/Client/compositor.h"
17#include "KWayland/Client/connection_thread.h"
18#include "KWayland/Client/event_queue.h"
19#include "KWayland/Client/region.h"
20#include "KWayland/Client/registry.h"
21#include "KWayland/Client/surface.h"
22#include "KWayland/Client/xdgforeign.h"
23
24class TestForeign : public QObject
25{
26 Q_OBJECT
27public:
28 explicit TestForeign(QObject *parent = nullptr);
29private Q_SLOTS:
30 void init();
31 void cleanup();
32
33 void testExport();
34 void testDeleteImported();
35 void testDeleteChildSurface();
36 void testDeleteParentSurface();
37 void testDeleteExported();
38 void testExportTwoTimes();
39 void testImportTwoTimes();
40 void testImportInvalidToplevel();
41
42private:
43 void doExport();
44
45 KWin::Display *m_display;
46 QPointer<KWin::CompositorInterface> m_compositorInterface;
47 KWin::XdgForeignV2Interface *m_foreignInterface;
48 KWayland::Client::ConnectionThread *m_connection;
49 KWayland::Client::Compositor *m_compositor;
50 KWayland::Client::EventQueue *m_queue;
51 KWayland::Client::XdgExporter *m_exporter;
52 KWayland::Client::XdgImporter *m_importer;
53
54 QPointer<KWayland::Client::Surface> m_exportedSurface;
55 QPointer<KWin::SurfaceInterface> m_exportedSurfaceInterface;
56
57 QPointer<KWayland::Client::XdgExported> m_exported;
58 QPointer<KWayland::Client::XdgImported> m_imported;
59
60 QPointer<KWayland::Client::Surface> m_childSurface;
61 QPointer<KWin::SurfaceInterface> m_childSurfaceInterface;
62
63 QThread *m_thread;
64};
65
66static const QString s_socketName = QStringLiteral("kwayland-test-xdg-foreign-0");
67
69 : QObject(parent)
70 , m_display(nullptr)
71 , m_compositorInterface(nullptr)
72 , m_connection(nullptr)
73 , m_compositor(nullptr)
74 , m_queue(nullptr)
75 , m_exporter(nullptr)
76 , m_importer(nullptr)
77 , m_thread(nullptr)
78{
79}
80
81void TestForeign::init()
82{
83 using namespace KWin;
84 delete m_display;
85 m_display = new KWin::Display(this);
86 m_display->addSocketName(s_socketName);
87 m_display->start();
88 QVERIFY(m_display->isRunning());
89
90 qRegisterMetaType<SurfaceInterface *>();
91 // setup connection
92 m_connection = new KWayland::Client::ConnectionThread;
93 QSignalSpy connectedSpy(m_connection, &KWayland::Client::ConnectionThread::connected);
94 m_connection->setSocketName(s_socketName);
95
96 m_thread = new QThread(this);
97 m_connection->moveToThread(m_thread);
98 m_thread->start();
99
100 m_connection->initConnection();
101 QVERIFY(connectedSpy.wait());
102
103 m_queue = new KWayland::Client::EventQueue(this);
104 QVERIFY(!m_queue->isValid());
105 m_queue->setup(m_connection);
106 QVERIFY(m_queue->isValid());
107
108 KWayland::Client::Registry registry;
109 QSignalSpy compositorSpy(&registry, &KWayland::Client::Registry::compositorAnnounced);
110
111 QSignalSpy exporterSpy(&registry, &KWayland::Client::Registry::exporterUnstableV2Announced);
112
113 QSignalSpy importerSpy(&registry, &KWayland::Client::Registry::importerUnstableV2Announced);
114
115 QVERIFY(!registry.eventQueue());
116 registry.setEventQueue(m_queue);
117 QCOMPARE(registry.eventQueue(), m_queue);
118 registry.create(m_connection->display());
119 QVERIFY(registry.isValid());
120 registry.setup();
121
122 m_compositorInterface = new CompositorInterface(m_display, m_display);
123 QVERIFY(compositorSpy.wait());
124 m_compositor = registry.createCompositor(compositorSpy.first().first().value<quint32>(), compositorSpy.first().last().value<quint32>(), this);
125
126 m_foreignInterface = new XdgForeignV2Interface(m_display, m_display);
127
128 QVERIFY(exporterSpy.wait());
129 // Both importer and exporter should have been triggered by now
130 QCOMPARE(exporterSpy.count(), 1);
131 QCOMPARE(importerSpy.count(), 1);
132
133 m_exporter = registry.createXdgExporter(exporterSpy.first().first().value<quint32>(), exporterSpy.first().last().value<quint32>(), this);
134 m_importer = registry.createXdgImporter(importerSpy.first().first().value<quint32>(), importerSpy.first().last().value<quint32>(), this);
135}
136
137void TestForeign::cleanup()
138{
139#define CLEANUP(variable) \
140 if (variable) { \
141 delete variable; \
142 variable = nullptr; \
143 }
144
145 CLEANUP(m_compositor)
146 CLEANUP(m_exporter)
147 CLEANUP(m_importer)
148 CLEANUP(m_queue)
149 if (m_connection) {
150 m_connection->deleteLater();
151 m_connection = nullptr;
152 }
153 if (m_thread) {
154 m_thread->quit();
155 m_thread->wait();
156 delete m_thread;
157 m_thread = nullptr;
158 }
159 delete m_display;
160 m_display = nullptr;
161#undef CLEANUP
162
163 // these are the children of the display
164 m_foreignInterface = nullptr;
165}
166
167void TestForeign::doExport()
168{
169 QSignalSpy serverSurfaceCreated(m_compositorInterface.data(), &KWin::CompositorInterface::surfaceCreated);
170
171 m_exportedSurface = m_compositor->createSurface();
172 QVERIFY(serverSurfaceCreated.wait());
173
174 m_exportedSurfaceInterface = serverSurfaceCreated.first().first().value<KWin::SurfaceInterface *>();
175
176 // Export a window
177 m_exported = m_exporter->exportTopLevel(m_exportedSurface);
178 QVERIFY(m_exported->handle().isEmpty());
179 QSignalSpy doneSpy(m_exported.data(), &KWayland::Client::XdgExported::done);
180 QVERIFY(doneSpy.wait());
181 QVERIFY(!m_exported->handle().isEmpty());
182
183 QSignalSpy transientSpy(m_foreignInterface, &KWin::XdgForeignV2Interface::transientChanged);
184
185 // Import the just exported window
186 m_imported = m_importer->importTopLevel(m_exported->handle());
187 QVERIFY(m_imported->isValid());
188
189 QSignalSpy childSurfaceInterfaceCreated(m_compositorInterface.data(), &KWin::CompositorInterface::surfaceCreated);
190 m_childSurface = m_compositor->createSurface();
191 QVERIFY(childSurfaceInterfaceCreated.wait());
192 m_childSurfaceInterface = childSurfaceInterfaceCreated.first().first().value<KWin::SurfaceInterface *>();
193 m_childSurface->commit(KWayland::Client::Surface::CommitFlag::None);
194
195 m_imported->setParentOf(m_childSurface);
196 QVERIFY(transientSpy.wait());
197
198 QCOMPARE(transientSpy.first().first().value<KWin::SurfaceInterface *>(), m_childSurfaceInterface.data());
199 QCOMPARE(transientSpy.first().at(1).value<KWin::SurfaceInterface *>(), m_exportedSurfaceInterface.data());
200
201 // transientFor api
202 QCOMPARE(m_foreignInterface->transientFor(m_childSurfaceInterface), m_exportedSurfaceInterface.data());
203}
204
205void TestForeign::testExport()
206{
207 doExport();
208}
209
210void TestForeign::testDeleteImported()
211{
212 doExport();
213
214 QSignalSpy transientSpy(m_foreignInterface, &KWin::XdgForeignV2Interface::transientChanged);
215
216 m_imported->deleteLater();
217 m_imported = nullptr;
218
219 QVERIFY(transientSpy.wait());
220
221 QCOMPARE(transientSpy.first().first().value<KWin::SurfaceInterface *>(), m_childSurfaceInterface.data());
222 QVERIFY(!transientSpy.first().at(1).value<KWin::SurfaceInterface *>());
223 QVERIFY(!m_foreignInterface->transientFor(m_childSurfaceInterface));
224}
225
226void TestForeign::testDeleteChildSurface()
227{
228 doExport();
229
230 QSignalSpy transientSpy(m_foreignInterface, &KWin::XdgForeignV2Interface::transientChanged);
231
232 m_childSurface->deleteLater();
233
234 QVERIFY(transientSpy.wait());
235
236 QVERIFY(!transientSpy.first().at(0).value<KWin::SurfaceInterface *>());
237 QCOMPARE(transientSpy.first().at(1).value<KWin::SurfaceInterface *>(), m_exportedSurfaceInterface.data());
238}
239
240void TestForeign::testDeleteParentSurface()
241{
242 doExport();
243
244 QSignalSpy transientSpy(m_foreignInterface, &KWin::XdgForeignV2Interface::transientChanged);
245 m_exportedSurface->deleteLater();
246 QVERIFY(transientSpy.wait());
247
248 QCOMPARE(transientSpy.first().first().value<KWin::SurfaceInterface *>(), m_childSurfaceInterface.data());
249 QVERIFY(!transientSpy.first().at(1).value<KWin::SurfaceInterface *>());
250 QVERIFY(!m_foreignInterface->transientFor(m_childSurfaceInterface));
251}
252
253void TestForeign::testDeleteExported()
254{
255 doExport();
256
257 QSignalSpy transientSpy(m_foreignInterface, &KWin::XdgForeignV2Interface::transientChanged);
258 QSignalSpy destroyedSpy(m_imported.data(), &KWayland::Client::XdgImported::importedDestroyed);
259
260 m_exported->deleteLater();
261 m_exported = nullptr;
262
263 QVERIFY(transientSpy.wait());
264 QVERIFY(destroyedSpy.wait());
265
266 QCOMPARE(transientSpy.first().first().value<KWin::SurfaceInterface *>(), m_childSurfaceInterface.data());
267 QVERIFY(!transientSpy.first().at(1).value<KWin::SurfaceInterface *>());
268 QVERIFY(!m_foreignInterface->transientFor(m_childSurfaceInterface));
269
270 QVERIFY(!m_imported->isValid());
271}
272
273void TestForeign::testExportTwoTimes()
274{
275 doExport();
276
277 // Export second window
278 KWayland::Client::XdgExported *exported2 = m_exporter->exportTopLevel(m_exportedSurface);
279 QVERIFY(exported2->handle().isEmpty());
280 QSignalSpy doneSpy(exported2, &KWayland::Client::XdgExported::done);
281 QVERIFY(doneSpy.wait());
282 QVERIFY(!exported2->handle().isEmpty());
283
284 QSignalSpy transientSpy(m_foreignInterface, &KWin::XdgForeignV2Interface::transientChanged);
285
286 // Import the just exported window
287 KWayland::Client::XdgImported *imported2 = m_importer->importTopLevel(exported2->handle());
288 QVERIFY(imported2->isValid());
289
290 // create a second child surface
291 QSignalSpy serverSurfaceCreated(m_compositorInterface.data(), &KWin::CompositorInterface::surfaceCreated);
292
293 KWayland::Client::Surface *childSurface2 = m_compositor->createSurface();
294 QVERIFY(serverSurfaceCreated.wait());
295
296 KWin::SurfaceInterface *childSurface2Interface = serverSurfaceCreated.first().first().value<KWin::SurfaceInterface *>();
297
298 imported2->setParentOf(childSurface2);
299 QVERIFY(transientSpy.wait());
300
301 QCOMPARE(transientSpy.first().first().value<KWin::SurfaceInterface *>(), childSurface2Interface);
302 QCOMPARE(transientSpy.first().at(1).value<KWin::SurfaceInterface *>(), m_exportedSurfaceInterface.data());
303
304 // transientFor api
305 // check the old relationship is still here
306 QCOMPARE(m_foreignInterface->transientFor(m_childSurfaceInterface), m_exportedSurfaceInterface.data());
307 // check the new relationship
308 QCOMPARE(m_foreignInterface->transientFor(childSurface2Interface), m_exportedSurfaceInterface.data());
309}
310
311void TestForeign::testImportTwoTimes()
312{
313 doExport();
314
315 QSignalSpy transientSpy(m_foreignInterface, &KWin::XdgForeignV2Interface::transientChanged);
316
317 // Import another time the exported window
318 KWayland::Client::XdgImported *imported2 = m_importer->importTopLevel(m_exported->handle());
319 QVERIFY(imported2->isValid());
320
321 // create a second child surface
322 QSignalSpy serverSurfaceCreated(m_compositorInterface.data(), &KWin::CompositorInterface::surfaceCreated);
323
324 KWayland::Client::Surface *childSurface2 = m_compositor->createSurface();
325 QVERIFY(serverSurfaceCreated.wait());
326
327 KWin::SurfaceInterface *childSurface2Interface = serverSurfaceCreated.first().first().value<KWin::SurfaceInterface *>();
328
329 imported2->setParentOf(childSurface2);
330 QVERIFY(transientSpy.wait());
331
332 QCOMPARE(transientSpy.first().first().value<KWin::SurfaceInterface *>(), childSurface2Interface);
333 QCOMPARE(transientSpy.first().at(1).value<KWin::SurfaceInterface *>(), m_exportedSurfaceInterface.data());
334
335 // transientFor api
336 // check the old relationship is still here
337 QCOMPARE(m_foreignInterface->transientFor(m_childSurfaceInterface), m_exportedSurfaceInterface.data());
338 // check the new relationship
339 QCOMPARE(m_foreignInterface->transientFor(childSurface2Interface), m_exportedSurfaceInterface.data());
340}
341
342void TestForeign::testImportInvalidToplevel()
343{
344 // This test verifies that the compositor properly handles the case where a client
345 // attempts to import a toplevel with an invalid handle.
346
347 KWayland::Client::XdgImported *imported = m_importer->importTopLevel(QStringLiteral("foobar"));
348 QVERIFY(imported->isValid());
349
350 QSignalSpy importedDestroySpy(imported, &KWayland::Client::XdgImported::importedDestroyed);
351 QVERIFY(importedDestroySpy.wait());
352}
353
354QTEST_GUILESS_MAIN(TestForeign)
355#include "test_xdg_foreign.moc"
void surfaceCreated(KWin::SurfaceInterface *surface)
Class holding the Wayland server display loop.
Definition display.h:34
bool addSocketName(const QString &name=QString())
Definition display.cpp:68
bool isRunning() const
Definition display.cpp:144
bool start()
Definition display.cpp:92
Resource representing a wl_surface.
Definition surface.h:80
void transientChanged(KWin::SurfaceInterface *child, KWin::SurfaceInterface *parent)
SurfaceInterface * transientFor(SurfaceInterface *surface)
TestForeign(QObject *parent=nullptr)
#define CLEANUP(variable)