/* SPDX-FileCopyrightText: 2018 David Edmundson SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL */ // Qt #include #include // KWin #include "wayland/compositor.h" #include "wayland/display.h" #include "wayland/xdgdecoration_v1.h" #include "wayland/xdgshell.h" #include "KWayland/Client/compositor.h" #include "KWayland/Client/connection_thread.h" #include "KWayland/Client/event_queue.h" #include "KWayland/Client/registry.h" #include "KWayland/Client/surface.h" #include "KWayland/Client/xdgdecoration.h" #include "KWayland/Client/xdgshell.h" class TestXdgDecoration : public QObject { Q_OBJECT public: explicit TestXdgDecoration(QObject *parent = nullptr); private Q_SLOTS: void init(); void cleanup(); void testDecoration_data(); void testDecoration(); private: KWin::Display *m_display = nullptr; KWin::CompositorInterface *m_compositorInterface = nullptr; KWin::XdgShellInterface *m_xdgShellInterface = nullptr; KWin::XdgDecorationManagerV1Interface *m_xdgDecorationManagerInterface = nullptr; KWayland::Client::ConnectionThread *m_connection = nullptr; KWayland::Client::Compositor *m_compositor = nullptr; KWayland::Client::EventQueue *m_queue = nullptr; KWayland::Client::XdgShell *m_xdgShell = nullptr; KWayland::Client::XdgDecorationManager *m_xdgDecorationManager = nullptr; QThread *m_thread = nullptr; KWayland::Client::Registry *m_registry = nullptr; }; static const QString s_socketName = QStringLiteral("kwayland-test-wayland-server-side-decoration-0"); TestXdgDecoration::TestXdgDecoration(QObject *parent) : QObject(parent) { } void TestXdgDecoration::init() { using namespace KWin; qRegisterMetaType(); qRegisterMetaType(); delete m_display; m_display = new KWin::Display(this); m_display->addSocketName(s_socketName); m_display->start(); QVERIFY(m_display->isRunning()); // setup connection m_connection = new KWayland::Client::ConnectionThread; QSignalSpy connectedSpy(m_connection, &KWayland::Client::ConnectionThread::connected); m_connection->setSocketName(s_socketName); m_thread = new QThread(this); m_connection->moveToThread(m_thread); m_thread->start(); m_connection->initConnection(); QVERIFY(connectedSpy.wait()); m_queue = new KWayland::Client::EventQueue(this); QVERIFY(!m_queue->isValid()); m_queue->setup(m_connection); QVERIFY(m_queue->isValid()); m_registry = new KWayland::Client::Registry(); QSignalSpy compositorSpy(m_registry, &KWayland::Client::Registry::compositorAnnounced); QSignalSpy xdgShellSpy(m_registry, &KWayland::Client::Registry::xdgShellStableAnnounced); QSignalSpy xdgDecorationManagerSpy(m_registry, &KWayland::Client::Registry::xdgDecorationAnnounced); QVERIFY(!m_registry->eventQueue()); m_registry->setEventQueue(m_queue); QCOMPARE(m_registry->eventQueue(), m_queue); m_registry->create(m_connection); QVERIFY(m_registry->isValid()); m_registry->setup(); m_compositorInterface = new CompositorInterface(m_display, m_display); QVERIFY(compositorSpy.wait()); m_compositor = m_registry->createCompositor(compositorSpy.first().first().value(), compositorSpy.first().last().value(), this); m_xdgShellInterface = new XdgShellInterface(m_display, m_display); QVERIFY(xdgShellSpy.wait()); m_xdgShell = m_registry->createXdgShell(xdgShellSpy.first().first().value(), xdgShellSpy.first().last().value(), this); m_xdgDecorationManagerInterface = new XdgDecorationManagerV1Interface(m_display, m_display); QVERIFY(xdgDecorationManagerSpy.wait()); m_xdgDecorationManager = m_registry->createXdgDecorationManager(xdgDecorationManagerSpy.first().first().value(), xdgDecorationManagerSpy.first().last().value(), this); } void TestXdgDecoration::cleanup() { if (m_compositor) { delete m_compositor; m_compositor = nullptr; } if (m_xdgShell) { delete m_xdgShell; m_xdgShell = nullptr; } if (m_xdgDecorationManager) { delete m_xdgDecorationManager; m_xdgDecorationManager = nullptr; } if (m_queue) { delete m_queue; m_queue = nullptr; } if (m_registry) { delete m_registry; m_registry = nullptr; } if (m_thread) { m_thread->quit(); m_thread->wait(); delete m_thread; m_thread = nullptr; } delete m_connection; m_connection = nullptr; delete m_display; m_display = nullptr; } void TestXdgDecoration::testDecoration_data() { using namespace KWin; QTest::addColumn("configuredMode"); QTest::addColumn("configuredModeExp"); QTest::addColumn("setMode"); QTest::addColumn("setModeExp"); const auto serverClient = XdgToplevelDecorationV1Interface::Mode::Client; const auto serverServer = XdgToplevelDecorationV1Interface::Mode::Server; const auto clientClient = KWayland::Client::XdgDecoration::Mode::ClientSide; const auto clientServer = KWayland::Client::XdgDecoration::Mode::ServerSide; QTest::newRow("client->client") << serverClient << clientClient << clientClient << serverClient; QTest::newRow("client->server") << serverClient << clientClient << clientServer << serverServer; QTest::newRow("server->client") << serverServer << clientServer << clientClient << serverClient; QTest::newRow("server->server") << serverServer << clientServer << clientServer << serverServer; } void TestXdgDecoration::testDecoration() { using namespace KWin; QFETCH(KWin::XdgToplevelDecorationV1Interface::Mode, configuredMode); QFETCH(KWayland::Client::XdgDecoration::Mode, configuredModeExp); QFETCH(KWayland::Client::XdgDecoration::Mode, setMode); QFETCH(KWin::XdgToplevelDecorationV1Interface::Mode, setModeExp); QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated); QSignalSpy shellSurfaceCreatedSpy(m_xdgShellInterface, &XdgShellInterface::toplevelCreated); QSignalSpy decorationCreatedSpy(m_xdgDecorationManagerInterface, &XdgDecorationManagerV1Interface::decorationCreated); // create shell surface and deco object std::unique_ptr surface(m_compositor->createSurface()); std::unique_ptr shellSurface(m_xdgShell->createSurface(surface.get())); std::unique_ptr decoration(m_xdgDecorationManager->getToplevelDecoration(shellSurface.get())); // and receive all these on the "server" QVERIFY(surfaceCreatedSpy.count() || surfaceCreatedSpy.wait()); QVERIFY(shellSurfaceCreatedSpy.count() || shellSurfaceCreatedSpy.wait()); QVERIFY(decorationCreatedSpy.count() || decorationCreatedSpy.wait()); auto shellSurfaceIface = shellSurfaceCreatedSpy.first().first().value(); auto decorationIface = decorationCreatedSpy.first().first().value(); QVERIFY(decorationIface); QVERIFY(shellSurfaceIface); QCOMPARE(decorationIface->toplevel(), shellSurfaceIface); QCOMPARE(decorationIface->preferredMode(), XdgToplevelDecorationV1Interface::Mode::Undefined); QSignalSpy clientConfiguredSpy(decoration.get(), &KWayland::Client::XdgDecoration::modeChanged); QSignalSpy modeRequestedSpy(decorationIface, &XdgToplevelDecorationV1Interface::preferredModeChanged); // server configuring a client decorationIface->sendConfigure(configuredMode); quint32 serial = shellSurfaceIface->sendConfigure(QSize(0, 0), {}); QVERIFY(clientConfiguredSpy.wait()); QCOMPARE(clientConfiguredSpy.first().first().value(), configuredModeExp); shellSurface->ackConfigure(serial); // client requesting another mode decoration->setMode(setMode); QVERIFY(modeRequestedSpy.wait()); QCOMPARE(modeRequestedSpy.first().first().value(), setModeExp); QCOMPARE(decorationIface->preferredMode(), setModeExp); modeRequestedSpy.clear(); decoration->unsetMode(); QVERIFY(modeRequestedSpy.wait()); QCOMPARE(modeRequestedSpy.first().first().value(), XdgToplevelDecorationV1Interface::Mode::Undefined); } QTEST_GUILESS_MAIN(TestXdgDecoration) #include "test_xdg_decoration.moc"