KWin
Loading...
Searching...
No Matches
test_xdg_shell.cpp
Go to the documentation of this file.
1/*
2 SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
3 SPDX-FileCopyrightText: 2017 David Edmundson <davidedmundson@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6*/
7
8// Qt
9#include <QSignalSpy>
10#include <QTest>
11// client
12#include "KWayland/Client/compositor.h"
13#include "KWayland/Client/connection_thread.h"
14#include "KWayland/Client/event_queue.h"
15#include "KWayland/Client/output.h"
16#include "KWayland/Client/registry.h"
17#include "KWayland/Client/seat.h"
18#include "KWayland/Client/shm_pool.h"
19#include "KWayland/Client/surface.h"
20#include "KWayland/Client/xdgshell.h"
21// server
22#include "wayland/compositor.h"
23#include "wayland/display.h"
24#include "wayland/output.h"
25#include "wayland/seat.h"
26#include "wayland/surface.h"
27#include "wayland/xdgshell.h"
28
30
31using namespace KWin;
32
33Q_DECLARE_METATYPE(Qt::MouseButton)
34
35static const QString s_socketName = QStringLiteral("kwayland-test-xdg_shell-0");
36
37class XdgShellTest : public QObject
38{
39 Q_OBJECT
40
41private Q_SLOTS:
42 void init();
43 void cleanup();
44
45 void testCreateSurface();
46 void testTitle();
47 void testWindowClass();
48 void testMaximize();
49 void testMinimize();
50 void testFullscreen();
51 void testShowWindowMenu();
52 void testMove();
53 void testResize_data();
54 void testResize();
55 void testTransient();
56 void testPing();
57 void testClose();
58 void testConfigureStates_data();
59 void testConfigureStates();
60 void testConfigureMultipleAcks();
61
62private:
63 XdgShellInterface *m_xdgShellInterface = nullptr;
64 KWayland::Client::Compositor *m_compositor = nullptr;
65 KWayland::Client::XdgShell *m_xdgShell = nullptr;
66 KWin::Display *m_display = nullptr;
67 CompositorInterface *m_compositorInterface = nullptr;
68 std::unique_ptr<FakeOutput> m_output1Handle;
69 OutputInterface *m_output1Interface = nullptr;
70 std::unique_ptr<FakeOutput> m_output2Handle;
71 OutputInterface *m_output2Interface = nullptr;
72 SeatInterface *m_seatInterface = nullptr;
73 KWayland::Client::ConnectionThread *m_connection = nullptr;
74 QThread *m_thread = nullptr;
75 KWayland::Client::EventQueue *m_queue = nullptr;
76 KWayland::Client::ShmPool *m_shmPool = nullptr;
77 KWayland::Client::Output *m_output1 = nullptr;
78 KWayland::Client::Output *m_output2 = nullptr;
79 KWayland::Client::Seat *m_seat = nullptr;
80};
81
82#define SURFACE \
83 QSignalSpy xdgSurfaceCreatedSpy(m_xdgShellInterface, &XdgShellInterface::toplevelCreated); \
84 std::unique_ptr<KWayland::Client::Surface> surface(m_compositor->createSurface()); \
85 std::unique_ptr<KWayland::Client::XdgShellSurface> xdgSurface(m_xdgShell->createSurface(surface.get())); \
86 QCOMPARE(xdgSurface->size(), QSize()); \
87 QVERIFY(xdgSurfaceCreatedSpy.wait()); \
88 auto serverXdgToplevel = xdgSurfaceCreatedSpy.first().first().value<XdgToplevelInterface *>(); \
89 QVERIFY(serverXdgToplevel);
90
91void XdgShellTest::init()
92{
93 delete m_display;
94 m_display = new KWin::Display(this);
95 m_display->addSocketName(s_socketName);
96 m_display->start();
97 QVERIFY(m_display->isRunning());
98 m_display->createShm();
99 m_output1Handle = std::make_unique<FakeOutput>();
100 m_output1Handle->setMode(QSize(1024, 768), 60000);
101 m_output1Interface = new OutputInterface(m_display, m_output1Handle.get(), m_display);
102 m_output2Handle = std::make_unique<FakeOutput>();
103 m_output2Handle->setMode(QSize(1024, 768), 60000);
104 m_output2Interface = new OutputInterface(m_display, m_output2Handle.get(), m_display);
105 m_seatInterface = new SeatInterface(m_display, m_display);
106 m_seatInterface->setHasKeyboard(true);
107 m_seatInterface->setHasPointer(true);
108 m_seatInterface->setHasTouch(true);
109 m_compositorInterface = new CompositorInterface(m_display, m_display);
110 m_xdgShellInterface = new XdgShellInterface(m_display, m_display);
111
112 // setup connection
113 m_connection = new KWayland::Client::ConnectionThread;
114 QSignalSpy connectedSpy(m_connection, &KWayland::Client::ConnectionThread::connected);
115 m_connection->setSocketName(s_socketName);
116
117 m_thread = new QThread(this);
118 m_connection->moveToThread(m_thread);
119 m_thread->start();
120
121 m_connection->initConnection();
122 QVERIFY(connectedSpy.wait());
123
124 m_queue = new KWayland::Client::EventQueue(this);
125 m_queue->setup(m_connection);
126
127 KWayland::Client::Registry registry;
128 QSignalSpy interfacesAnnouncedSpy(&registry, &KWayland::Client::Registry::interfacesAnnounced);
129 QSignalSpy interfaceAnnouncedSpy(&registry, &KWayland::Client::Registry::interfaceAnnounced);
130 QSignalSpy outputAnnouncedSpy(&registry, &KWayland::Client::Registry::outputAnnounced);
131
132 QSignalSpy xdgShellAnnouncedSpy(&registry, &KWayland::Client::Registry::xdgShellStableAnnounced);
133 registry.setEventQueue(m_queue);
134 registry.create(m_connection);
135 QVERIFY(registry.isValid());
136 registry.setup();
137 QVERIFY(interfacesAnnouncedSpy.wait());
138
139 QCOMPARE(outputAnnouncedSpy.count(), 2);
140 m_output1 = registry.createOutput(outputAnnouncedSpy.first().at(0).value<quint32>(), outputAnnouncedSpy.first().at(1).value<quint32>(), this);
141 m_output2 = registry.createOutput(outputAnnouncedSpy.last().at(0).value<quint32>(), outputAnnouncedSpy.last().at(1).value<quint32>(), this);
142
143 m_shmPool = registry.createShmPool(registry.interface(KWayland::Client::Registry::Interface::Shm).name, registry.interface(KWayland::Client::Registry::Interface::Shm).version, this);
144 QVERIFY(m_shmPool);
145 QVERIFY(m_shmPool->isValid());
146
147 m_compositor =
148 registry.createCompositor(registry.interface(KWayland::Client::Registry::Interface::Compositor).name, registry.interface(KWayland::Client::Registry::Interface::Compositor).version, this);
149 QVERIFY(m_compositor);
150 QVERIFY(m_compositor->isValid());
151
152 m_seat = registry.createSeat(registry.interface(KWayland::Client::Registry::Interface::Seat).name, registry.interface(KWayland::Client::Registry::Interface::Seat).version, this);
153 QVERIFY(m_seat);
154 QVERIFY(m_seat->isValid());
155
156 QCOMPARE(xdgShellAnnouncedSpy.count(), 1);
157
158 m_xdgShell = registry.createXdgShell(registry.interface(KWayland::Client::Registry::Interface::XdgShellStable).name,
159 registry.interface(KWayland::Client::Registry::Interface::XdgShellStable).version,
160 this);
161 QVERIFY(m_xdgShell);
162 QVERIFY(m_xdgShell->isValid());
163}
164
165void XdgShellTest::cleanup()
166{
167#define CLEANUP(variable) \
168 if (variable) { \
169 delete variable; \
170 variable = nullptr; \
171 }
172 CLEANUP(m_xdgShell)
173 CLEANUP(m_compositor)
174 CLEANUP(m_shmPool)
175 CLEANUP(m_output1)
176 CLEANUP(m_output2)
177 CLEANUP(m_seat)
178 CLEANUP(m_queue)
179 if (m_connection) {
180 m_connection->deleteLater();
181 m_connection = nullptr;
182 }
183 if (m_thread) {
184 m_thread->quit();
185 m_thread->wait();
186 delete m_thread;
187 m_thread = nullptr;
188 }
189
190 CLEANUP(m_display)
191#undef CLEANUP
192
193 // these are the children of the display
194 m_compositorInterface = nullptr;
195 m_xdgShellInterface = nullptr;
196 m_output1Handle.reset();
197 m_output1Interface = nullptr;
198 m_output2Handle.reset();
199 m_output2Interface = nullptr;
200 m_seatInterface = nullptr;
201}
202
203void XdgShellTest::testCreateSurface()
204{
205 // this test verifies that we can create a surface
206 // first created the signal spies for the server
207 QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
208 QSignalSpy xdgSurfaceCreatedSpy(m_xdgShellInterface, &XdgShellInterface::toplevelCreated);
209
210 // create surface
211 std::unique_ptr<KWayland::Client::Surface> surface(m_compositor->createSurface());
212 QVERIFY(surface != nullptr);
213 QVERIFY(surfaceCreatedSpy.wait());
214 auto serverSurface = surfaceCreatedSpy.first().first().value<SurfaceInterface *>();
215 QVERIFY(serverSurface);
216
217 // create shell surface
218 std::unique_ptr<KWayland::Client::XdgShellSurface> xdgSurface(m_xdgShell->createSurface(surface.get()));
219 QVERIFY(xdgSurface != nullptr);
220 QVERIFY(xdgSurfaceCreatedSpy.wait());
221 // verify base things
222 auto serverToplevel = xdgSurfaceCreatedSpy.first().first().value<XdgToplevelInterface *>();
223 QVERIFY(serverToplevel);
224 QCOMPARE(serverToplevel->windowTitle(), QString());
225 QCOMPARE(serverToplevel->windowClass(), QByteArray());
226 QCOMPARE(serverToplevel->parentXdgToplevel(), nullptr);
227 QCOMPARE(serverToplevel->surface(), serverSurface);
228
229 // now let's destroy it
230 QSignalSpy destroyedSpy(serverToplevel, &QObject::destroyed);
231 xdgSurface.reset();
232 QVERIFY(destroyedSpy.wait());
233}
234
235void XdgShellTest::testTitle()
236{
237 // this test verifies that we can change the title of a shell surface
238 // first create surface
239 SURFACE
240
241 // should not have a title yet
242 QCOMPARE(serverXdgToplevel->windowTitle(), QString());
243
244 // lets' change the title
245 QSignalSpy titleChangedSpy(serverXdgToplevel, &XdgToplevelInterface::windowTitleChanged);
246 xdgSurface->setTitle(QStringLiteral("foo"));
247 QVERIFY(titleChangedSpy.wait());
248 QCOMPARE(titleChangedSpy.count(), 1);
249 QCOMPARE(titleChangedSpy.first().first().toString(), QStringLiteral("foo"));
250 QCOMPARE(serverXdgToplevel->windowTitle(), QStringLiteral("foo"));
251}
252
253void XdgShellTest::testWindowClass()
254{
255 // this test verifies that we can change the window class/app id of a shell surface
256 // first create surface
257 SURFACE
258
259 // should not have a window class yet
260 QCOMPARE(serverXdgToplevel->windowClass(), QByteArray());
261
262 // let's change the window class
263 QSignalSpy windowClassChanged(serverXdgToplevel, &XdgToplevelInterface::windowClassChanged);
264 xdgSurface->setAppId(QByteArrayLiteral("org.kde.xdgsurfacetest"));
265 QVERIFY(windowClassChanged.wait());
266 QCOMPARE(windowClassChanged.count(), 1);
267 QCOMPARE(windowClassChanged.first().first().toByteArray(), QByteArrayLiteral("org.kde.xdgsurfacetest"));
268 QCOMPARE(serverXdgToplevel->windowClass(), QByteArrayLiteral("org.kde.xdgsurfacetest"));
269}
270
271void XdgShellTest::testMaximize()
272{
273 // this test verifies that the maximize/unmaximize calls work
274 SURFACE
275
276 QSignalSpy maximizeRequestedSpy(serverXdgToplevel, &XdgToplevelInterface::maximizeRequested);
277 QSignalSpy unmaximizeRequestedSpy(serverXdgToplevel, &XdgToplevelInterface::unmaximizeRequested);
278
279 xdgSurface->setMaximized(true);
280 QVERIFY(maximizeRequestedSpy.wait());
281 QCOMPARE(maximizeRequestedSpy.count(), 1);
282
283 xdgSurface->setMaximized(false);
284 QVERIFY(unmaximizeRequestedSpy.wait());
285 QCOMPARE(unmaximizeRequestedSpy.count(), 1);
286}
287
288void XdgShellTest::testMinimize()
289{
290 // this test verifies that the minimize request is delivered
291 SURFACE
292
293 QSignalSpy minimizeRequestedSpy(serverXdgToplevel, &XdgToplevelInterface::minimizeRequested);
294
295 xdgSurface->requestMinimize();
296 QVERIFY(minimizeRequestedSpy.wait());
297 QCOMPARE(minimizeRequestedSpy.count(), 1);
298}
299
300void XdgShellTest::testFullscreen()
301{
302 qRegisterMetaType<OutputInterface *>();
303 // this test verifies going to/from fullscreen
304 SURFACE
305
306 QSignalSpy fullscreenRequestedSpy(serverXdgToplevel, &XdgToplevelInterface::fullscreenRequested);
307 QSignalSpy unfullscreenRequestedSpy(serverXdgToplevel, &XdgToplevelInterface::unfullscreenRequested);
308
309 // without an output
310 xdgSurface->setFullscreen(true, nullptr);
311 QVERIFY(fullscreenRequestedSpy.wait());
312 QCOMPARE(fullscreenRequestedSpy.count(), 1);
313 QVERIFY(!fullscreenRequestedSpy.last().at(0).value<OutputInterface *>());
314
315 // unset
316 xdgSurface->setFullscreen(false);
317 QVERIFY(unfullscreenRequestedSpy.wait());
318 QCOMPARE(unfullscreenRequestedSpy.count(), 1);
319
320 // with outputs
321 xdgSurface->setFullscreen(true, m_output1);
322 QVERIFY(fullscreenRequestedSpy.wait());
323 QCOMPARE(fullscreenRequestedSpy.count(), 2);
324 QCOMPARE(fullscreenRequestedSpy.last().at(0).value<OutputInterface *>(), m_output1Interface);
325
326 // now other output
327 xdgSurface->setFullscreen(true, m_output2);
328 QVERIFY(fullscreenRequestedSpy.wait());
329 QCOMPARE(fullscreenRequestedSpy.count(), 3);
330 QCOMPARE(fullscreenRequestedSpy.last().at(0).value<OutputInterface *>(), m_output2Interface);
331}
332
333void XdgShellTest::testShowWindowMenu()
334{
335 qRegisterMetaType<SeatInterface *>();
336 // this test verifies that the show window menu request works
337 SURFACE
338
339 // hack: pretend that the xdg-surface had been configured
340 serverXdgToplevel->sendConfigure(QSize(0, 0), XdgToplevelInterface::States());
341
342 QSignalSpy windowMenuSpy(serverXdgToplevel, &XdgToplevelInterface::windowMenuRequested);
343
344 // TODO: the serial needs to be a proper one
345 xdgSurface->requestShowWindowMenu(m_seat, 20, QPoint(30, 40));
346 QVERIFY(windowMenuSpy.wait());
347 QCOMPARE(windowMenuSpy.count(), 1);
348 QCOMPARE(windowMenuSpy.first().at(0).value<SeatInterface *>(), m_seatInterface);
349 QCOMPARE(windowMenuSpy.first().at(1).toPoint(), QPoint(30, 40));
350 QCOMPARE(windowMenuSpy.first().at(2).value<quint32>(), 20u);
351}
352
353void XdgShellTest::testMove()
354{
355 qRegisterMetaType<SeatInterface *>();
356 // this test verifies that the move request works
357 SURFACE
358
359 // hack: pretend that the xdg-surface had been configured
360 serverXdgToplevel->sendConfigure(QSize(0, 0), XdgToplevelInterface::States());
361
362 QSignalSpy moveSpy(serverXdgToplevel, &XdgToplevelInterface::moveRequested);
363
364 // TODO: the serial needs to be a proper one
365 xdgSurface->requestMove(m_seat, 50);
366 QVERIFY(moveSpy.wait());
367 QCOMPARE(moveSpy.count(), 1);
368 QCOMPARE(moveSpy.first().at(0).value<SeatInterface *>(), m_seatInterface);
369 QCOMPARE(moveSpy.first().at(1).value<quint32>(), 50u);
370}
371
372void XdgShellTest::testResize_data()
373{
374 QTest::addColumn<Qt::Edges>("edges");
375 QTest::addColumn<XdgToplevelInterface::ResizeAnchor>("anchor");
376
377 QTest::newRow("none") << Qt::Edges() << XdgToplevelInterface::ResizeAnchor::None;
378 QTest::newRow("top") << Qt::Edges(Qt::TopEdge) << XdgToplevelInterface::ResizeAnchor::Top;
379 QTest::newRow("bottom") << Qt::Edges(Qt::BottomEdge) << XdgToplevelInterface::ResizeAnchor::Bottom;
380 QTest::newRow("left") << Qt::Edges(Qt::LeftEdge) << XdgToplevelInterface::ResizeAnchor::Left;
381 QTest::newRow("top left") << Qt::Edges(Qt::TopEdge | Qt::LeftEdge) << XdgToplevelInterface::ResizeAnchor::TopLeft;
382 QTest::newRow("bottom left") << Qt::Edges(Qt::BottomEdge | Qt::LeftEdge) << XdgToplevelInterface::ResizeAnchor::BottomLeft;
383 QTest::newRow("right") << Qt::Edges(Qt::RightEdge) << XdgToplevelInterface::ResizeAnchor::Right;
384 QTest::newRow("top right") << Qt::Edges(Qt::TopEdge | Qt::RightEdge) << XdgToplevelInterface::ResizeAnchor::TopRight;
385 QTest::newRow("bottom right") << Qt::Edges(Qt::BottomEdge | Qt::RightEdge) << XdgToplevelInterface::ResizeAnchor::BottomRight;
386}
387
388void XdgShellTest::testResize()
389{
390 qRegisterMetaType<SeatInterface *>();
391 // this test verifies that the resize request works
392 SURFACE
393
394 // hack: pretend that the xdg-surface had been configured
395 serverXdgToplevel->sendConfigure(QSize(0, 0), XdgToplevelInterface::States());
396
397 QSignalSpy resizeSpy(serverXdgToplevel, &XdgToplevelInterface::resizeRequested);
398
399 // TODO: the serial needs to be a proper one
400 QFETCH(Qt::Edges, edges);
401 xdgSurface->requestResize(m_seat, 60, edges);
402 QVERIFY(resizeSpy.wait());
403 QCOMPARE(resizeSpy.count(), 1);
404 QCOMPARE(resizeSpy.first().at(0).value<SeatInterface *>(), m_seatInterface);
405 QTEST(resizeSpy.first().at(1).value<XdgToplevelInterface::ResizeAnchor>(), "anchor");
406 QCOMPARE(resizeSpy.first().at(2).value<quint32>(), 60u);
407}
408
409void XdgShellTest::testTransient()
410{
411 // this test verifies that setting the transient for works
412 SURFACE
413 std::unique_ptr<KWayland::Client::Surface> surface2(m_compositor->createSurface());
414 std::unique_ptr<KWayland::Client::XdgShellSurface> xdgSurface2(m_xdgShell->createSurface(surface2.get()));
415 QVERIFY(xdgSurfaceCreatedSpy.wait());
416 auto serverXdgToplevel2 = xdgSurfaceCreatedSpy.last().first().value<XdgToplevelInterface *>();
417 QVERIFY(serverXdgToplevel2);
418
419 QVERIFY(!serverXdgToplevel->parentXdgToplevel());
420 QVERIFY(!serverXdgToplevel2->parentXdgToplevel());
421
422 // now make xdsgSurface2 a transient for xdgSurface
423 QSignalSpy transientForSpy(serverXdgToplevel2, &XdgToplevelInterface::parentXdgToplevelChanged);
424 xdgSurface2->setTransientFor(xdgSurface.get());
425
426 QVERIFY(transientForSpy.wait());
427 QCOMPARE(transientForSpy.count(), 1);
428 QCOMPARE(serverXdgToplevel2->parentXdgToplevel(), serverXdgToplevel);
429 QVERIFY(!serverXdgToplevel->parentXdgToplevel());
430
431 // unset the transient for
432 xdgSurface2->setTransientFor(nullptr);
433 QVERIFY(transientForSpy.wait());
434 QCOMPARE(transientForSpy.count(), 2);
435 QVERIFY(!serverXdgToplevel2->parentXdgToplevel());
436 QVERIFY(!serverXdgToplevel->parentXdgToplevel());
437}
438
439void XdgShellTest::testPing()
440{
441 // this test verifies that a ping request is sent to the client
442 SURFACE
443
444 QSignalSpy pingSpy(m_xdgShellInterface, &XdgShellInterface::pongReceived);
445
446 quint32 serial = m_xdgShellInterface->ping(serverXdgToplevel->xdgSurface());
447 QVERIFY(pingSpy.wait());
448 QCOMPARE(pingSpy.count(), 1);
449 QCOMPARE(pingSpy.takeFirst().at(0).value<quint32>(), serial);
450
451 // test of a ping failure
452 // disconnecting the connection thread to the queue will break the connection and pings will do a timeout
453 disconnect(m_connection, &KWayland::Client::ConnectionThread::eventsRead, m_queue, &KWayland::Client::EventQueue::dispatch);
454 m_xdgShellInterface->ping(serverXdgToplevel->xdgSurface());
455 QSignalSpy pingDelayedSpy(m_xdgShellInterface, &XdgShellInterface::pingDelayed);
456 QVERIFY(pingDelayedSpy.wait());
457
458 QSignalSpy pingTimeoutSpy(m_xdgShellInterface, &XdgShellInterface::pingTimeout);
459 QVERIFY(pingTimeoutSpy.wait());
460}
461
462void XdgShellTest::testClose()
463{
464 // this test verifies that a close request is sent to the client
465 SURFACE
466
467 QSignalSpy closeSpy(xdgSurface.get(), &KWayland::Client::XdgShellSurface::closeRequested);
468
469 serverXdgToplevel->sendClose();
470 QVERIFY(closeSpy.wait());
471 QCOMPARE(closeSpy.count(), 1);
472
473 QSignalSpy destroyedSpy(serverXdgToplevel, &XdgToplevelInterface::destroyed);
474 xdgSurface.reset();
475 QVERIFY(destroyedSpy.wait());
476}
477
478void XdgShellTest::testConfigureStates_data()
479{
480 QTest::addColumn<XdgToplevelInterface::States>("serverStates");
481 QTest::addColumn<KWayland::Client::XdgShellSurface::States>("clientStates");
482
483 const auto sa = XdgToplevelInterface::States(XdgToplevelInterface::State::Activated);
484 const auto sm = XdgToplevelInterface::States(XdgToplevelInterface::State::Maximized);
485 const auto sf = XdgToplevelInterface::States(XdgToplevelInterface::State::FullScreen);
486 const auto sr = XdgToplevelInterface::States(XdgToplevelInterface::State::Resizing);
487
488 const auto ca = KWayland::Client::XdgShellSurface::States(KWayland::Client::XdgShellSurface::State::Activated);
489 const auto cm = KWayland::Client::XdgShellSurface::States(KWayland::Client::XdgShellSurface::State::Maximized);
490 const auto cf = KWayland::Client::XdgShellSurface::States(KWayland::Client::XdgShellSurface::State::Fullscreen);
491 const auto cr = KWayland::Client::XdgShellSurface::States(KWayland::Client::XdgShellSurface::State::Resizing);
492
493 QTest::newRow("none") << XdgToplevelInterface::States() << KWayland::Client::XdgShellSurface::States();
494 QTest::newRow("Active") << sa << ca;
495 QTest::newRow("Maximize") << sm << cm;
496 QTest::newRow("Fullscreen") << sf << cf;
497 QTest::newRow("Resizing") << sr << cr;
498
499 QTest::newRow("Active/Maximize") << (sa | sm) << (ca | cm);
500 QTest::newRow("Active/Fullscreen") << (sa | sf) << (ca | cf);
501 QTest::newRow("Active/Resizing") << (sa | sr) << (ca | cr);
502 QTest::newRow("Maximize/Fullscreen") << (sm | sf) << (cm | cf);
503 QTest::newRow("Maximize/Resizing") << (sm | sr) << (cm | cr);
504 QTest::newRow("Fullscreen/Resizing") << (sf | sr) << (cf | cr);
505
506 QTest::newRow("Active/Maximize/Fullscreen") << (sa | sm | sf) << (ca | cm | cf);
507 QTest::newRow("Active/Maximize/Resizing") << (sa | sm | sr) << (ca | cm | cr);
508 QTest::newRow("Maximize/Fullscreen|Resizing") << (sm | sf | sr) << (cm | cf | cr);
509
510 QTest::newRow("Active/Maximize/Fullscreen/Resizing") << (sa | sm | sf | sr) << (ca | cm | cf | cr);
511}
512
513void XdgShellTest::testConfigureStates()
514{
515 qRegisterMetaType<KWayland::Client::XdgShellSurface::States>();
516 // this test verifies that configure states works
517 SURFACE
518
519 QSignalSpy configureSpy(xdgSurface.get(), &KWayland::Client::XdgShellSurface::configureRequested);
520
521 QFETCH(XdgToplevelInterface::States, serverStates);
522 serverXdgToplevel->sendConfigure(QSize(0, 0), serverStates);
523 QVERIFY(configureSpy.wait());
524 QCOMPARE(configureSpy.count(), 1);
525 QCOMPARE(configureSpy.first().at(0).toSize(), QSize(0, 0));
526 QTEST(configureSpy.first().at(1).value<KWayland::Client::XdgShellSurface::States>(), "clientStates");
527 QCOMPARE(configureSpy.first().at(2).value<quint32>(), m_display->serial());
528
529 QSignalSpy ackSpy(serverXdgToplevel->xdgSurface(), &XdgSurfaceInterface::configureAcknowledged);
530
531 xdgSurface->ackConfigure(configureSpy.first().at(2).value<quint32>());
532 surface->commit(KWayland::Client::Surface::CommitFlag::None);
533 QVERIFY(ackSpy.wait());
534 QCOMPARE(ackSpy.count(), 1);
535 QCOMPARE(ackSpy.first().first().value<quint32>(), configureSpy.first().at(2).value<quint32>());
536}
537
538void XdgShellTest::testConfigureMultipleAcks()
539{
540 qRegisterMetaType<KWayland::Client::XdgShellSurface::States>();
541 // this test verifies that with multiple configure requests the last acknowledged one acknowledges all
542 SURFACE
543
544 QSignalSpy configureSpy(xdgSurface.get(), &KWayland::Client::XdgShellSurface::configureRequested);
545 QSignalSpy sizeChangedSpy(xdgSurface.get(), &KWayland::Client::XdgShellSurface::sizeChanged);
546 QSignalSpy ackSpy(serverXdgToplevel->xdgSurface(), &XdgSurfaceInterface::configureAcknowledged);
547
548 serverXdgToplevel->sendConfigure(QSize(10, 20), XdgToplevelInterface::States());
549 const quint32 serial1 = m_display->serial();
550 serverXdgToplevel->sendConfigure(QSize(20, 30), XdgToplevelInterface::States());
551 const quint32 serial2 = m_display->serial();
552 QVERIFY(serial1 != serial2);
553 serverXdgToplevel->sendConfigure(QSize(30, 40), XdgToplevelInterface::States());
554 const quint32 serial3 = m_display->serial();
555 QVERIFY(serial1 != serial3);
556 QVERIFY(serial2 != serial3);
557
558 QVERIFY(configureSpy.wait());
559 QCOMPARE(configureSpy.count(), 3);
560 QCOMPARE(configureSpy.at(0).at(0).toSize(), QSize(10, 20));
561 QCOMPARE(configureSpy.at(0).at(1).value<KWayland::Client::XdgShellSurface::States>(), KWayland::Client::XdgShellSurface::States());
562 QCOMPARE(configureSpy.at(0).at(2).value<quint32>(), serial1);
563 QCOMPARE(configureSpy.at(1).at(0).toSize(), QSize(20, 30));
564 QCOMPARE(configureSpy.at(1).at(1).value<KWayland::Client::XdgShellSurface::States>(), KWayland::Client::XdgShellSurface::States());
565 QCOMPARE(configureSpy.at(1).at(2).value<quint32>(), serial2);
566 QCOMPARE(configureSpy.at(2).at(0).toSize(), QSize(30, 40));
567 QCOMPARE(configureSpy.at(2).at(1).value<KWayland::Client::XdgShellSurface::States>(), KWayland::Client::XdgShellSurface::States());
568 QCOMPARE(configureSpy.at(2).at(2).value<quint32>(), serial3);
569 QCOMPARE(sizeChangedSpy.count(), 3);
570 QCOMPARE(sizeChangedSpy.at(0).at(0).toSize(), QSize(10, 20));
571 QCOMPARE(sizeChangedSpy.at(1).at(0).toSize(), QSize(20, 30));
572 QCOMPARE(sizeChangedSpy.at(2).at(0).toSize(), QSize(30, 40));
573 QCOMPARE(xdgSurface->size(), QSize(30, 40));
574
575 xdgSurface->ackConfigure(serial3);
576 surface->commit(KWayland::Client::Surface::CommitFlag::None);
577 QVERIFY(ackSpy.wait());
578 QCOMPARE(ackSpy.count(), 1);
579 QCOMPARE(ackSpy.last().first().value<quint32>(), serial3);
580
581 // configure once more with a null size
582 serverXdgToplevel->sendConfigure(QSize(0, 0), XdgToplevelInterface::States());
583 // should not change size
584 QVERIFY(configureSpy.wait());
585 QCOMPARE(configureSpy.count(), 4);
586 QCOMPARE(sizeChangedSpy.count(), 3);
587 QCOMPARE(xdgSurface->size(), QSize(30, 40));
588}
589
590QTEST_GUILESS_MAIN(XdgShellTest)
591#include "test_xdg_shell.moc"
void surfaceCreated(KWin::SurfaceInterface *surface)
Class holding the Wayland server display loop.
Definition display.h:34
void createShm()
Definition display.cpp:128
quint32 serial()
Definition display.cpp:139
bool addSocketName(const QString &name=QString())
Definition display.cpp:68
bool isRunning() const
Definition display.cpp:144
bool start()
Definition display.cpp:92
Represents a Seat on the Wayland Display.
Definition seat.h:134
void setHasTouch(bool has)
Definition seat.cpp:372
void setHasKeyboard(bool has)
Definition seat.cpp:342
void setHasPointer(bool has)
Definition seat.cpp:357
Resource representing a wl_surface.
Definition surface.h:80
void toplevelCreated(XdgToplevelInterface *toplevel)
quint32 ping(XdgSurfaceInterface *surface)
Definition xdgshell.cpp:125
void pingTimeout(quint32 serial)
void pingDelayed(quint32 serial)
void pongReceived(quint32 serial)
void configureAcknowledged(quint32 serial)
void windowClassChanged(const QString &windowClass)
void windowMenuRequested(KWin::SeatInterface *seat, const QPoint &pos, quint32 serial)
void windowTitleChanged(const QString &windowTitle)
void fullscreenRequested(KWin::OutputInterface *output)
void moveRequested(KWin::SeatInterface *seat, quint32 serial)
void resizeRequested(KWin::SeatInterface *seat, KWin::XdgToplevelInterface::ResizeAnchor anchor, quint32 serial)
Q_DECLARE_METATYPE(KWin::SwitchEvent::State)
#define SURFACE
#define CLEANUP(variable)