Commit acb0683e authored by Vlad Zahorodnii's avatar Vlad Zahorodnii
Browse files

wayland: Properly handle async xdg-decoration updates

Currently, if a window switches between SSD and CSD, it is possible to
encounter a "corrupted" state where the server-side decoration is wrapped
around the window while it still has the client-side decoration.

The xdg-decoration protocol fixes this problem by saying that decoration
updates are bound to xdg_surface configure events.

At the moment, kwin sort of applies decoration updates immediately. With
this change, decoration updates will be done according to the spec.

If the compositor wants to create a decoration, it will send a configure
event and apply the decoration when the configure event is acked by the
client. In order to send the configure event with a good window geometry
size, kwin will create the decoration to query the border size but not
assign it to the client yet. As is, KDecoration api doesn't make
querying the border size ahead of time easy. The decoration plugin can
assign arbitrary border sizes to windows as it pleases it. We could change
that, but it effectively means starting KDecoration3 and setting existing
window deco ecosystem around kwin on fire the second time, that's off the
table.

If the compositor wants to remove the decoration, it will send a
configure event. When the configure event is acked and the surface is
committed, the window decoration will be destroyed.

Sync'ing decoration updates to configure events ensures that we cannot
end up with having both client-side and server-side decoration. It also
helps us to fix a bunch of geometry related issues caused by creating
and destroying the decoration without any surface buffer attached yet.

