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"
35static const QString s_socketName = QStringLiteral(
"kwayland-test-xdg_shell-0");
45 void testCreateSurface();
47 void testWindowClass();
50 void testFullscreen();
51 void testShowWindowMenu();
53 void testResize_data();
58 void testConfigureStates_data();
59 void testConfigureStates();
60 void testConfigureMultipleAcks();
64 KWayland::Client::Compositor *m_compositor =
nullptr;
65 KWayland::Client::XdgShell *m_xdgShell =
nullptr;
68 std::unique_ptr<FakeOutput> m_output1Handle;
70 std::unique_ptr<FakeOutput> m_output2Handle;
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;
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);
91void XdgShellTest::init()
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);
113 m_connection =
new KWayland::Client::ConnectionThread;
114 QSignalSpy connectedSpy(m_connection, &KWayland::Client::ConnectionThread::connected);
115 m_connection->setSocketName(s_socketName);
117 m_thread =
new QThread(
this);
118 m_connection->moveToThread(m_thread);
121 m_connection->initConnection();
122 QVERIFY(connectedSpy.wait());
124 m_queue =
new KWayland::Client::EventQueue(
this);
125 m_queue->setup(m_connection);
127 KWayland::Client::Registry registry;
128 QSignalSpy interfacesAnnouncedSpy(®istry, &KWayland::Client::Registry::interfacesAnnounced);
129 QSignalSpy interfaceAnnouncedSpy(®istry, &KWayland::Client::Registry::interfaceAnnounced);
130 QSignalSpy outputAnnouncedSpy(®istry, &KWayland::Client::Registry::outputAnnounced);
132 QSignalSpy xdgShellAnnouncedSpy(®istry, &KWayland::Client::Registry::xdgShellStableAnnounced);
133 registry.setEventQueue(m_queue);
134 registry.create(m_connection);
135 QVERIFY(registry.isValid());
137 QVERIFY(interfacesAnnouncedSpy.wait());
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);
143 m_shmPool = registry.createShmPool(registry.interface(KWayland::Client::Registry::Interface::Shm).name, registry.interface(KWayland::Client::Registry::Interface::Shm).version,
this);
145 QVERIFY(m_shmPool->isValid());
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());
152 m_seat = registry.createSeat(registry.interface(KWayland::Client::Registry::Interface::Seat).name, registry.interface(KWayland::Client::Registry::Interface::Seat).version,
this);
154 QVERIFY(m_seat->isValid());
156 QCOMPARE(xdgShellAnnouncedSpy.count(), 1);
158 m_xdgShell = registry.createXdgShell(registry.interface(KWayland::Client::Registry::Interface::XdgShellStable).name,
159 registry.interface(KWayland::Client::Registry::Interface::XdgShellStable).version,
162 QVERIFY(m_xdgShell->isValid());
165void XdgShellTest::cleanup()
167#define CLEANUP(variable) \
170 variable = nullptr; \
180 m_connection->deleteLater();
181 m_connection =
nullptr;
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;
203void XdgShellTest::testCreateSurface()
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);
218 std::unique_ptr<KWayland::Client::XdgShellSurface> xdgSurface(m_xdgShell->createSurface(surface.get()));
219 QVERIFY(xdgSurface !=
nullptr);
220 QVERIFY(xdgSurfaceCreatedSpy.wait());
223 QVERIFY(serverToplevel);
224 QCOMPARE(serverToplevel->windowTitle(), QString());
225 QCOMPARE(serverToplevel->windowClass(), QByteArray());
226 QCOMPARE(serverToplevel->parentXdgToplevel(),
nullptr);
227 QCOMPARE(serverToplevel->surface(), serverSurface);
230 QSignalSpy destroyedSpy(serverToplevel, &QObject::destroyed);
232 QVERIFY(destroyedSpy.wait());
235void XdgShellTest::testTitle()
242 QCOMPARE(serverXdgToplevel->windowTitle(), QString());
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"));
253void XdgShellTest::testWindowClass()
260 QCOMPARE(serverXdgToplevel->windowClass(), QByteArray());
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"));
271void XdgShellTest::testMaximize()
279 xdgSurface->setMaximized(
true);
280 QVERIFY(maximizeRequestedSpy.wait());
281 QCOMPARE(maximizeRequestedSpy.count(), 1);
283 xdgSurface->setMaximized(
false);
284 QVERIFY(unmaximizeRequestedSpy.wait());
285 QCOMPARE(unmaximizeRequestedSpy.count(), 1);
288void XdgShellTest::testMinimize()
295 xdgSurface->requestMinimize();
296 QVERIFY(minimizeRequestedSpy.wait());
297 QCOMPARE(minimizeRequestedSpy.count(), 1);
300void XdgShellTest::testFullscreen()
302 qRegisterMetaType<OutputInterface *>();
310 xdgSurface->setFullscreen(
true,
nullptr);
311 QVERIFY(fullscreenRequestedSpy.wait());
312 QCOMPARE(fullscreenRequestedSpy.count(), 1);
313 QVERIFY(!fullscreenRequestedSpy.last().at(0).value<
OutputInterface *>());
316 xdgSurface->setFullscreen(
false);
317 QVERIFY(unfullscreenRequestedSpy.wait());
318 QCOMPARE(unfullscreenRequestedSpy.count(), 1);
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);
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);
333void XdgShellTest::testShowWindowMenu()
335 qRegisterMetaType<SeatInterface *>();
340 serverXdgToplevel->sendConfigure(QSize(0, 0), XdgToplevelInterface::States());
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);
353void XdgShellTest::testMove()
355 qRegisterMetaType<SeatInterface *>();
360 serverXdgToplevel->sendConfigure(QSize(0, 0), XdgToplevelInterface::States());
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);
372void XdgShellTest::testResize_data()
374 QTest::addColumn<Qt::Edges>(
"edges");
375 QTest::addColumn<XdgToplevelInterface::ResizeAnchor>(
"anchor");
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;
388void XdgShellTest::testResize()
390 qRegisterMetaType<SeatInterface *>();
395 serverXdgToplevel->sendConfigure(QSize(0, 0), XdgToplevelInterface::States());
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);
406 QCOMPARE(resizeSpy.first().at(2).value<quint32>(), 60u);
409void XdgShellTest::testTransient()
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());
417 QVERIFY(serverXdgToplevel2);
419 QVERIFY(!serverXdgToplevel->parentXdgToplevel());
420 QVERIFY(!serverXdgToplevel2->parentXdgToplevel());
424 xdgSurface2->setTransientFor(xdgSurface.get());
426 QVERIFY(transientForSpy.wait());
427 QCOMPARE(transientForSpy.count(), 1);
428 QCOMPARE(serverXdgToplevel2->parentXdgToplevel(), serverXdgToplevel);
429 QVERIFY(!serverXdgToplevel->parentXdgToplevel());
432 xdgSurface2->setTransientFor(
nullptr);
433 QVERIFY(transientForSpy.wait());
434 QCOMPARE(transientForSpy.count(), 2);
435 QVERIFY(!serverXdgToplevel2->parentXdgToplevel());
436 QVERIFY(!serverXdgToplevel->parentXdgToplevel());
439void XdgShellTest::testPing()
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);
453 disconnect(m_connection, &KWayland::Client::ConnectionThread::eventsRead, m_queue, &KWayland::Client::EventQueue::dispatch);
454 m_xdgShellInterface->
ping(serverXdgToplevel->xdgSurface());
456 QVERIFY(pingDelayedSpy.wait());
459 QVERIFY(pingTimeoutSpy.wait());
462void XdgShellTest::testClose()
467 QSignalSpy closeSpy(xdgSurface.get(), &KWayland::Client::XdgShellSurface::closeRequested);
469 serverXdgToplevel->sendClose();
470 QVERIFY(closeSpy.wait());
471 QCOMPARE(closeSpy.count(), 1);
473 QSignalSpy destroyedSpy(serverXdgToplevel, &XdgToplevelInterface::destroyed);
475 QVERIFY(destroyedSpy.wait());
478void XdgShellTest::testConfigureStates_data()
480 QTest::addColumn<XdgToplevelInterface::States>(
"serverStates");
481 QTest::addColumn<KWayland::Client::XdgShellSurface::States>(
"clientStates");
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);
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);
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;
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);
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);
510 QTest::newRow(
"Active/Maximize/Fullscreen/Resizing") << (sa | sm | sf | sr) << (ca | cm | cf | cr);
513void XdgShellTest::testConfigureStates()
515 qRegisterMetaType<KWayland::Client::XdgShellSurface::States>();
519 QSignalSpy configureSpy(xdgSurface.get(), &KWayland::Client::XdgShellSurface::configureRequested);
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());
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>());
538void XdgShellTest::testConfigureMultipleAcks()
540 qRegisterMetaType<KWayland::Client::XdgShellSurface::States>();
544 QSignalSpy configureSpy(xdgSurface.get(), &KWayland::Client::XdgShellSurface::configureRequested);
545 QSignalSpy sizeChangedSpy(xdgSurface.get(), &KWayland::Client::XdgShellSurface::sizeChanged);
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);
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));
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);
582 serverXdgToplevel->sendConfigure(QSize(0, 0), XdgToplevelInterface::States());
584 QVERIFY(configureSpy.wait());
585 QCOMPARE(configureSpy.count(), 4);
586 QCOMPARE(sizeChangedSpy.count(), 3);
587 QCOMPARE(xdgSurface->size(), QSize(30, 40));
591#include "test_xdg_shell.moc"
void surfaceCreated(KWin::SurfaceInterface *surface)
Class holding the Wayland server display loop.
bool addSocketName(const QString &name=QString())
Represents a Seat on the Wayland Display.
void setHasTouch(bool has)
void setHasKeyboard(bool has)
void setHasPointer(bool has)
Resource representing a wl_surface.
void toplevelCreated(XdgToplevelInterface *toplevel)
quint32 ping(XdgSurfaceInterface *surface)
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 unfullscreenRequested()
void windowTitleChanged(const QString &windowTitle)
void fullscreenRequested(KWin::OutputInterface *output)
void moveRequested(KWin::SeatInterface *seat, quint32 serial)
void parentXdgToplevelChanged()
void unmaximizeRequested()
void resizeRequested(KWin::SeatInterface *seat, KWin::XdgToplevelInterface::ResizeAnchor anchor, quint32 serial)
#define CLEANUP(variable)