diff --git a/autotests/integration/decoration_input_test.cpp b/autotests/integration/decoration_input_test.cpp index 5807ca70bd..e581e6e6ba 100644 --- a/autotests/integration/decoration_input_test.cpp +++ b/autotests/integration/decoration_input_test.cpp @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include @@ -97,16 +96,21 @@ AbstractClient *DecorationInputTest::showWindow() KWayland::Client::Surface *surface = Test::createSurface(Test::waylandCompositor()); VERIFY(surface); - Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface, surface); + Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface, Test::CreationSetup::CreateOnly, surface); VERIFY(shellSurface); - auto deco = Test::waylandServerSideDecoration()->create(surface, surface); - QSignalSpy decoSpy(deco, &ServerSideDecoration::modeChanged); - VERIFY(decoSpy.isValid()); - VERIFY(decoSpy.wait()); - deco->requestMode(ServerSideDecoration::Mode::Server); - VERIFY(decoSpy.wait()); - COMPARE(deco->mode(), ServerSideDecoration::Mode::Server); + Test::XdgToplevelDecorationV1 *decoration = Test::createXdgToplevelDecorationV1(shellSurface, shellSurface); + VERIFY(decoration); + + QSignalSpy decorationConfigureRequestedSpy(decoration, &Test::XdgToplevelDecorationV1::configureRequested); + QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested); + + decoration->set_mode(Test::XdgToplevelDecorationV1::mode_server_side); + surface->commit(KWayland::Client::Surface::CommitFlag::None); + VERIFY(surfaceConfigureRequestedSpy.wait()); + COMPARE(decorationConfigureRequestedSpy.last().at(0).value(), Test::XdgToplevelDecorationV1::mode_server_side); + // let's render + shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value()); auto c = Test::renderAndWaitForShown(surface, QSize(500, 50), Qt::blue); VERIFY(c); COMPARE(workspace()->activeClient(), c); @@ -149,7 +153,7 @@ void DecorationInputTest::initTestCase() void DecorationInputTest::init() { using namespace KWayland::Client; - QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::Seat | Test::AdditionalWaylandInterface::Decoration)); + QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::Seat | Test::AdditionalWaylandInterface::XdgDecorationV1)); QVERIFY(Test::waitForWaylandPointer()); workspace()->setActiveOutput(QPoint(640, 512)); diff --git a/autotests/integration/dont_crash_no_border.cpp b/autotests/integration/dont_crash_no_border.cpp index 8bdfaef1d7..b194178424 100644 --- a/autotests/integration/dont_crash_no_border.cpp +++ b/autotests/integration/dont_crash_no_border.cpp @@ -20,7 +20,6 @@ #include "workspace.h" #include -#include #include #include @@ -72,7 +71,7 @@ void DontCrashNoBorder::initTestCase() void DontCrashNoBorder::init() { - QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::Decoration)); + QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::XdgDecorationV1)); workspace()->setActiveOutput(QPoint(640, 512)); Cursors::self()->mouse()->setPos(QPoint(640, 512)); @@ -86,20 +85,20 @@ void DontCrashNoBorder::cleanup() void DontCrashNoBorder::testCreateWindow() { // create a window and ensure that this doesn't crash - using namespace KWayland::Client; - QScopedPointer surface(Test::createSurface()); - QVERIFY(!surface.isNull()); - QScopedPointer shellSurface(Test::createXdgToplevelSurface(surface.data())); - QVERIFY(shellSurface); - QScopedPointer deco(Test::waylandServerSideDecoration()->create(surface.data())); - QSignalSpy decoSpy(deco.data(), &ServerSideDecoration::modeChanged); - QVERIFY(decoSpy.isValid()); - QVERIFY(decoSpy.wait()); - deco->requestMode(ServerSideDecoration::Mode::Server); - QVERIFY(decoSpy.wait()); - QCOMPARE(deco->mode(), ServerSideDecoration::Mode::Server); + QScopedPointer shellSurface(Test::createXdgToplevelSurface(surface.data(), Test::CreationSetup::CreateOnly)); + QScopedPointer decoration(Test::createXdgToplevelDecorationV1(shellSurface.data())); + QSignalSpy decorationConfigureRequestedSpy(decoration.data(), &Test::XdgToplevelDecorationV1::configureRequested); + QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested); + + // Initialize the xdg-toplevel surface. + decoration->set_mode(Test::XdgToplevelDecorationV1::mode_server_side); + surface->commit(KWayland::Client::Surface::CommitFlag::None); + QVERIFY(surfaceConfigureRequestedSpy.wait()); + QCOMPARE(decorationConfigureRequestedSpy.last().at(0).value(), Test::XdgToplevelDecorationV1::mode_client_side); + // let's render + shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value()); auto c = Test::renderAndWaitForShown(surface.data(), QSize(500, 50), Qt::blue); QVERIFY(c); QCOMPARE(workspace()->activeClient(), c); diff --git a/autotests/integration/effects/popup_open_close_animation_test.cpp b/autotests/integration/effects/popup_open_close_animation_test.cpp index 230c9b220b..cbb41b92a2 100644 --- a/autotests/integration/effects/popup_open_close_animation_test.cpp +++ b/autotests/integration/effects/popup_open_close_animation_test.cpp @@ -213,11 +213,17 @@ void PopupOpenCloseAnimationTest::testAnimateDecorationTooltips() using namespace KWayland::Client; QScopedPointer surface(Test::createSurface()); QVERIFY(!surface.isNull()); - QScopedPointer shellSurface(Test::createXdgToplevelSurface(surface.data())); + QScopedPointer shellSurface(Test::createXdgToplevelSurface(surface.data(), Test::CreationSetup::CreateOnly)); QVERIFY(!shellSurface.isNull()); QScopedPointer deco(Test::createXdgToplevelDecorationV1(shellSurface.data())); QVERIFY(!deco.isNull()); + + QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested); deco->set_mode(Test::XdgToplevelDecorationV1::mode_server_side); + surface->commit(KWayland::Client::Surface::CommitFlag::None); + QVERIFY(surfaceConfigureRequestedSpy.wait()); + + shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value()); AbstractClient *client = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue); QVERIFY(client); QVERIFY(client->isDecorated()); diff --git a/autotests/integration/maximize_test.cpp b/autotests/integration/maximize_test.cpp index 513be1ceca..b31da0aaf7 100644 --- a/autotests/integration/maximize_test.cpp +++ b/autotests/integration/maximize_test.cpp @@ -20,7 +20,6 @@ #include #include #include -#include #include #include @@ -68,8 +67,7 @@ void TestMaximized::initTestCase() void TestMaximized::init() { - QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::Decoration | - Test::AdditionalWaylandInterface::XdgDecorationV1 | + QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::XdgDecorationV1 | Test::AdditionalWaylandInterface::PlasmaShell)); workspace()->setActiveOutput(QPoint(640, 512)); @@ -94,9 +92,15 @@ void TestMaximized::testMaximizedPassedToDeco() // Create the test client. QScopedPointer surface(Test::createSurface()); - QScopedPointer shellSurface(Test::createXdgToplevelSurface(surface.data())); - QScopedPointer ssd(Test::waylandServerSideDecoration()->create(surface.data())); + QScopedPointer shellSurface(Test::createXdgToplevelSurface(surface.data(), Test::CreationSetup::CreateOnly)); + QScopedPointer xdgDecoration(Test::createXdgToplevelDecorationV1(shellSurface.data())); + QSignalSpy toplevelConfigureRequestedSpy(shellSurface.data(), &Test::XdgToplevel::configureRequested); + QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested); + surface->commit(KWayland::Client::Surface::CommitFlag::None); + QVERIFY(surfaceConfigureRequestedSpy.wait()); + + shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value()); auto client = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue); QVERIFY(client); QVERIFY(client->isDecorated()); @@ -106,10 +110,8 @@ void TestMaximized::testMaximizedPassedToDeco() QCOMPARE(client->maximizeMode(), MaximizeMode::MaximizeRestore); // Wait for configure event that signals the client is active now. - QSignalSpy toplevelConfigureRequestedSpy(shellSurface.data(), &Test::XdgToplevel::configureRequested); - QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested); QVERIFY(surfaceConfigureRequestedSpy.wait()); - QCOMPARE(surfaceConfigureRequestedSpy.count(), 1); + QCOMPARE(surfaceConfigureRequestedSpy.count(), 2); // When there are no borders, there is no change to them when maximizing. // TODO: we should test both cases with fixed fake decoration for autotests. @@ -125,7 +127,7 @@ void TestMaximized::testMaximizedPassedToDeco() workspace()->slotWindowMaximize(); QVERIFY(surfaceConfigureRequestedSpy.wait()); - QCOMPARE(surfaceConfigureRequestedSpy.count(), 2); + QCOMPARE(surfaceConfigureRequestedSpy.count(), 3); QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(1280, 1024 - decoration->borderTop())); shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value()); Test::render(surface.data(), toplevelConfigureRequestedSpy.last().at(0).toSize(), Qt::red); @@ -145,7 +147,7 @@ void TestMaximized::testMaximizedPassedToDeco() // now unmaximize again workspace()->slotWindowMaximize(); QVERIFY(surfaceConfigureRequestedSpy.wait()); - QCOMPARE(surfaceConfigureRequestedSpy.count(), 3); + QCOMPARE(surfaceConfigureRequestedSpy.count(), 4); QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(100, 50)); shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value()); diff --git a/autotests/integration/touch_input_test.cpp b/autotests/integration/touch_input_test.cpp index c8debc2c17..8c8b70ace2 100644 --- a/autotests/integration/touch_input_test.cpp +++ b/autotests/integration/touch_input_test.cpp @@ -19,7 +19,6 @@ #include #include #include -#include #include #include @@ -70,7 +69,6 @@ void TouchInputTest::init() { using namespace KWayland::Client; QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::Seat | - Test::AdditionalWaylandInterface::Decoration | Test::AdditionalWaylandInterface::XdgDecorationV1)); QVERIFY(Test::waitForWaylandTouch()); m_touch = Test::waylandSeat()->createTouch(Test::waylandSeat()); @@ -100,18 +98,17 @@ AbstractClient *TouchInputTest::showWindow(bool decorated) KWayland::Client::Surface *surface = Test::createSurface(Test::waylandCompositor()); VERIFY(surface); - Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface, surface); + Test::XdgToplevel *shellSurface = Test::createXdgToplevelSurface(surface, Test::CreationSetup::CreateOnly, surface); VERIFY(shellSurface); if (decorated) { - auto deco = Test::waylandServerSideDecoration()->create(surface, surface); - QSignalSpy decoSpy(deco, &ServerSideDecoration::modeChanged); - VERIFY(decoSpy.isValid()); - VERIFY(decoSpy.wait()); - deco->requestMode(ServerSideDecoration::Mode::Server); - VERIFY(decoSpy.wait()); - COMPARE(deco->mode(), ServerSideDecoration::Mode::Server); + auto decoration = Test::createXdgToplevelDecorationV1(shellSurface, shellSurface); + decoration->set_mode(Test::XdgToplevelDecorationV1::mode_server_side); } + QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested); + surface->commit(KWayland::Client::Surface::CommitFlag::None); + VERIFY(surfaceConfigureRequestedSpy.wait()); // let's render + shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value()); auto c = Test::renderAndWaitForShown(surface, QSize(100, 50), Qt::blue); VERIFY(c); diff --git a/autotests/integration/xdgshellclient_test.cpp b/autotests/integration/xdgshellclient_test.cpp index 849d3199c4..7855e85287 100644 --- a/autotests/integration/xdgshellclient_test.cpp +++ b/autotests/integration/xdgshellclient_test.cpp @@ -83,7 +83,6 @@ private Q_SLOTS: void testUnresponsiveWindow_data(); void testUnresponsiveWindow(); void testAppMenu(); - void testNoDecorationModeRequested(); void testSendClientWithTransientToDesktop(); void testMinimizeWindowWithTransients(); void testXdgDecoration_data(); @@ -187,8 +186,7 @@ void TestXdgShellClient::initTestCase() void TestXdgShellClient::init() { - QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::Decoration | - Test::AdditionalWaylandInterface::Seat | + QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::Seat | Test::AdditionalWaylandInterface::XdgDecorationV1 | Test::AdditionalWaylandInterface::AppMenu)); QVERIFY(Test::waitForWaylandPointer()); @@ -369,10 +367,10 @@ void TestXdgShellClient::testMinimizeActiveWindow() void TestXdgShellClient::testFullscreen_data() { - QTest::addColumn("decoMode"); + QTest::addColumn("decoMode"); - QTest::newRow("client-side deco") << ServerSideDecoration::Mode::Client; - QTest::newRow("server-side deco") << ServerSideDecoration::Mode::Server; + QTest::newRow("client-side deco") << Test::XdgToplevelDecorationV1::mode_client_side; + QTest::newRow("server-side deco") << Test::XdgToplevelDecorationV1::mode_server_side; } void TestXdgShellClient::testFullscreen() @@ -382,49 +380,43 @@ void TestXdgShellClient::testFullscreen() Test::XdgToplevel::States states; QScopedPointer surface(Test::createSurface()); - QScopedPointer shellSurface(Test::createXdgToplevelSurface(surface.data())); - QVERIFY(shellSurface); + QScopedPointer shellSurface(Test::createXdgToplevelSurface(surface.data(), Test::CreationSetup::CreateOnly)); + QScopedPointer decoration(Test::createXdgToplevelDecorationV1(shellSurface.data())); + QSignalSpy decorationConfigureRequestedSpy(decoration.data(), &Test::XdgToplevelDecorationV1::configureRequested); + QSignalSpy toplevelConfigureRequestedSpy(shellSurface.data(), &Test::XdgToplevel::configureRequested); + QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested); - // create deco - QScopedPointer deco(Test::waylandServerSideDecoration()->create(surface.data())); - QSignalSpy decoSpy(deco.data(), &ServerSideDecoration::modeChanged); - QVERIFY(decoSpy.isValid()); - QVERIFY(decoSpy.wait()); - QFETCH(ServerSideDecoration::Mode, decoMode); - deco->requestMode(decoMode); - QVERIFY(decoSpy.wait()); - QCOMPARE(deco->mode(), decoMode); + // Initialize the xdg-toplevel surface. + QFETCH(Test::XdgToplevelDecorationV1::mode, decoMode); + decoration->set_mode(decoMode); + surface->commit(KWayland::Client::Surface::CommitFlag::None); + QVERIFY(surfaceConfigureRequestedSpy.wait()); + shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value()); auto client = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue); QVERIFY(client); QVERIFY(client->isActive()); QCOMPARE(client->layer(), NormalLayer); QVERIFY(!client->isFullScreen()); QCOMPARE(client->clientSize(), QSize(100, 50)); - QCOMPARE(client->isDecorated(), decoMode == ServerSideDecoration::Mode::Server); + QCOMPARE(client->isDecorated(), decoMode == Test::XdgToplevelDecorationV1::mode_server_side); QCOMPARE(client->clientSizeToFrameSize(client->clientSize()), client->size()); QSignalSpy fullScreenChangedSpy(client, &AbstractClient::fullScreenChanged); QVERIFY(fullScreenChangedSpy.isValid()); QSignalSpy frameGeometryChangedSpy(client, &AbstractClient::frameGeometryChanged); QVERIFY(frameGeometryChangedSpy.isValid()); - QSignalSpy toplevelConfigureRequestedSpy(shellSurface.data(), &Test::XdgToplevel::configureRequested); - QVERIFY(toplevelConfigureRequestedSpy.isValid()); - QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested); - QVERIFY(surfaceConfigureRequestedSpy.isValid()); // Wait for the compositor to send a configure event with the Activated state. QVERIFY(surfaceConfigureRequestedSpy.wait()); - QCOMPARE(surfaceConfigureRequestedSpy.count(), 1); - QCOMPARE(toplevelConfigureRequestedSpy.count(), 1); + QCOMPARE(surfaceConfigureRequestedSpy.count(), 2); states = toplevelConfigureRequestedSpy.last().at(1).value(); QVERIFY(states & Test::XdgToplevel::State::Activated); // Ask the compositor to show the window in full screen mode. shellSurface->set_fullscreen(nullptr); QVERIFY(surfaceConfigureRequestedSpy.wait()); - QCOMPARE(surfaceConfigureRequestedSpy.count(), 2); - QCOMPARE(surfaceConfigureRequestedSpy.count(), 2); + QCOMPARE(surfaceConfigureRequestedSpy.count(), 3); states = toplevelConfigureRequestedSpy.last().at(1).value(); QVERIFY(states & Test::XdgToplevel::State::Fullscreen); QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value(), client->output()->geometry().size()); @@ -442,8 +434,7 @@ void TestXdgShellClient::testFullscreen() // Ask the compositor to show the window in normal mode. shellSurface->unset_fullscreen(); QVERIFY(surfaceConfigureRequestedSpy.wait()); - QCOMPARE(surfaceConfigureRequestedSpy.count(), 3); - QCOMPARE(toplevelConfigureRequestedSpy.count(), 3); + QCOMPARE(surfaceConfigureRequestedSpy.count(), 4); states = toplevelConfigureRequestedSpy.last().at(1).value(); QVERIFY(!(states & Test::XdgToplevel::State::Fullscreen)); QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value(), QSize(100, 50)); @@ -455,7 +446,7 @@ void TestXdgShellClient::testFullscreen() QCOMPARE(fullScreenChangedSpy.count(), 2); QCOMPARE(client->clientSize(), QSize(100, 50)); QVERIFY(!client->isFullScreen()); - QCOMPARE(client->isDecorated(), decoMode == ServerSideDecoration::Mode::Server); + QCOMPARE(client->isDecorated(), decoMode == Test::XdgToplevelDecorationV1::mode_server_side); QCOMPARE(client->layer(), NormalLayer); // Destroy the client. @@ -476,10 +467,10 @@ void TestXdgShellClient::testUserCanSetFullscreen() void TestXdgShellClient::testMaximizedToFullscreen_data() { - QTest::addColumn("decoMode"); + QTest::addColumn("decoMode"); - QTest::newRow("client-side deco") << ServerSideDecoration::Mode::Client; - QTest::newRow("server-side deco") << ServerSideDecoration::Mode::Server; + QTest::newRow("client-side deco") << Test::XdgToplevelDecorationV1::mode_client_side; + QTest::newRow("server-side deco") << Test::XdgToplevelDecorationV1::mode_server_side; } void TestXdgShellClient::testMaximizedToFullscreen() @@ -489,45 +480,41 @@ void TestXdgShellClient::testMaximizedToFullscreen() Test::XdgToplevel::States states; QScopedPointer surface(Test::createSurface()); - QScopedPointer shellSurface(Test::createXdgToplevelSurface(surface.data())); - QVERIFY(shellSurface); + QScopedPointer shellSurface(Test::createXdgToplevelSurface(surface.data(), Test::CreationSetup::CreateOnly)); + QScopedPointer decoration(Test::createXdgToplevelDecorationV1(shellSurface.data())); + QSignalSpy decorationConfigureRequestedSpy(decoration.data(), &Test::XdgToplevelDecorationV1::configureRequested); + QSignalSpy toplevelConfigureRequestedSpy(shellSurface.data(), &Test::XdgToplevel::configureRequested); + QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested); - // create deco - QScopedPointer deco(Test::waylandServerSideDecoration()->create(surface.data())); - QSignalSpy decoSpy(deco.data(), &ServerSideDecoration::modeChanged); - QVERIFY(decoSpy.isValid()); - QVERIFY(decoSpy.wait()); - QFETCH(ServerSideDecoration::Mode, decoMode); - deco->requestMode(decoMode); - QVERIFY(decoSpy.wait()); - QCOMPARE(deco->mode(), decoMode); + // Initialize the xdg-toplevel surface. + QFETCH(Test::XdgToplevelDecorationV1::mode, decoMode); + decoration->set_mode(decoMode); + surface->commit(KWayland::Client::Surface::CommitFlag::None); + QVERIFY(surfaceConfigureRequestedSpy.wait()); + shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value()); auto client = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue); QVERIFY(client); QVERIFY(client->isActive()); QVERIFY(!client->isFullScreen()); QCOMPARE(client->clientSize(), QSize(100, 50)); - QCOMPARE(client->isDecorated(), decoMode == ServerSideDecoration::Mode::Server); + QCOMPARE(client->isDecorated(), decoMode == Test::XdgToplevelDecorationV1::mode_server_side); QSignalSpy fullscreenChangedSpy(client, &AbstractClient::fullScreenChanged); QVERIFY(fullscreenChangedSpy.isValid()); QSignalSpy frameGeometryChangedSpy(client, &AbstractClient::frameGeometryChanged); QVERIFY(frameGeometryChangedSpy.isValid()); - QSignalSpy toplevelConfigureRequestedSpy(shellSurface.data(), &Test::XdgToplevel::configureRequested); - QVERIFY(toplevelConfigureRequestedSpy.isValid()); - QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested); - QVERIFY(surfaceConfigureRequestedSpy.isValid()); // Wait for the compositor to send a configure event with the Activated state. QVERIFY(surfaceConfigureRequestedSpy.wait()); - QCOMPARE(surfaceConfigureRequestedSpy.count(), 1); + QCOMPARE(surfaceConfigureRequestedSpy.count(), 2); states = toplevelConfigureRequestedSpy.last().at(1).value(); QVERIFY(states & Test::XdgToplevel::State::Activated); // Ask the compositor to maximize the window. shellSurface->set_maximized(); QVERIFY(surfaceConfigureRequestedSpy.wait()); - QCOMPARE(surfaceConfigureRequestedSpy.count(), 2); + QCOMPARE(surfaceConfigureRequestedSpy.count(), 3); states = toplevelConfigureRequestedSpy.last().at(1).value(); QVERIFY(states & Test::XdgToplevel::State::Maximized); @@ -539,7 +526,7 @@ void TestXdgShellClient::testMaximizedToFullscreen() // Ask the compositor to show the window in full screen mode. shellSurface->set_fullscreen(nullptr); QVERIFY(surfaceConfigureRequestedSpy.wait()); - QCOMPARE(surfaceConfigureRequestedSpy.count(), 3); + QCOMPARE(surfaceConfigureRequestedSpy.count(), 4); QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value(), client->output()->geometry().size()); states = toplevelConfigureRequestedSpy.last().at(1).value(); QVERIFY(states & Test::XdgToplevel::State::Maximized); @@ -558,7 +545,7 @@ void TestXdgShellClient::testMaximizedToFullscreen() shellSurface->unset_fullscreen(); shellSurface->unset_maximized(); QVERIFY(surfaceConfigureRequestedSpy.wait()); - QCOMPARE(surfaceConfigureRequestedSpy.count(), 4); + QCOMPARE(surfaceConfigureRequestedSpy.count(), 5); QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value(), QSize(100, 50)); states = toplevelConfigureRequestedSpy.last().at(1).value(); QVERIFY(!(states & Test::XdgToplevel::State::Maximized)); @@ -569,7 +556,7 @@ void TestXdgShellClient::testMaximizedToFullscreen() QVERIFY(frameGeometryChangedSpy.wait()); QVERIFY(!client->isFullScreen()); - QCOMPARE(client->isDecorated(), decoMode == ServerSideDecoration::Mode::Server); + QCOMPARE(client->isDecorated(), decoMode == Test::XdgToplevelDecorationV1::mode_server_side); QCOMPARE(client->maximizeMode(), MaximizeRestore); // Destroy the client. @@ -634,18 +621,22 @@ void TestXdgShellClient::testWindowOpensLargerThanScreen() { // this test creates a window which is as large as the screen, but is decorated // the window should get resized to fit into the screen, BUG: 366632 + QScopedPointer surface(Test::createSurface()); - QScopedPointer shellSurface(Test::createXdgToplevelSurface(surface.data())); + QScopedPointer shellSurface(Test::createXdgToplevelSurface(surface.data(), Test::CreationSetup::CreateOnly)); + QScopedPointer decoration(Test::createXdgToplevelDecorationV1(shellSurface.data())); + QSignalSpy decorationConfigureRequestedSpy(decoration.data(), &Test::XdgToplevelDecorationV1::configureRequested); + QSignalSpy toplevelConfigureRequestedSpy(shellSurface.data(), &Test::XdgToplevel::configureRequested); + QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested); - // create deco - QScopedPointer deco(Test::waylandServerSideDecoration()->create(surface.data())); - QSignalSpy decoSpy(deco.data(), &ServerSideDecoration::modeChanged); - QVERIFY(decoSpy.isValid()); - QVERIFY(decoSpy.wait()); - deco->requestMode(ServerSideDecoration::Mode::Server); - QVERIFY(decoSpy.wait()); - QCOMPARE(deco->mode(), ServerSideDecoration::Mode::Server); + // Initialize the xdg-toplevel surface. + decoration->set_mode(Test::XdgToplevelDecorationV1::mode_server_side); + surface->commit(KWayland::Client::Surface::CommitFlag::None); + QVERIFY(surfaceConfigureRequestedSpy.wait()); + QCOMPARE(decorationConfigureRequestedSpy.last().at(0).value(), Test::XdgToplevelDecorationV1::mode_server_side); + + shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value()); AbstractOutput *output = workspace()->activeOutput(); auto c = Test::renderAndWaitForShown(surface.data(), output->geometry().size(), Qt::blue); QVERIFY(c); @@ -886,25 +877,6 @@ void TestXdgShellClient::testAppMenu() QVERIFY (QDBusConnection::sessionBus().unregisterService("org.kde.kappmenu")); } -void TestXdgShellClient::testNoDecorationModeRequested() -{ - // this test verifies that the decoration follows the default mode if no mode is explicitly requested - QScopedPointer surface(Test::createSurface()); - QScopedPointer shellSurface(Test::createXdgToplevelSurface(surface.data())); - QScopedPointer deco(Test::waylandServerSideDecoration()->create(surface.data())); - QSignalSpy decoSpy(deco.data(), &ServerSideDecoration::modeChanged); - QVERIFY(decoSpy.isValid()); - if (deco->mode() != ServerSideDecoration::Mode::Server) { - QVERIFY(decoSpy.wait()); - } - QCOMPARE(deco->mode(), ServerSideDecoration::Mode::Server); - - auto c = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue); - QVERIFY(c); - QCOMPARE(c->noBorder(), false); - QCOMPARE(c->isDecorated(), true); -} - void TestXdgShellClient::testSendClientWithTransientToDesktop() { // this test verifies that when sending a client to a desktop all transients are also send to that desktop diff --git a/src/abstract_client.cpp b/src/abstract_client.cpp index d83bf9c7e6..ac87702b43 100644 --- a/src/abstract_client.cpp +++ b/src/abstract_client.cpp @@ -2310,7 +2310,7 @@ void AbstractClient::endInteractiveMoveResize() void AbstractClient::createDecoration(const QRect &oldGeometry) { - setDecoration(Decoration::DecorationBridge::self()->createDecoration(this)); + setDecoration(QSharedPointer(Decoration::DecorationBridge::self()->createDecoration(this))); moveResize(oldGeometry); Q_EMIT geometryShapeChanged(this, oldGeometry); @@ -2323,19 +2323,19 @@ void AbstractClient::destroyDecoration() resize(clientSize); } -void AbstractClient::setDecoration(KDecoration2::Decoration *decoration) +void AbstractClient::setDecoration(QSharedPointer decoration) { if (m_decoration.decoration.data() == decoration) { return; } if (decoration) { - QMetaObject::invokeMethod(decoration, QOverload<>::of(&KDecoration2::Decoration::update), Qt::QueuedConnection); - connect(decoration, &KDecoration2::Decoration::shadowChanged, this, &Toplevel::updateShadow); - connect(decoration, &KDecoration2::Decoration::bordersChanged, + QMetaObject::invokeMethod(decoration.data(), QOverload<>::of(&KDecoration2::Decoration::update), Qt::QueuedConnection); + connect(decoration.data(), &KDecoration2::Decoration::shadowChanged, this, &Toplevel::updateShadow); + connect(decoration.data(), &KDecoration2::Decoration::bordersChanged, this, &AbstractClient::updateDecorationInputShape); - connect(decoration, &KDecoration2::Decoration::resizeOnlyBordersChanged, + connect(decoration.data(), &KDecoration2::Decoration::resizeOnlyBordersChanged, this, &AbstractClient::updateDecorationInputShape); - connect(decoration, &KDecoration2::Decoration::bordersChanged, this, [this]() { + connect(decoration.data(), &KDecoration2::Decoration::bordersChanged, this, [this]() { GeometryUpdatesBlocker blocker(this); const QRect oldGeometry = frameGeometry(); resize(implicitSize()); @@ -2347,7 +2347,7 @@ void AbstractClient::setDecoration(KDecoration2::Decoration *decoration) connect(decoratedClient()->decoratedClient(), &KDecoration2::DecoratedClient::sizeChanged, this, &AbstractClient::updateDecorationInputShape); } - m_decoration.decoration.reset(decoration); + m_decoration.decoration = decoration; updateDecorationInputShape(); Q_EMIT decorationChanged(); } diff --git a/src/abstract_client.h b/src/abstract_client.h index f52732db53..a9505a17f4 100644 --- a/src/abstract_client.h +++ b/src/abstract_client.h @@ -1197,7 +1197,7 @@ protected: s_haveResizeEffect = false; } - void setDecoration(KDecoration2::Decoration *decoration); + void setDecoration(QSharedPointer decoration); virtual void createDecoration(const QRect &oldGeometry); virtual void destroyDecoration(); void startDecorationDoubleClickTimer(); @@ -1313,7 +1313,7 @@ private: } m_interactiveMoveResize; struct { - QScopedPointer decoration; + QSharedPointer decoration; QPointer client; QElapsedTimer doubleClickTimer; QRegion inputRegion; diff --git a/src/x11client.cpp b/src/x11client.cpp index c6bd73fab2..e71139b8df 100644 --- a/src/x11client.cpp +++ b/src/x11client.cpp @@ -1117,10 +1117,10 @@ void X11Client::invalidateDecoration() void X11Client::createDecoration(const QRect& oldgeom) { - KDecoration2::Decoration *decoration = Decoration::DecorationBridge::self()->createDecoration(this); + QSharedPointer decoration(Decoration::DecorationBridge::self()->createDecoration(this)); if (decoration) { - connect(decoration, &KDecoration2::Decoration::resizeOnlyBordersChanged, this, &X11Client::updateInputWindow); - connect(decoration, &KDecoration2::Decoration::bordersChanged, this, &X11Client::updateFrameExtents); + connect(decoration.data(), &KDecoration2::Decoration::resizeOnlyBordersChanged, this, &X11Client::updateInputWindow); + connect(decoration.data(), &KDecoration2::Decoration::bordersChanged, this, &X11Client::updateFrameExtents); connect(decoratedClient()->decoratedClient(), &KDecoration2::DecoratedClient::sizeChanged, this, &X11Client::updateInputWindow); } setDecoration(decoration); diff --git a/src/xdgshellclient.cpp b/src/xdgshellclient.cpp index 06237932a7..c30a11bda4 100644 --- a/src/xdgshellclient.cpp +++ b/src/xdgshellclient.cpp @@ -159,6 +159,7 @@ void XdgSurfaceClient::handleCommit() return; } + handleRolePrecommit(); if (haveNextWindowGeometry()) { handleNextWindowGeometry(); resetHaveNextWindowGeometry(); @@ -171,6 +172,10 @@ void XdgSurfaceClient::handleCommit() updateDepth(); } +void XdgSurfaceClient::handleRolePrecommit() +{ +} + void XdgSurfaceClient::handleRoleCommit() { } @@ -738,6 +743,7 @@ bool XdgToplevelClient::userCanSetNoBorder() const case XdgToplevelDecorationV1Interface::Mode::Server: case XdgToplevelDecorationV1Interface::Mode::Undefined: return Decoration::DecorationBridge::hasPlugin() && !isFullScreen() && !isShade(); + case XdgToplevelDecorationV1Interface::Mode::None: case XdgToplevelDecorationV1Interface::Mode::Client: return false; } @@ -747,25 +753,7 @@ bool XdgToplevelClient::userCanSetNoBorder() const bool XdgToplevelClient::noBorder() const { - if (m_serverDecoration) { - switch (m_serverDecoration->preferredMode()) { - case ServerSideDecorationManagerInterface::Mode::Server: - return m_userNoBorder || isRequestedFullScreen(); - case ServerSideDecorationManagerInterface::Mode::Client: - case ServerSideDecorationManagerInterface::Mode::None: - return true; - } - } - if (m_xdgDecoration) { - switch (m_xdgDecoration->preferredMode()) { - case XdgToplevelDecorationV1Interface::Mode::Server: - case XdgToplevelDecorationV1Interface::Mode::Undefined: - return !Decoration::DecorationBridge::hasPlugin() || m_userNoBorder || isRequestedFullScreen(); - case XdgToplevelDecorationV1Interface::Mode::Client: - return true; - } - } - return true; + return m_userNoBorder || preferredDecorationMode() != DecorationMode::Server; } void XdgToplevelClient::setNoBorder(bool set) @@ -778,45 +766,14 @@ void XdgToplevelClient::setNoBorder(bool set) return; } m_userNoBorder = set; - updateDecoration(true, false); + configureDecoration(); updateWindowRules(Rules::NoBorder); } -void XdgToplevelClient::updateDecoration(bool check_workspace_pos, bool force) -{ - if (!force && ((!isDecorated() && noBorder()) || (isDecorated() && !noBorder()))) { - return; - } - const QRect oldFrameGeometry = frameGeometry(); - const QRect oldClientGeometry = clientGeometry(); - if (force) { - destroyDecoration(); - } - if (!noBorder()) { - createDecoration(oldFrameGeometry); - } else { - destroyDecoration(); - } - if (m_serverDecoration && isDecorated()) { - m_serverDecoration->setMode(ServerSideDecorationManagerInterface::Mode::Server); - } - if (m_xdgDecoration) { - if (isDecorated() || m_userNoBorder) { - m_xdgDecoration->sendConfigure(XdgToplevelDecorationV1Interface::Mode::Server); - } else { - m_xdgDecoration->sendConfigure(XdgToplevelDecorationV1Interface::Mode::Client); - } - scheduleConfigure(); - } - updateShadow(); - if (check_workspace_pos) { - checkWorkspacePosition(oldFrameGeometry, oldClientGeometry); - } -} - void XdgToplevelClient::invalidateDecoration() { - updateDecoration(true, true); + clearDecoration(); + configureDecoration(); } bool XdgToplevelClient::supportsWindowRules() const @@ -902,24 +859,40 @@ void XdgToplevelClient::closeWindow() XdgSurfaceConfigure *XdgToplevelClient::sendRoleConfigure() const { - const QSize requestedClientSize = frameSizeToClientSize(moveResizeGeometry().size()); - const quint32 serial = m_shellSurface->sendConfigure(requestedClientSize, m_requestedStates); + QSize nextClientSize = moveResizeGeometry().size(); + if (!nextClientSize.isEmpty()) { + if (m_nextDecoration) { + nextClientSize.rwidth() -= m_nextDecoration->borderLeft() + m_nextDecoration->borderRight(); + nextClientSize.rheight() -= m_nextDecoration->borderTop() + m_nextDecoration->borderBottom(); + } + } + + const quint32 serial = m_shellSurface->sendConfigure(nextClientSize, m_requestedStates); XdgToplevelConfigure *configureEvent = new XdgToplevelConfigure(); configureEvent->position = moveResizeGeometry().topLeft(); configureEvent->states = m_requestedStates; + configureEvent->decoration = m_nextDecoration; configureEvent->serial = serial; return configureEvent; } +void XdgToplevelClient::handleRolePrecommit() +{ + auto configureEvent = static_cast(lastAcknowledgedConfigure()); + if (configureEvent && decoration() != configureEvent->decoration) { + setDecoration(configureEvent->decoration); + updateShadow(); + } +} + void XdgToplevelClient::handleRoleCommit() { auto configureEvent = static_cast(lastAcknowledgedConfigure()); if (configureEvent) { handleStatesAcknowledged(configureEvent->states); } - updateDecoration(true, false); } void XdgToplevelClient::doMinimize() @@ -1365,11 +1338,6 @@ bool XdgToplevelClient::initialFullScreenMode() const void XdgToplevelClient::initialize() { bool needsPlacement = isPlaceable(); - - // Decoration update is forced so an xdg_toplevel_decoration.configure event - // is sent if the client has called the set_mode() request with csd mode. - updateDecoration(false, true); - setupWindowRules(false); // Move or resize the window only if enforced by a window rule. @@ -1420,6 +1388,7 @@ void XdgToplevelClient::initialize() Placement::self()->place(this, area); } + configureDecoration(); scheduleConfigure(); updateColorScheme(); setupWindowManagementInterface(); @@ -1470,36 +1439,126 @@ void XdgToplevelClient::installAppMenu(AppMenuInterface *appMenu) updateMenu(appMenu->address()); } -void XdgToplevelClient::installServerDecoration(ServerSideDecorationInterface *decoration) +XdgToplevelClient::DecorationMode XdgToplevelClient::preferredDecorationMode() const { - m_serverDecoration = decoration; - - connect(m_serverDecoration, &ServerSideDecorationInterface::destroyed, this, [this] { - if (!isZombie() && readyForPainting()) { - updateDecoration(/* check_workspace_pos */ true); - } - }); - connect(m_serverDecoration, &ServerSideDecorationInterface::preferredModeChanged, this, - [this] () { - const bool changed = m_serverDecoration->preferredMode() != m_serverDecoration->mode(); - if (changed && readyForPainting()) { - updateDecoration(/* check_workspace_pos */ true); - } - } - ); - if (readyForPainting()) { - updateDecoration(/* check_workspace_pos */ true); + if (!Decoration::DecorationBridge::hasPlugin()) { + return DecorationMode::Client; + } else if (m_userNoBorder || isRequestedFullScreen()) { + return DecorationMode::None; } + + if (m_xdgDecoration) { + switch (m_xdgDecoration->preferredMode()) { + case XdgToplevelDecorationV1Interface::Mode::Undefined: + return DecorationMode::Server; + case XdgToplevelDecorationV1Interface::Mode::None: + return DecorationMode::None; + case XdgToplevelDecorationV1Interface::Mode::Client: + return DecorationMode::Client; + case XdgToplevelDecorationV1Interface::Mode::Server: + return DecorationMode::Server; + } + } + + if (m_serverDecoration) { + switch (m_serverDecoration->preferredMode()) { + case ServerSideDecorationManagerInterface::Mode::None: + return DecorationMode::None; + case ServerSideDecorationManagerInterface::Mode::Client: + return DecorationMode::Client; + case ServerSideDecorationManagerInterface::Mode::Server: + return DecorationMode::Server; + } + } + + return DecorationMode::Client; +} + +void XdgToplevelClient::clearDecoration() +{ + m_nextDecoration = nullptr; +} + +void XdgToplevelClient::configureDecoration() +{ + const DecorationMode decorationMode = preferredDecorationMode(); + switch (decorationMode) { + case DecorationMode::None: + case DecorationMode::Client: + clearDecoration(); + break; + case DecorationMode::Server: + if (!m_nextDecoration) { + m_nextDecoration.reset(Decoration::DecorationBridge::self()->createDecoration(this)); + } + break; + } + + // All decoration updates are synchronized to toplevel configure events. + if (m_xdgDecoration) { + configureXdgDecoration(decorationMode); + } else if (m_serverDecoration) { + configureServerDecoration(decorationMode); + } +} + +void XdgToplevelClient::configureXdgDecoration(DecorationMode decorationMode) +{ + switch (decorationMode) { + case DecorationMode::None: // Faked as server side mode under the hood. + m_xdgDecoration->sendConfigure(XdgToplevelDecorationV1Interface::Mode::None); + break; + case DecorationMode::Client: + m_xdgDecoration->sendConfigure(XdgToplevelDecorationV1Interface::Mode::Client); + break; + case DecorationMode::Server: + m_xdgDecoration->sendConfigure(XdgToplevelDecorationV1Interface::Mode::Server); + break; + } + scheduleConfigure(); +} + +void XdgToplevelClient::configureServerDecoration(DecorationMode decorationMode) +{ + switch (decorationMode) { + case DecorationMode::None: + m_serverDecoration->setMode(ServerSideDecorationManagerInterface::Mode::None); + break; + case DecorationMode::Client: + m_serverDecoration->setMode(ServerSideDecorationManagerInterface::Mode::Client); + break; + case DecorationMode::Server: + m_serverDecoration->setMode(ServerSideDecorationManagerInterface::Mode::Server); + break; + } + scheduleConfigure(); } void XdgToplevelClient::installXdgDecoration(XdgToplevelDecorationV1Interface *decoration) { m_xdgDecoration = decoration; + connect(m_xdgDecoration, &XdgToplevelDecorationV1Interface::destroyed, + this, &XdgToplevelClient::clearDecoration); connect(m_xdgDecoration, &XdgToplevelDecorationV1Interface::preferredModeChanged, this, [this] { if (m_isInitialized) { - // force is true as we must send a new configure response. - updateDecoration(/* check_workspace_pos */ false, /* force */ true); + configureDecoration(); + } + }); +} + +void XdgToplevelClient::installServerDecoration(ServerSideDecorationInterface *decoration) +{ + m_serverDecoration = decoration; + if (m_isInitialized) { + configureDecoration(); + } + + connect(m_serverDecoration, &ServerSideDecorationInterface::destroyed, + this, &XdgToplevelClient::clearDecoration); + connect(m_serverDecoration, &ServerSideDecorationInterface::preferredModeChanged, this, [this]() { + if (m_isInitialized) { + configureDecoration(); } }); } @@ -1542,7 +1601,7 @@ void XdgToplevelClient::setFullScreen(bool set, bool user) dontInteractiveMoveResize(); } - updateDecoration(false, false); + configureDecoration(); if (set) { const AbstractOutput *output = m_fullScreenRequestedOutput ? m_fullScreenRequestedOutput.data() : kwinApp()->platform()->outputAt(moveResizeGeometry().center()); diff --git a/src/xdgshellclient.h b/src/xdgshellclient.h index fd10e73062..1352426177 100644 --- a/src/xdgshellclient.h +++ b/src/xdgshellclient.h @@ -67,6 +67,7 @@ protected: virtual XdgSurfaceConfigure *sendRoleConfigure() const = 0; virtual void handleRoleCommit(); + virtual void handleRolePrecommit(); XdgSurfaceConfigure *lastAcknowledgedConfigure() const; void scheduleConfigure(); @@ -102,6 +103,7 @@ private: class XdgToplevelConfigure final : public XdgSurfaceConfigure { public: + QSharedPointer decoration; KWaylandServer::XdgToplevelInterface::States states; }; @@ -114,6 +116,12 @@ class XdgToplevelClient final : public XdgSurfaceClient FocusWindow, }; + enum class DecorationMode { + None, + Client, + Server, + }; + public: explicit XdgToplevelClient(KWaylandServer::XdgToplevelInterface *shellSurface); ~XdgToplevelClient() override; @@ -159,6 +167,7 @@ public: protected: XdgSurfaceConfigure *sendRoleConfigure() const override; void handleRoleCommit() override; + void handleRolePrecommit() override; void doMinimize() override; void doInteractiveResizeSync() override; void doSetActive() override; @@ -197,7 +206,11 @@ private: void sendPing(PingReason reason); MaximizeMode initialMaximizeMode() const; bool initialFullScreenMode() const; - void updateDecoration(bool check_workspace_pos, bool force = false); + DecorationMode preferredDecorationMode() const; + void configureDecoration(); + void configureXdgDecoration(DecorationMode decorationMode); + void configureServerDecoration(DecorationMode decorationMode); + void clearDecoration(); QPointer m_appMenuInterface; QPointer m_paletteInterface; @@ -216,6 +229,7 @@ private: bool m_userNoBorder = false; bool m_isTransient = false; QPointer m_fullScreenRequestedOutput; + QSharedPointer m_nextDecoration; }; class XdgPopupClient final : public XdgSurfaceClient