BUG: 445259
parent db996e08
......@@ -28,7 +28,6 @@
#include <KWayland/Client/compositor.h>
#include <KWayland/Client/keyboard.h>
#include <KWayland/Client/pointer.h>
#include <KWayland/Client/server_decoration.h>
#include <KWayland/Client/seat.h>
#include <KWayland/Client/shm_pool.h>
#include <KWayland/Client/surface.h>
......@@ -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>(), Test::XdgToplevelDecorationV1::mode_server_side);
// let's render
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
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));
......
......@@ -20,7 +20,6 @@
#include "workspace.h"
#include <kwineffects.h>
#include <KWayland/Client/server_decoration.h>
#include <KWayland/Client/surface.h>
#include <KDecoration2/Decoration>
......@@ -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<KWayland::Client::Surface> surface(Test::createSurface());
QVERIFY(!surface.isNull());
QScopedPointer<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.data()));
QVERIFY(shellSurface);
QScopedPointer<ServerSideDecoration> 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<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.data(), Test::CreationSetup::CreateOnly));
QScopedPointer<Test::XdgToplevelDecorationV1> 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>(), Test::XdgToplevelDecorationV1::mode_client_side);
// let's render
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
auto c = Test::renderAndWaitForShown(surface.data(), QSize(500, 50), Qt::blue);
QVERIFY(c);
QCOMPARE(workspace()->activeClient(), c);
......
......@@ -213,11 +213,17 @@ void PopupOpenCloseAnimationTest::testAnimateDecorationTooltips()
using namespace KWayland::Client;
QScopedPointer<KWayland::Client::Surface> surface(Test::createSurface());
QVERIFY(!surface.isNull());
QScopedPointer<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.data()));
QScopedPointer<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.data(), Test::CreationSetup::CreateOnly));
QVERIFY(!shellSurface.isNull());
QScopedPointer<Test::XdgToplevelDecorationV1> 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<quint32>());
AbstractClient *client = Test::renderAndWaitForShown(surface.data(), QSize(100, 50), Qt::blue);
QVERIFY(client);
QVERIFY(client->isDecorated());
......
......@@ -20,7 +20,6 @@
#include <KWayland/Client/compositor.h>
#include <KWayland/Client/shm_pool.h>
#include <KWayland/Client/surface.h>
#include <KWayland/Client/server_decoration.h>
#include <KWayland/Client/plasmashell.h>
#include <KDecoration2/DecoratedClient>
......@@ -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<KWayland::Client::Surface> surface(Test::createSurface());
QScopedPointer<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.data()));
QScopedPointer<ServerSideDecoration> ssd(Test::waylandServerSideDecoration()->create(surface.data()));
QScopedPointer<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.data(), Test::CreationSetup::CreateOnly));
QScopedPointer<Test::XdgToplevelDecorationV1> 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<quint32>());
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<quint32>());
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<quint32>());
......
......@@ -19,7 +19,6 @@
#include <KWayland/Client/compositor.h>
#include <KWayland/Client/connection_thread.h>
#include <KWayland/Client/seat.h>
#include <KWayland/Client/server_decoration.h>
#include <KWayland/Client/surface.h>
#include <KWayland/Client/touch.h>
......@@ -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<quint32>());
auto c = Test::renderAndWaitForShown(surface, QSize(100, 50), Qt::blue);
VERIFY(c);
......
......@@ -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<ServerSideDecoration::Mode>("decoMode");
QTest::addColumn<Test::XdgToplevelDecorationV1::mode>("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<KWayland::Client::Surface> surface(Test::createSurface());
QScopedPointer<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.data()));
QVERIFY(shellSurface);
// create deco
QScopedPointer<ServerSideDecoration> 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);
QScopedPointer<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.data(), Test::CreationSetup::CreateOnly));
QScopedPointer<Test::XdgToplevelDecorationV1> 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);
// 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<quint32>());
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<Test::XdgToplevel::States>();
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<Test::XdgToplevel::States>();
QVERIFY(states & Test::XdgToplevel::State::Fullscreen);
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), 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<Test::XdgToplevel::States>();
QVERIFY(!(states & Test::XdgToplevel::State::Fullscreen));
QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), 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<ServerSideDecoration::Mode>("decoMode");
QTest::addColumn<Test::XdgToplevelDecorationV1::mode>("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<KWayland::Client::Surface> surface(Test::createSurface());
QScopedPointer<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.data()));
QVERIFY(shellSurface);
// create deco
QScopedPointer<ServerSideDecoration> 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);
QScopedPointer<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.data(), Test::CreationSetup::CreateOnly));
QScopedPointer<Test::XdgToplevelDecorationV1> 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);
// 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<quint32>());
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<Test::XdgToplevel::States>();
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<Test::XdgToplevel::States>();
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<QSize>(), client->output()->geometry().size());
states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
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>(), QSize(100, 50));
states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
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<KWayland::Client::Surface> surface(Test::createSurface());
QScopedPointer<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.data()));
QScopedPointer<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.data(), Test::CreationSetup::CreateOnly));
QScopedPointer<Test::XdgToplevelDecorationV1> 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);
// Initialize the xdg-toplevel surface.
decoration->set_mode(Test::XdgToplevelDecorationV1::mode_server_side);
surface->commit(KWayland::Client::Surface::CommitFlag::None);
// create deco
QScopedPointer<ServerSideDecoration> 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);
QVERIFY(surfaceConfigureRequestedSpy.wait());
QCOMPARE(decorationConfigureRequestedSpy.last().at(0).value<Test::XdgToplevelDecorationV1::mode>(), Test::XdgToplevelDecorationV1::mode_server_side);
shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
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<KWayland::Client::Surface> surface(Test::createSurface());
QScopedPointer<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.data()));
QScopedPointer<ServerSideDecoration> 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
......
......@@ -2310,7 +2310,7 @@ void AbstractClient::endInteractiveMoveResize()
void AbstractClient::createDecoration(const QRect &oldGeometry)
{
setDecoration(Decoration::DecorationBridge::self()->createDecoration(this));
setDecoration(QSharedPointer<KDecoration2::Decoration>(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<KDecoration2::Decoration> 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();
}
......
......@@ -1197,7 +1197,7 @@ protected:
s_haveResizeEffect = false;
}
void setDecoration(KDecoration2::Decoration *decoration);
void setDecoration(QSharedPointer<KDecoration2::Decoration> decoration);
virtual void createDecoration(const QRect &oldGeometry);
virtual void destroyDecoration();
void startDecorationDoubleClickTimer();
......@@ -1313,7 +1313,7 @@ private:
} m_interactiveMoveResize;
struct {
QScopedPointer<KDecoration2::Decoration> decoration;
QSharedPointer<KDecoration2::Decoration> decoration;
QPointer<Decoration::DecoratedClientImpl> client;
QElapsedTimer doubleClickTimer;
QRegion inputRegion;
......
......@@ -1117,10 +1117,10 @@ void X11Client::invalidateDecoration()
void X11Client::createDecoration(const QRect& oldgeom)
{
KDecoration2::Decoration *decoration = Decoration::DecorationBridge::self()->createDecoration(this);
QSharedPointer<KDecoration2::Decoration> 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