diff --git a/autotests/integration/kwin_wayland_test.h b/autotests/integration/kwin_wayland_test.h --- a/autotests/integration/kwin_wayland_test.h +++ b/autotests/integration/kwin_wayland_test.h @@ -46,6 +46,7 @@ class ShellSurface; class ShmPool; class Surface; +class XdgDecorationManager; } } @@ -89,7 +90,8 @@ PointerConstraints = 1 << 4, IdleInhibition = 1 << 5, AppMenu = 1 << 6, - ShadowManager = 1 << 7 + ShadowManager = 1 << 7, + XdgDecoration = 1 << 8, }; Q_DECLARE_FLAGS(AdditionalWaylandInterfaces, AdditionalWaylandInterface) /** @@ -120,7 +122,7 @@ KWayland::Client::PointerConstraints *waylandPointerConstraints(); KWayland::Client::IdleInhibitManager *waylandIdleInhibitManager(); KWayland::Client::AppMenuManager *waylandAppMenuManager(); - +KWayland::Client::XdgDecorationManager *xdgDecorationManager(); bool waitForWaylandPointer(); bool waitForWaylandTouch(); diff --git a/autotests/integration/shell_client_test.cpp b/autotests/integration/shell_client_test.cpp --- a/autotests/integration/shell_client_test.cpp +++ b/autotests/integration/shell_client_test.cpp @@ -36,12 +36,13 @@ #include #include #include +#include #include #include #include #include - +#include // system #include @@ -96,6 +97,8 @@ void testSendClientWithTransientToDesktop(); void testMinimizeWindowWithTransients_data(); void testMinimizeWindowWithTransients(); + void testXdgDecoration_data(); + void testXdgDecoration(); }; void TestShellClient::initTestCase() @@ -122,6 +125,7 @@ void TestShellClient::init() { QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::Decoration | + Test::AdditionalWaylandInterface::XdgDecoration | Test::AdditionalWaylandInterface::AppMenu)); screens()->setCurrent(0); @@ -1196,5 +1200,44 @@ QVERIFY(!transient->isMinimized()); } +void TestShellClient::testXdgDecoration_data() +{ + QTest::addColumn("requestedMode"); + QTest::addColumn("expectedMode"); + + QTest::newRow("client side requested") << XdgDecoration::Mode::ClientSide << XdgDecoration::Mode::ClientSide; + QTest::newRow("server side requested") << XdgDecoration::Mode::ServerSide << XdgDecoration::Mode::ServerSide; +} + +void TestShellClient::testXdgDecoration() +{ + QScopedPointer surface(Test::createSurface()); + QScopedPointer shellSurface(Test::createXdgShellStableSurface(surface.data())); + QScopedPointer deco(Test::xdgDecorationManager()->getToplevelDecoration(shellSurface.data())); + + QSignalSpy decorationConfiguredSpy(deco.data(), &XdgDecoration::modeChanged); + QSignalSpy configureRequestedSpy(shellSurface.data(), &XdgShellSurface::configureRequested); + + QFETCH(KWayland::Client::XdgDecoration::Mode, requestedMode); + QFETCH(KWayland::Client::XdgDecoration::Mode, expectedMode); + + //request a mode + deco->setMode(requestedMode); + + //kwin will send a configure + decorationConfiguredSpy.wait(); + configureRequestedSpy.wait(); + + QCOMPARE(decorationConfiguredSpy.count(), 1); + QCOMPARE(decorationConfiguredSpy.first()[0].value(), expectedMode); + QVERIFY(configureRequestedSpy.count() > 0); + + shellSurface->ackConfigure(configureRequestedSpy.last()[2].toInt()); + + auto c = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue); + QCOMPARE(c->userCanSetNoBorder(), expectedMode == XdgDecoration::Mode::ServerSide); + QCOMPARE(c->isDecorated(), expectedMode == XdgDecoration::Mode::ServerSide); +} + WAYLANDTEST_MAIN(TestShellClient) #include "shell_client_test.moc" diff --git a/autotests/integration/test_helpers.cpp b/autotests/integration/test_helpers.cpp --- a/autotests/integration/test_helpers.cpp +++ b/autotests/integration/test_helpers.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include //screenlocker @@ -78,6 +79,7 @@ QVector outputs; IdleInhibitManager *idleInhibit = nullptr; AppMenuManager *appMenu = nullptr; + XdgDecorationManager *xdgDecoration = nullptr; } s_waylandConnection; bool setupWaylandConnection(AdditionalWaylandInterfaces flags) @@ -218,6 +220,12 @@ return false; } } + if (flags.testFlag(AdditionalWaylandInterface::XdgDecoration)) { + s_waylandConnection.xdgDecoration = registry->createXdgDecorationManager(registry->interface(Registry::Interface::XdgDecorationUnstableV1).name, registry->interface(Registry::Interface::XdgDecorationUnstableV1).version); + if (!s_waylandConnection.xdgDecoration->isValid()) { + return false; + } + } return true; } @@ -258,6 +266,8 @@ s_waylandConnection.registry = nullptr; delete s_waylandConnection.appMenu; s_waylandConnection.appMenu = nullptr; + delete s_waylandConnection.xdgDecoration; + s_waylandConnection.xdgDecoration = nullptr; if (s_waylandConnection.thread) { QSignalSpy spy(s_waylandConnection.connection, &QObject::destroyed); s_waylandConnection.connection->deleteLater(); @@ -332,6 +342,12 @@ return s_waylandConnection.appMenu; } +XdgDecorationManager *xdgDecorationManager() +{ + return s_waylandConnection.xdgDecoration; +} + + bool waitForWaylandPointer() { if (!s_waylandConnection.seat) { diff --git a/shell_client.h b/shell_client.h --- a/shell_client.h +++ b/shell_client.h @@ -34,6 +34,7 @@ class AppMenuInterface; class PlasmaShellSurfaceInterface; class QtExtendedSurfaceInterface; +class XdgDecorationInterface; } } @@ -142,6 +143,7 @@ void installServerSideDecoration(KWayland::Server::ServerSideDecorationInterface *decoration); void installAppMenu(KWayland::Server::AppMenuInterface *appmenu); void installPalette(KWayland::Server::ServerSideDecorationPaletteInterface *palette); + void installXdgDecoration(KWayland::Server::XdgDecorationInterface *decoration); bool isInitialPositionSet() const override; @@ -257,6 +259,7 @@ QPointer m_appMenuInterface; QPointer m_paletteInterface; KWayland::Server::ServerSideDecorationInterface *m_serverDecoration = nullptr; + KWayland::Server::XdgDecorationInterface *m_xdgDecoration = nullptr; bool m_userNoBorder = false; bool m_fullScreen = false; bool m_transient = false; diff --git a/shell_client.cpp b/shell_client.cpp --- a/shell_client.cpp +++ b/shell_client.cpp @@ -48,6 +48,7 @@ #include #include #include +#include #include @@ -579,6 +580,11 @@ if (m_serverDecoration && isDecorated()) { m_serverDecoration->setMode(KWayland::Server::ServerSideDecorationManagerInterface::Mode::Server); } + if (m_xdgDecoration) { + auto mode = isDecorated() ? XdgDecorationInterface::Mode::ServerSide: XdgDecorationInterface::Mode::ClientSide; + m_xdgDecoration->configure(mode); + m_xdgShellSurface->configure(xdgSurfaceStates(), m_requestedClientSize); + } getShadow(); if (check_workspace_pos) checkWorkspacePosition(oldgeom, -2, oldClientGeom); @@ -932,6 +938,9 @@ return m_userNoBorder || isFullScreen(); } } + if (m_xdgDecoration && m_xdgDecoration->requestedMode() != XdgDecorationInterface::Mode::ClientSide) { + return m_userNoBorder || isFullScreen(); + } return true; } @@ -1056,6 +1065,9 @@ if (m_serverDecoration && m_serverDecoration->mode() == ServerSideDecorationManagerInterface::Mode::Server) { return !isFullScreen() && !isShade() && !tabGroup(); } + if (m_xdgDecoration && m_xdgDecoration->requestedMode() != XdgDecorationInterface::Mode::ClientSide) { + return !isFullScreen() && !isShade() && !tabGroup(); + } if (m_internal) { return !m_internalWindowFlags.testFlag(Qt::FramelessWindowHint) || m_internalWindowFlags.testFlag(Qt::Popup); } @@ -1194,7 +1206,7 @@ configureRequest.maximizeMode = m_requestedMaximizeMode; const QSize size = rect.size() - QSize(borderLeft() + borderRight(), borderTop() + borderBottom()); - m_requestedClientSize = QSize(0, 0); + m_requestedClientSize = size; if (m_shellSurface) { m_shellSurface->requestSize(size); @@ -1274,7 +1286,7 @@ void ShellClient::unmap() { m_unmapped = true; - m_requestedClientSize = QSize(); + m_requestedClientSize = QSize(0, 0); destroyWindowManagementInterface(); if (Workspace::self()) { addWorkspaceRepaint(visibleRect()); @@ -1806,6 +1818,29 @@ ); } +void ShellClient::installXdgDecoration(XdgDecorationInterface *deco) +{ + Q_ASSERT(m_xdgShellSurface); + + m_xdgDecoration = deco; + + connect(m_xdgDecoration, &QObject::destroyed, this, + [this] { + m_xdgDecoration = nullptr; + if (m_closing || !Workspace::self()) { + return; + } + updateDecoration(true); + } + ); + + connect(m_xdgDecoration, &XdgDecorationInterface::modeRequested, this, + [this] () { + //force is true as we must send a new configure response + updateDecoration(false, true); + }); +} + bool ShellClient::shouldExposeToWindowManagement() { if (isInternal()) { diff --git a/wayland_server.h b/wayland_server.h --- a/wayland_server.h +++ b/wayland_server.h @@ -59,6 +59,7 @@ class QtSurfaceExtensionInterface; class OutputManagementInterface; class OutputConfigurationInterface; +class XdgDecorationManagerInterface; class XdgShellInterface; class XdgForeignInterface; class XdgOutputManagerInterface; @@ -246,6 +247,7 @@ KWayland::Server::ServerSideDecorationPaletteManagerInterface *m_paletteManager = nullptr; KWayland::Server::IdleInterface *m_idle = nullptr; KWayland::Server::XdgOutputManagerInterface *m_xdgOutputManager = nullptr; + KWayland::Server::XdgDecorationManagerInterface *m_xdgDecorationManager = nullptr; struct { KWayland::Server::ClientConnection *client = nullptr; QMetaObject::Connection destroyConnection; diff --git a/wayland_server.cpp b/wayland_server.cpp --- a/wayland_server.cpp +++ b/wayland_server.cpp @@ -57,6 +57,7 @@ #include #include #include +#include #include #include #include @@ -235,6 +236,14 @@ connect(m_xdgShell, &XdgShellInterface::surfaceCreated, this, &WaylandServer::createSurface); connect(m_xdgShell, &XdgShellInterface::xdgPopupCreated, this, &WaylandServer::createSurface); + m_xdgDecorationManager = m_display->createXdgDecorationManager(m_xdgShell, m_display); + m_xdgDecorationManager->create(); + connect(m_xdgDecorationManager, &XdgDecorationManagerInterface::xdgDecorationInterfaceCreated, this, [this] (XdgDecorationInterface *deco) { + if (ShellClient *client = findClient(deco->surface()->surface())) { + client->installXdgDecoration(deco); + } + }); + m_display->createShm(); m_seat = m_display->createSeat(m_display); m_seat->create();