File indexing completed on 2024-05-05 17:36:08

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2016 Martin Gräßlin <mgraesslin@kde.org>
0006     SPDX-FileCopyrightText: 2019 David Edmundson <davidedmundson@kde.org>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 #include "kwin_wayland_test.h"
0011 
0012 #include "core/output.h"
0013 #include "core/outputbackend.h"
0014 #include "cursor.h"
0015 #include "decorations/decorationbridge.h"
0016 #include "decorations/settings.h"
0017 #include "deleted.h"
0018 #include "effects.h"
0019 #include "virtualdesktops.h"
0020 #include "wayland/clientconnection.h"
0021 #include "wayland/display.h"
0022 #include "wayland_server.h"
0023 #include "window.h"
0024 #include "workspace.h"
0025 
0026 #include <KDecoration2/DecoratedClient>
0027 #include <KDecoration2/Decoration>
0028 #include <KDecoration2/DecorationSettings>
0029 
0030 #include <KWayland/Client/appmenu.h>
0031 #include <KWayland/Client/compositor.h>
0032 #include <KWayland/Client/connection_thread.h>
0033 #include <KWayland/Client/output.h>
0034 #include <KWayland/Client/pointer.h>
0035 #include <KWayland/Client/seat.h>
0036 #include <KWayland/Client/server_decoration.h>
0037 #include <KWayland/Client/subsurface.h>
0038 #include <KWayland/Client/surface.h>
0039 
0040 #include <QDBusConnection>
0041 
0042 // system
0043 #include <sys/socket.h>
0044 #include <sys/types.h>
0045 #include <unistd.h>
0046 
0047 #include <csignal>
0048 
0049 using namespace KWin;
0050 
0051 static const QString s_socketName = QStringLiteral("wayland_test_kwin_xdgshellwindow-0");
0052 
0053 class TestXdgShellWindow : public QObject
0054 {
0055     Q_OBJECT
0056 private Q_SLOTS:
0057     void initTestCase();
0058     void init();
0059     void cleanup();
0060 
0061     void testMapUnmap();
0062     void testDesktopPresenceChanged();
0063     void testWindowOutputs();
0064     void testMinimizeActiveWindow();
0065     void testFullscreen_data();
0066     void testFullscreen();
0067     void testUserCanSetFullscreen();
0068     void testSendFullScreenWindowToAnotherOutput();
0069 
0070     void testMaximizeHorizontal();
0071     void testMaximizeVertical();
0072     void testMaximizeFull();
0073     void testMaximizedToFullscreen_data();
0074     void testMaximizedToFullscreen();
0075     void testSendMaximizedWindowToAnotherOutput();
0076     void testFullscreenMultipleOutputs();
0077     void testHidden();
0078     void testDesktopFileName();
0079     void testCaptionSimplified();
0080     void testCaptionMultipleWindows();
0081     void testUnresponsiveWindow_data();
0082     void testUnresponsiveWindow();
0083     void testAppMenu();
0084     void testSendClientWithTransientToDesktop();
0085     void testMinimizeWindowWithTransients();
0086     void testXdgDecoration_data();
0087     void testXdgDecoration();
0088     void testXdgNeverCommitted();
0089     void testXdgInitialState();
0090     void testXdgInitiallyMaximised();
0091     void testXdgInitiallyFullscreen();
0092     void testXdgInitiallyMinimized();
0093     void testXdgWindowGeometryIsntSet();
0094     void testXdgWindowGeometryAttachBuffer();
0095     void testXdgWindowGeometryAttachSubSurface();
0096     void testXdgWindowGeometryInteractiveResize();
0097     void testXdgWindowGeometryFullScreen();
0098     void testXdgWindowGeometryMaximize();
0099     void testXdgWindowReactive();
0100     void testXdgWindowRepositioning();
0101     void testPointerInputTransform();
0102     void testReentrantSetFrameGeometry();
0103     void testDoubleMaximize();
0104     void testDoubleFullscreenSeparatedByCommit();
0105     void testMaximizeAndChangeDecorationModeAfterInitialCommit();
0106     void testFullScreenAndChangeDecorationModeAfterInitialCommit();
0107     void testChangeDecorationModeAfterInitialCommit();
0108 };
0109 
0110 void TestXdgShellWindow::testXdgWindowReactive()
0111 {
0112     std::unique_ptr<Test::XdgPositioner> positioner(Test::createXdgPositioner());
0113     positioner->set_size(10, 10);
0114     positioner->set_anchor_rect(10, 10, 10, 10);
0115     positioner->set_reactive();
0116 
0117     std::unique_ptr<KWayland::Client::Surface> rootSurface(Test::createSurface());
0118     std::unique_ptr<Test::XdgToplevel> root(Test::createXdgToplevelSurface(rootSurface.get()));
0119     auto rootWindow = Test::renderAndWaitForShown(rootSurface.get(), QSize(100, 100), Qt::cyan);
0120     QVERIFY(rootWindow);
0121 
0122     std::unique_ptr<KWayland::Client::Surface> childSurface(Test::createSurface());
0123     std::unique_ptr<Test::XdgPopup> popup(Test::createXdgPopupSurface(childSurface.get(), root->xdgSurface(), positioner.get()));
0124     auto childWindow = Test::renderAndWaitForShown(childSurface.get(), QSize(10, 10), Qt::cyan);
0125     QVERIFY(childWindow);
0126 
0127     QSignalSpy popupConfigureRequested(popup.get(), &Test::XdgPopup::configureRequested);
0128     rootWindow->move(rootWindow->pos() + QPoint(20, 20));
0129 
0130     QVERIFY(popupConfigureRequested.wait());
0131     QCOMPARE(popupConfigureRequested.count(), 1);
0132 }
0133 
0134 void TestXdgShellWindow::testXdgWindowRepositioning()
0135 {
0136     std::unique_ptr<Test::XdgPositioner> positioner(Test::createXdgPositioner());
0137     positioner->set_size(10, 10);
0138     positioner->set_anchor_rect(10, 10, 10, 10);
0139 
0140     std::unique_ptr<Test::XdgPositioner> otherPositioner(Test::createXdgPositioner());
0141     otherPositioner->set_size(50, 50);
0142     otherPositioner->set_anchor_rect(10, 10, 10, 10);
0143 
0144     std::unique_ptr<KWayland::Client::Surface> rootSurface(Test::createSurface());
0145     std::unique_ptr<Test::XdgToplevel> root(Test::createXdgToplevelSurface(rootSurface.get()));
0146     auto rootWindow = Test::renderAndWaitForShown(rootSurface.get(), QSize(100, 100), Qt::cyan);
0147     QVERIFY(rootWindow);
0148 
0149     std::unique_ptr<KWayland::Client::Surface> childSurface(Test::createSurface());
0150     std::unique_ptr<Test::XdgPopup> popup(Test::createXdgPopupSurface(childSurface.get(), root->xdgSurface(), positioner.get()));
0151     auto childWindow = Test::renderAndWaitForShown(childSurface.get(), QSize(10, 10), Qt::cyan);
0152     QVERIFY(childWindow);
0153 
0154     QSignalSpy reconfigureSpy(popup.get(), &Test::XdgPopup::configureRequested);
0155 
0156     popup->reposition(otherPositioner->object(), 500000);
0157 
0158     QVERIFY(reconfigureSpy.wait());
0159     QCOMPARE(reconfigureSpy.count(), 1);
0160 }
0161 
0162 void TestXdgShellWindow::initTestCase()
0163 {
0164     qRegisterMetaType<KWin::Deleted *>();
0165     qRegisterMetaType<KWin::Window *>();
0166     qRegisterMetaType<KWayland::Client::Output *>();
0167 
0168     QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
0169     QVERIFY(waylandServer()->init(s_socketName));
0170     QMetaObject::invokeMethod(kwinApp()->outputBackend(), "setVirtualOutputs", Qt::DirectConnection, Q_ARG(QVector<QRect>, QVector<QRect>() << QRect(0, 0, 1280, 1024) << QRect(1280, 0, 1280, 1024)));
0171 
0172     kwinApp()->start();
0173     QVERIFY(applicationStartedSpy.wait());
0174     const auto outputs = workspace()->outputs();
0175     QCOMPARE(outputs.count(), 2);
0176     QCOMPARE(outputs[0]->geometry(), QRect(0, 0, 1280, 1024));
0177     QCOMPARE(outputs[1]->geometry(), QRect(1280, 0, 1280, 1024));
0178 }
0179 
0180 void TestXdgShellWindow::init()
0181 {
0182     QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::Seat | Test::AdditionalWaylandInterface::XdgDecorationV1 | Test::AdditionalWaylandInterface::AppMenu));
0183     QVERIFY(Test::waitForWaylandPointer());
0184 
0185     workspace()->setActiveOutput(QPoint(640, 512));
0186     // put mouse in the middle of screen one
0187     KWin::Cursors::self()->mouse()->setPos(QPoint(640, 512));
0188 }
0189 
0190 void TestXdgShellWindow::cleanup()
0191 {
0192     Test::destroyWaylandConnection();
0193 }
0194 
0195 void TestXdgShellWindow::testMapUnmap()
0196 {
0197     // This test verifies that the compositor destroys XdgToplevelWindow when the
0198     // associated xdg_toplevel surface is unmapped.
0199 
0200     // Create a wl_surface and an xdg_toplevel, but don't commit them yet!
0201     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0202     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly));
0203 
0204     QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
0205 
0206     QSignalSpy configureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
0207 
0208     // Tell the compositor that we want to map the surface.
0209     surface->commit(KWayland::Client::Surface::CommitFlag::None);
0210 
0211     // The compositor will respond with a configure event.
0212     QVERIFY(configureRequestedSpy.wait());
0213     QCOMPARE(configureRequestedSpy.count(), 1);
0214 
0215     // Now we can attach a buffer with actual data to the surface.
0216     Test::render(surface.get(), QSize(100, 50), Qt::blue);
0217     QVERIFY(windowAddedSpy.wait());
0218     QCOMPARE(windowAddedSpy.count(), 1);
0219     Window *window = windowAddedSpy.last().first().value<Window *>();
0220     QVERIFY(window);
0221     QCOMPARE(window->readyForPainting(), true);
0222 
0223     // When the window becomes active, the compositor will send another configure event.
0224     QVERIFY(configureRequestedSpy.wait());
0225     QCOMPARE(configureRequestedSpy.count(), 2);
0226 
0227     // Unmap the xdg_toplevel surface by committing a null buffer.
0228     surface->attachBuffer(KWayland::Client::Buffer::Ptr());
0229     surface->commit(KWayland::Client::Surface::CommitFlag::None);
0230     QVERIFY(Test::waitForWindowDestroyed(window));
0231 
0232     // Tell the compositor that we want to re-map the xdg_toplevel surface.
0233     surface->commit(KWayland::Client::Surface::CommitFlag::None);
0234 
0235     // The compositor will respond with a configure event.
0236     QVERIFY(configureRequestedSpy.wait());
0237     QCOMPARE(configureRequestedSpy.count(), 3);
0238 
0239     // Now we can attach a buffer with actual data to the surface.
0240     Test::render(surface.get(), QSize(100, 50), Qt::blue);
0241     QVERIFY(windowAddedSpy.wait());
0242     QCOMPARE(windowAddedSpy.count(), 2);
0243     window = windowAddedSpy.last().first().value<Window *>();
0244     QVERIFY(window);
0245     QCOMPARE(window->readyForPainting(), true);
0246 
0247     // The compositor will respond with a configure event.
0248     QVERIFY(configureRequestedSpy.wait());
0249     QCOMPARE(configureRequestedSpy.count(), 4);
0250 
0251     // Destroy the test window.
0252     shellSurface.reset();
0253     QVERIFY(Test::waitForWindowDestroyed(window));
0254 }
0255 
0256 void TestXdgShellWindow::testDesktopPresenceChanged()
0257 {
0258     // this test verifies that the desktop presence changed signals are properly emitted
0259     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0260     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0261     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0262     QVERIFY(window);
0263     QCOMPARE(window->desktop(), 1);
0264     effects->setNumberOfDesktops(4);
0265     QSignalSpy desktopPresenceChangedClientSpy(window, &Window::desktopPresenceChanged);
0266     QSignalSpy desktopPresenceChangedWorkspaceSpy(workspace(), &Workspace::desktopPresenceChanged);
0267     QSignalSpy desktopPresenceChangedEffectsSpy(effects, &EffectsHandler::desktopPresenceChanged);
0268 
0269     // let's change the desktop
0270     workspace()->sendWindowToDesktop(window, 2, false);
0271     QCOMPARE(window->desktop(), 2);
0272     QCOMPARE(desktopPresenceChangedClientSpy.count(), 1);
0273     QCOMPARE(desktopPresenceChangedWorkspaceSpy.count(), 1);
0274     QCOMPARE(desktopPresenceChangedEffectsSpy.count(), 1);
0275 
0276     // verify the arguments
0277     QCOMPARE(desktopPresenceChangedClientSpy.first().at(0).value<Window *>(), window);
0278     QCOMPARE(desktopPresenceChangedClientSpy.first().at(1).toInt(), 1);
0279     QCOMPARE(desktopPresenceChangedWorkspaceSpy.first().at(0).value<Window *>(), window);
0280     QCOMPARE(desktopPresenceChangedWorkspaceSpy.first().at(1).toInt(), 1);
0281     QCOMPARE(desktopPresenceChangedEffectsSpy.first().at(0).value<EffectWindow *>(), window->effectWindow());
0282     QCOMPARE(desktopPresenceChangedEffectsSpy.first().at(1).toInt(), 1);
0283     QCOMPARE(desktopPresenceChangedEffectsSpy.first().at(2).toInt(), 2);
0284 }
0285 
0286 void TestXdgShellWindow::testWindowOutputs()
0287 {
0288     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0289     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0290     auto size = QSize(200, 200);
0291 
0292     QSignalSpy outputEnteredSpy(surface.get(), &KWayland::Client::Surface::outputEntered);
0293     QSignalSpy outputLeftSpy(surface.get(), &KWayland::Client::Surface::outputLeft);
0294 
0295     auto window = Test::renderAndWaitForShown(surface.get(), size, Qt::blue);
0296     // assumption: window is initially placed on first screen
0297     QVERIFY(outputEnteredSpy.wait());
0298     QCOMPARE(outputEnteredSpy.count(), 1);
0299     QCOMPARE(surface->outputs().count(), 1);
0300     QCOMPARE(surface->outputs().first()->globalPosition(), QPoint(0, 0));
0301 
0302     // move to overlapping both first and second screen
0303     window->moveResize(QRect(QPoint(1250, 100), size));
0304     QVERIFY(outputEnteredSpy.wait());
0305     QCOMPARE(outputEnteredSpy.count(), 2);
0306     QCOMPARE(outputLeftSpy.count(), 0);
0307     QCOMPARE(surface->outputs().count(), 2);
0308     QVERIFY(surface->outputs()[0] != surface->outputs()[1]);
0309 
0310     // move entirely into second screen
0311     window->moveResize(QRect(QPoint(1400, 100), size));
0312     QVERIFY(outputLeftSpy.wait());
0313     QCOMPARE(outputEnteredSpy.count(), 2);
0314     QCOMPARE(outputLeftSpy.count(), 1);
0315     QCOMPARE(surface->outputs().count(), 1);
0316     QCOMPARE(surface->outputs().first()->globalPosition(), QPoint(1280, 0));
0317 }
0318 
0319 void TestXdgShellWindow::testMinimizeActiveWindow()
0320 {
0321     // this test verifies that when minimizing the active window it gets deactivated
0322     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0323     std::unique_ptr<QObject> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0324     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0325     QVERIFY(window);
0326     QVERIFY(window->isActive());
0327     QCOMPARE(workspace()->activeWindow(), window);
0328     QVERIFY(window->wantsInput());
0329     QVERIFY(window->wantsTabFocus());
0330     QVERIFY(window->isShown());
0331 
0332     workspace()->slotWindowMinimize();
0333     QVERIFY(!window->isShown());
0334     QVERIFY(window->wantsInput());
0335     QVERIFY(window->wantsTabFocus());
0336     QVERIFY(!window->isActive());
0337     QVERIFY(!workspace()->activeWindow());
0338     QVERIFY(window->isMinimized());
0339 
0340     // unminimize again
0341     window->unminimize();
0342     QVERIFY(!window->isMinimized());
0343     QVERIFY(!window->isActive());
0344     QVERIFY(window->wantsInput());
0345     QVERIFY(window->wantsTabFocus());
0346     QVERIFY(window->isShown());
0347     QCOMPARE(workspace()->activeWindow(), nullptr);
0348 }
0349 
0350 void TestXdgShellWindow::testFullscreen_data()
0351 {
0352     QTest::addColumn<Test::XdgToplevelDecorationV1::mode>("decoMode");
0353 
0354     QTest::newRow("client-side deco") << Test::XdgToplevelDecorationV1::mode_client_side;
0355     QTest::newRow("server-side deco") << Test::XdgToplevelDecorationV1::mode_server_side;
0356 }
0357 
0358 void TestXdgShellWindow::testFullscreen()
0359 {
0360     // this test verifies that a window can be properly fullscreened
0361 
0362     Test::XdgToplevel::States states;
0363 
0364     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0365     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly));
0366     std::unique_ptr<Test::XdgToplevelDecorationV1> decoration(Test::createXdgToplevelDecorationV1(shellSurface.get()));
0367     QSignalSpy decorationConfigureRequestedSpy(decoration.get(), &Test::XdgToplevelDecorationV1::configureRequested);
0368     QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
0369     QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
0370 
0371     // Initialize the xdg-toplevel surface.
0372     QFETCH(Test::XdgToplevelDecorationV1::mode, decoMode);
0373     decoration->set_mode(decoMode);
0374     surface->commit(KWayland::Client::Surface::CommitFlag::None);
0375     QVERIFY(surfaceConfigureRequestedSpy.wait());
0376 
0377     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
0378     auto window = Test::renderAndWaitForShown(surface.get(), QSize(500, 250), Qt::blue);
0379     QVERIFY(window);
0380     QVERIFY(window->isActive());
0381     QCOMPARE(window->layer(), NormalLayer);
0382     QVERIFY(!window->isFullScreen());
0383     QCOMPARE(window->clientSize(), QSize(500, 250));
0384     QCOMPARE(window->isDecorated(), decoMode == Test::XdgToplevelDecorationV1::mode_server_side);
0385     QCOMPARE(window->clientSizeToFrameSize(window->clientSize()), window->size());
0386 
0387     QSignalSpy fullScreenChangedSpy(window, &Window::fullScreenChanged);
0388     QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
0389 
0390     // Wait for the compositor to send a configure event with the Activated state.
0391     QVERIFY(surfaceConfigureRequestedSpy.wait());
0392     QCOMPARE(surfaceConfigureRequestedSpy.count(), 2);
0393     states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
0394     QVERIFY(states & Test::XdgToplevel::State::Activated);
0395 
0396     // Ask the compositor to show the window in full screen mode.
0397     shellSurface->set_fullscreen(nullptr);
0398     QVERIFY(surfaceConfigureRequestedSpy.wait());
0399     QCOMPARE(surfaceConfigureRequestedSpy.count(), 3);
0400     states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
0401     QVERIFY(states & Test::XdgToplevel::State::Fullscreen);
0402     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), window->output()->geometry().size());
0403 
0404     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
0405     Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::red);
0406 
0407     QVERIFY(fullScreenChangedSpy.wait());
0408     QCOMPARE(fullScreenChangedSpy.count(), 1);
0409     QVERIFY(window->isFullScreen());
0410     QVERIFY(!window->isDecorated());
0411     QCOMPARE(window->layer(), ActiveLayer);
0412     QCOMPARE(window->frameGeometry(), QRect(QPoint(0, 0), window->output()->geometry().size()));
0413 
0414     // Ask the compositor to show the window in normal mode.
0415     shellSurface->unset_fullscreen();
0416     QVERIFY(surfaceConfigureRequestedSpy.wait());
0417     QCOMPARE(surfaceConfigureRequestedSpy.count(), 4);
0418     states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
0419     QVERIFY(!(states & Test::XdgToplevel::State::Fullscreen));
0420     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), QSize(500, 250));
0421 
0422     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
0423     Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::blue);
0424 
0425     QVERIFY(fullScreenChangedSpy.wait());
0426     QCOMPARE(fullScreenChangedSpy.count(), 2);
0427     QCOMPARE(window->clientSize(), QSize(500, 250));
0428     QVERIFY(!window->isFullScreen());
0429     QCOMPARE(window->isDecorated(), decoMode == Test::XdgToplevelDecorationV1::mode_server_side);
0430     QCOMPARE(window->layer(), NormalLayer);
0431 
0432     // Destroy the window.
0433     shellSurface.reset();
0434     QVERIFY(Test::waitForWindowDestroyed(window));
0435 }
0436 
0437 void TestXdgShellWindow::testUserCanSetFullscreen()
0438 {
0439     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0440     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0441     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0442     QVERIFY(window);
0443     QVERIFY(window->isActive());
0444     QVERIFY(!window->isFullScreen());
0445     QVERIFY(window->userCanSetFullScreen());
0446 }
0447 
0448 void TestXdgShellWindow::testSendFullScreenWindowToAnotherOutput()
0449 {
0450     // This test verifies that the fullscreen window will have correct geometry restore
0451     // after it's sent to another output.
0452 
0453     const auto outputs = workspace()->outputs();
0454 
0455     // Create the window.
0456     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0457     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0458     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0459     QVERIFY(window);
0460 
0461     // Wait for the compositor to send a configure event with the activated state.
0462     QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
0463     QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
0464     QVERIFY(surfaceConfigureRequestedSpy.wait());
0465 
0466     // Move the window to the left monitor.
0467     window->move(QPointF(10, 20));
0468     QCOMPARE(window->frameGeometry(), QRectF(10, 20, 100, 50));
0469     QCOMPARE(window->output(), outputs[0]);
0470 
0471     // Make the window fullscreen.
0472     QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
0473     shellSurface->set_fullscreen(nullptr);
0474     QVERIFY(surfaceConfigureRequestedSpy.wait());
0475     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
0476     Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::red);
0477     QVERIFY(frameGeometryChangedSpy.wait());
0478     QCOMPARE(window->isFullScreen(), true);
0479     QCOMPARE(window->frameGeometry(), QRectF(0, 0, 1280, 1024));
0480     QCOMPARE(window->fullscreenGeometryRestore(), QRectF(10, 20, 100, 50));
0481     QCOMPARE(window->output(), outputs[0]);
0482 
0483     // Send the window to another output.
0484     workspace()->sendWindowToOutput(window, outputs[1]);
0485     QCOMPARE(window->isFullScreen(), true);
0486     QCOMPARE(window->frameGeometry(), QRectF(1280, 0, 1280, 1024));
0487     QCOMPARE(window->fullscreenGeometryRestore(), QRectF(1280 + 10, 20, 100, 50));
0488     QCOMPARE(window->output(), outputs[1]);
0489 }
0490 
0491 void TestXdgShellWindow::testMaximizedToFullscreen_data()
0492 {
0493     QTest::addColumn<Test::XdgToplevelDecorationV1::mode>("decoMode");
0494 
0495     QTest::newRow("client-side deco") << Test::XdgToplevelDecorationV1::mode_client_side;
0496     QTest::newRow("server-side deco") << Test::XdgToplevelDecorationV1::mode_server_side;
0497 }
0498 
0499 void TestXdgShellWindow::testMaximizedToFullscreen()
0500 {
0501     // this test verifies that a window can be properly fullscreened after maximizing
0502 
0503     Test::XdgToplevel::States states;
0504 
0505     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0506     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly));
0507     std::unique_ptr<Test::XdgToplevelDecorationV1> decoration(Test::createXdgToplevelDecorationV1(shellSurface.get()));
0508     QSignalSpy decorationConfigureRequestedSpy(decoration.get(), &Test::XdgToplevelDecorationV1::configureRequested);
0509     QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
0510     QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
0511 
0512     // Initialize the xdg-toplevel surface.
0513     QFETCH(Test::XdgToplevelDecorationV1::mode, decoMode);
0514     decoration->set_mode(decoMode);
0515     surface->commit(KWayland::Client::Surface::CommitFlag::None);
0516     QVERIFY(surfaceConfigureRequestedSpy.wait());
0517 
0518     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
0519     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0520     QVERIFY(window);
0521     QVERIFY(window->isActive());
0522     QVERIFY(!window->isFullScreen());
0523     QCOMPARE(window->clientSize(), QSize(100, 50));
0524     QCOMPARE(window->isDecorated(), decoMode == Test::XdgToplevelDecorationV1::mode_server_side);
0525 
0526     QSignalSpy fullscreenChangedSpy(window, &Window::fullScreenChanged);
0527     QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
0528 
0529     // Wait for the compositor to send a configure event with the Activated state.
0530     QVERIFY(surfaceConfigureRequestedSpy.wait());
0531     QCOMPARE(surfaceConfigureRequestedSpy.count(), 2);
0532     states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
0533     QVERIFY(states & Test::XdgToplevel::State::Activated);
0534 
0535     // Ask the compositor to maximize the window.
0536     shellSurface->set_maximized();
0537     QVERIFY(surfaceConfigureRequestedSpy.wait());
0538     QCOMPARE(surfaceConfigureRequestedSpy.count(), 3);
0539     states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
0540     QVERIFY(states & Test::XdgToplevel::State::Maximized);
0541 
0542     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
0543     Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::red);
0544     QVERIFY(frameGeometryChangedSpy.wait());
0545     QCOMPARE(window->maximizeMode(), MaximizeFull);
0546 
0547     // Ask the compositor to show the window in full screen mode.
0548     shellSurface->set_fullscreen(nullptr);
0549     QVERIFY(surfaceConfigureRequestedSpy.wait());
0550     QCOMPARE(surfaceConfigureRequestedSpy.count(), 4);
0551     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), window->output()->geometry().size());
0552     states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
0553     QVERIFY(states & Test::XdgToplevel::State::Maximized);
0554     QVERIFY(states & Test::XdgToplevel::State::Fullscreen);
0555 
0556     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
0557     Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::red);
0558 
0559     QVERIFY(fullscreenChangedSpy.wait());
0560     QCOMPARE(fullscreenChangedSpy.count(), 1);
0561     QCOMPARE(window->maximizeMode(), MaximizeFull);
0562     QVERIFY(window->isFullScreen());
0563     QVERIFY(!window->isDecorated());
0564 
0565     // Switch back to normal mode.
0566     shellSurface->unset_fullscreen();
0567     shellSurface->unset_maximized();
0568     QVERIFY(surfaceConfigureRequestedSpy.wait());
0569     QCOMPARE(surfaceConfigureRequestedSpy.count(), 5);
0570     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), QSize(100, 50));
0571     states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
0572     QVERIFY(!(states & Test::XdgToplevel::State::Maximized));
0573     QVERIFY(!(states & Test::XdgToplevel::State::Fullscreen));
0574 
0575     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
0576     Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::red);
0577 
0578     QVERIFY(frameGeometryChangedSpy.wait());
0579     QVERIFY(!window->isFullScreen());
0580     QCOMPARE(window->isDecorated(), decoMode == Test::XdgToplevelDecorationV1::mode_server_side);
0581     QCOMPARE(window->maximizeMode(), MaximizeRestore);
0582 
0583     // Destroy the window.
0584     shellSurface.reset();
0585     QVERIFY(Test::waitForWindowDestroyed(window));
0586 }
0587 
0588 void TestXdgShellWindow::testFullscreenMultipleOutputs()
0589 {
0590     // this test verifies that kwin will place fullscreen windows in the outputs its instructed to
0591 
0592     const auto outputs = workspace()->outputs();
0593     for (KWin::Output *output : outputs) {
0594         Test::XdgToplevel::States states;
0595 
0596         std::unique_ptr<KWayland::Client::Surface> surface = Test::createSurface();
0597         QVERIFY(surface);
0598         QSharedPointer<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0599         QVERIFY(shellSurface);
0600 
0601         auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0602         QVERIFY(window);
0603         QVERIFY(window->isActive());
0604         QVERIFY(!window->isFullScreen());
0605         QCOMPARE(window->clientSize(), QSize(100, 50));
0606         QVERIFY(!window->isDecorated());
0607 
0608         QSignalSpy fullscreenChangedSpy(window, &Window::fullScreenChanged);
0609         QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
0610         QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
0611         QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
0612 
0613         // Wait for the compositor to send a configure event with the Activated state.
0614         QVERIFY(surfaceConfigureRequestedSpy.wait());
0615         QCOMPARE(surfaceConfigureRequestedSpy.count(), 1);
0616         states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
0617         QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
0618 
0619         // Ask the compositor to show the window in full screen mode.
0620         shellSurface->set_fullscreen(*Test::waylandOutput(output->name()));
0621         QVERIFY(surfaceConfigureRequestedSpy.wait());
0622         QCOMPARE(surfaceConfigureRequestedSpy.count(), 2);
0623         QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), output->geometry().size());
0624 
0625         shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
0626         Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::red);
0627 
0628         QVERIFY(!fullscreenChangedSpy.isEmpty() || fullscreenChangedSpy.wait());
0629         QCOMPARE(fullscreenChangedSpy.count(), 1);
0630 
0631         QVERIFY(!frameGeometryChangedSpy.isEmpty() || frameGeometryChangedSpy.wait());
0632 
0633         QVERIFY(window->isFullScreen());
0634 
0635         QCOMPARE(window->frameGeometry(), output->geometry());
0636     }
0637 }
0638 
0639 void TestXdgShellWindow::testHidden()
0640 {
0641     // this test verifies that when hiding window it doesn't get shown
0642     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0643     std::unique_ptr<QObject> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0644     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0645     QVERIFY(window);
0646     QVERIFY(window->isActive());
0647     QCOMPARE(workspace()->activeWindow(), window);
0648     QVERIFY(window->wantsInput());
0649     QVERIFY(window->wantsTabFocus());
0650     QVERIFY(window->isShown());
0651 
0652     window->hideClient();
0653     QVERIFY(!window->isShown());
0654     QVERIFY(!window->isActive());
0655     QVERIFY(window->wantsInput());
0656     QVERIFY(window->wantsTabFocus());
0657 
0658     // unhide again
0659     window->showClient();
0660     QVERIFY(window->isShown());
0661     QVERIFY(window->wantsInput());
0662     QVERIFY(window->wantsTabFocus());
0663 
0664     // QCOMPARE(workspace()->activeClient(), c);
0665 }
0666 
0667 void TestXdgShellWindow::testDesktopFileName()
0668 {
0669     QIcon::setThemeName(QStringLiteral("breeze"));
0670     // this test verifies that desktop file name is passed correctly to the window
0671     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0672     // only xdg-shell as ShellSurface misses the setter
0673     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0674     shellSurface->set_app_id(QStringLiteral("org.kde.foo"));
0675     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0676     QVERIFY(window);
0677     QCOMPARE(window->desktopFileName(), QStringLiteral("org.kde.foo"));
0678     QCOMPARE(window->resourceClass(), QStringLiteral("org.kde.foo"));
0679     QVERIFY(window->resourceName().startsWith("testXdgShellWindow"));
0680     // the desktop file does not exist, so icon should be generic Wayland
0681     QCOMPARE(window->icon().name(), QStringLiteral("wayland"));
0682 
0683     QSignalSpy desktopFileNameChangedSpy(window, &Window::desktopFileNameChanged);
0684     QSignalSpy iconChangedSpy(window, &Window::iconChanged);
0685     shellSurface->set_app_id(QStringLiteral("org.kde.bar"));
0686     QVERIFY(desktopFileNameChangedSpy.wait());
0687     QCOMPARE(window->desktopFileName(), QStringLiteral("org.kde.bar"));
0688     QCOMPARE(window->resourceClass(), QStringLiteral("org.kde.bar"));
0689     QVERIFY(window->resourceName().startsWith("testXdgShellWindow"));
0690     // icon should still be wayland
0691     QCOMPARE(window->icon().name(), QStringLiteral("wayland"));
0692     QVERIFY(iconChangedSpy.isEmpty());
0693 
0694     const QString dfPath = QFINDTESTDATA("data/example.desktop");
0695     shellSurface->set_app_id(dfPath.toUtf8());
0696     QVERIFY(desktopFileNameChangedSpy.wait());
0697     QCOMPARE(iconChangedSpy.count(), 1);
0698     QCOMPARE(window->desktopFileName(), dfPath);
0699     QCOMPARE(window->icon().name(), QStringLiteral("kwin"));
0700 }
0701 
0702 void TestXdgShellWindow::testCaptionSimplified()
0703 {
0704     // this test verifies that caption is properly trimmed
0705     // see BUG 323798 comment #12
0706     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0707     // only done for xdg-shell as ShellSurface misses the setter
0708     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0709     const QString origTitle = QString::fromUtf8(QByteArrayLiteral("Was tun, wenn Schüler Autismus haben?\342\200\250\342\200\250\342\200\250 – Marlies Hübner - Mozilla Firefox"));
0710     shellSurface->set_title(origTitle);
0711     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0712     QVERIFY(window);
0713     QVERIFY(window->caption() != origTitle);
0714     QCOMPARE(window->caption(), origTitle.simplified());
0715 }
0716 
0717 void TestXdgShellWindow::testCaptionMultipleWindows()
0718 {
0719     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0720     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0721     shellSurface->set_title(QStringLiteral("foo"));
0722     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0723     QVERIFY(window);
0724     QCOMPARE(window->caption(), QStringLiteral("foo"));
0725     QCOMPARE(window->captionNormal(), QStringLiteral("foo"));
0726     QCOMPARE(window->captionSuffix(), QString());
0727 
0728     std::unique_ptr<KWayland::Client::Surface> surface2(Test::createSurface());
0729     std::unique_ptr<Test::XdgToplevel> shellSurface2(Test::createXdgToplevelSurface(surface2.get()));
0730     shellSurface2->set_title(QStringLiteral("foo"));
0731     auto c2 = Test::renderAndWaitForShown(surface2.get(), QSize(100, 50), Qt::blue);
0732     QVERIFY(c2);
0733     QCOMPARE(c2->caption(), QStringLiteral("foo <2>"));
0734     QCOMPARE(c2->captionNormal(), QStringLiteral("foo"));
0735     QCOMPARE(c2->captionSuffix(), QStringLiteral(" <2>"));
0736 
0737     std::unique_ptr<KWayland::Client::Surface> surface3(Test::createSurface());
0738     std::unique_ptr<Test::XdgToplevel> shellSurface3(Test::createXdgToplevelSurface(surface3.get()));
0739     shellSurface3->set_title(QStringLiteral("foo"));
0740     auto c3 = Test::renderAndWaitForShown(surface3.get(), QSize(100, 50), Qt::blue);
0741     QVERIFY(c3);
0742     QCOMPARE(c3->caption(), QStringLiteral("foo <3>"));
0743     QCOMPARE(c3->captionNormal(), QStringLiteral("foo"));
0744     QCOMPARE(c3->captionSuffix(), QStringLiteral(" <3>"));
0745 
0746     std::unique_ptr<KWayland::Client::Surface> surface4(Test::createSurface());
0747     std::unique_ptr<Test::XdgToplevel> shellSurface4(Test::createXdgToplevelSurface(surface4.get()));
0748     shellSurface4->set_title(QStringLiteral("bar"));
0749     auto c4 = Test::renderAndWaitForShown(surface4.get(), QSize(100, 50), Qt::blue);
0750     QVERIFY(c4);
0751     QCOMPARE(c4->caption(), QStringLiteral("bar"));
0752     QCOMPARE(c4->captionNormal(), QStringLiteral("bar"));
0753     QCOMPARE(c4->captionSuffix(), QString());
0754     QSignalSpy captionChangedSpy(c4, &Window::captionChanged);
0755     shellSurface4->set_title(QStringLiteral("foo"));
0756     QVERIFY(captionChangedSpy.wait());
0757     QCOMPARE(captionChangedSpy.count(), 1);
0758     QCOMPARE(c4->caption(), QStringLiteral("foo <4>"));
0759     QCOMPARE(c4->captionNormal(), QStringLiteral("foo"));
0760     QCOMPARE(c4->captionSuffix(), QStringLiteral(" <4>"));
0761 }
0762 
0763 void TestXdgShellWindow::testUnresponsiveWindow_data()
0764 {
0765     QTest::addColumn<QString>("shellInterface"); // see env selection in qwaylandintegration.cpp
0766     QTest::addColumn<bool>("socketMode");
0767 
0768     QTest::newRow("xdg display") << "xdg-shell" << false;
0769     QTest::newRow("xdg socket") << "xdg-shell" << true;
0770 }
0771 
0772 void TestXdgShellWindow::testUnresponsiveWindow()
0773 {
0774     // this test verifies that killWindow properly terminates a process
0775     // for this an external binary is launched
0776     const QString kill = QFINDTESTDATA(QStringLiteral("kill"));
0777     QVERIFY(!kill.isEmpty());
0778     QSignalSpy windowAddedSpy(workspace(), &Workspace::windowAdded);
0779 
0780     std::unique_ptr<QProcess> process(new QProcess);
0781     QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
0782 
0783     QFETCH(QString, shellInterface);
0784     QFETCH(bool, socketMode);
0785     env.insert("QT_WAYLAND_SHELL_INTEGRATION", shellInterface);
0786     if (socketMode) {
0787         int sx[2];
0788         QVERIFY(socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sx) >= 0);
0789         waylandServer()->display()->createClient(sx[0]);
0790         int socket = dup(sx[1]);
0791         QVERIFY(socket != -1);
0792         env.insert(QStringLiteral("WAYLAND_SOCKET"), QByteArray::number(socket));
0793         env.remove("WAYLAND_DISPLAY");
0794     } else {
0795         env.insert("WAYLAND_DISPLAY", s_socketName);
0796     }
0797     process->setProcessEnvironment(env);
0798     process->setProcessChannelMode(QProcess::ForwardedChannels);
0799     process->setProgram(kill);
0800     QSignalSpy processStartedSpy{process.get(), &QProcess::started};
0801     process->start();
0802     QVERIFY(processStartedSpy.wait());
0803 
0804     Window *killWindow = nullptr;
0805     if (windowAddedSpy.isEmpty()) {
0806         QVERIFY(windowAddedSpy.wait());
0807     }
0808     ::kill(process->processId(), SIGUSR1); // send a signal to freeze the process
0809 
0810     killWindow = windowAddedSpy.first().first().value<Window *>();
0811     QVERIFY(killWindow);
0812     QSignalSpy unresponsiveSpy(killWindow, &Window::unresponsiveChanged);
0813     QSignalSpy killedSpy(process.get(), static_cast<void (QProcess::*)(int, QProcess::ExitStatus)>(&QProcess::finished));
0814     QSignalSpy deletedSpy(killWindow, &QObject::destroyed);
0815 
0816     qint64 startTime = QDateTime::currentMSecsSinceEpoch();
0817 
0818     // wait for the process to be frozen
0819     QTest::qWait(10);
0820 
0821     // pretend the user clicked the close button
0822     killWindow->closeWindow();
0823 
0824     // window should not yet be marked unresponsive nor killed
0825     QVERIFY(!killWindow->unresponsive());
0826     QVERIFY(killedSpy.isEmpty());
0827 
0828     QVERIFY(unresponsiveSpy.wait());
0829     // window should be marked unresponsive but not killed
0830     auto elapsed1 = QDateTime::currentMSecsSinceEpoch() - startTime;
0831     QVERIFY(elapsed1 > 900 && elapsed1 < 1200); // ping timer is 1s, but coarse timers on a test across two processes means we need a fuzzy compare
0832     QVERIFY(killWindow->unresponsive());
0833     QVERIFY(killedSpy.isEmpty());
0834 
0835     QVERIFY(deletedSpy.wait());
0836     if (!socketMode) {
0837         // process was killed - because we're across process this could happen in either order
0838         QVERIFY(killedSpy.count() || killedSpy.wait());
0839     }
0840 
0841     auto elapsed2 = QDateTime::currentMSecsSinceEpoch() - startTime;
0842     QVERIFY(elapsed2 > 1800); // second ping comes in a second later
0843 }
0844 
0845 void TestXdgShellWindow::testAppMenu()
0846 {
0847     // register a faux appmenu client
0848     QVERIFY(QDBusConnection::sessionBus().registerService("org.kde.kappmenu"));
0849 
0850     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0851     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0852     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0853     QVERIFY(window);
0854     std::unique_ptr<KWayland::Client::AppMenu> menu(Test::waylandAppMenuManager()->create(surface.get()));
0855     QSignalSpy spy(window, &Window::hasApplicationMenuChanged);
0856     menu->setAddress("service.name", "object/path");
0857     spy.wait();
0858     QCOMPARE(window->hasApplicationMenu(), true);
0859     QCOMPARE(window->applicationMenuServiceName(), QString("service.name"));
0860     QCOMPARE(window->applicationMenuObjectPath(), QString("object/path"));
0861 
0862     QVERIFY(QDBusConnection::sessionBus().unregisterService("org.kde.kappmenu"));
0863 }
0864 
0865 void TestXdgShellWindow::testSendClientWithTransientToDesktop()
0866 {
0867     // this test verifies that when sending a window to a desktop all transients are also send to that desktop
0868 
0869     VirtualDesktopManager *vds = VirtualDesktopManager::self();
0870     vds->setCount(2);
0871     const QVector<VirtualDesktop *> desktops = vds->desktops();
0872 
0873     std::unique_ptr<KWayland::Client::Surface> surface{Test::createSurface()};
0874     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0875 
0876     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0877     QVERIFY(window);
0878 
0879     // let's create a transient window
0880     std::unique_ptr<KWayland::Client::Surface> transientSurface{Test::createSurface()};
0881     std::unique_ptr<Test::XdgToplevel> transientShellSurface(Test::createXdgToplevelSurface(transientSurface.get()));
0882     transientShellSurface->set_parent(shellSurface->object());
0883 
0884     auto transient = Test::renderAndWaitForShown(transientSurface.get(), QSize(100, 50), Qt::blue);
0885     QVERIFY(transient);
0886     QCOMPARE(workspace()->activeWindow(), transient);
0887     QCOMPARE(transient->transientFor(), window);
0888     QVERIFY(window->transients().contains(transient));
0889 
0890     // initially, the parent and the transient are on the first virtual desktop
0891     QCOMPARE(window->desktops(), QVector<VirtualDesktop *>{desktops[0]});
0892     QVERIFY(!window->isOnAllDesktops());
0893     QCOMPARE(transient->desktops(), QVector<VirtualDesktop *>{desktops[0]});
0894     QVERIFY(!transient->isOnAllDesktops());
0895 
0896     // send the transient to the second virtual desktop
0897     workspace()->slotWindowToDesktop(desktops[1]);
0898     QCOMPARE(window->desktops(), QVector<VirtualDesktop *>{desktops[0]});
0899     QCOMPARE(transient->desktops(), QVector<VirtualDesktop *>{desktops[1]});
0900 
0901     // activate c
0902     workspace()->activateWindow(window);
0903     QCOMPARE(workspace()->activeWindow(), window);
0904     QVERIFY(window->isActive());
0905 
0906     // and send it to the desktop it's already on
0907     QCOMPARE(window->desktops(), QVector<VirtualDesktop *>{desktops[0]});
0908     QCOMPARE(transient->desktops(), QVector<VirtualDesktop *>{desktops[1]});
0909     workspace()->slotWindowToDesktop(desktops[0]);
0910 
0911     // which should move the transient back to the desktop
0912     QCOMPARE(window->desktops(), QVector<VirtualDesktop *>{desktops[0]});
0913     QCOMPARE(transient->desktops(), QVector<VirtualDesktop *>{desktops[0]});
0914 }
0915 
0916 void TestXdgShellWindow::testMinimizeWindowWithTransients()
0917 {
0918     // this test verifies that when minimizing/unminimizing a window all its
0919     // transients will be minimized/unminimized as well
0920 
0921     // create the main window
0922     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0923     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0924     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0925     QVERIFY(window);
0926     QVERIFY(!window->isMinimized());
0927 
0928     // create a transient window
0929     std::unique_ptr<KWayland::Client::Surface> transientSurface(Test::createSurface());
0930     std::unique_ptr<Test::XdgToplevel> transientShellSurface(Test::createXdgToplevelSurface(transientSurface.get()));
0931     transientShellSurface->set_parent(shellSurface->object());
0932     auto transient = Test::renderAndWaitForShown(transientSurface.get(), QSize(100, 50), Qt::red);
0933     QVERIFY(transient);
0934     QVERIFY(!transient->isMinimized());
0935     QCOMPARE(transient->transientFor(), window);
0936     QVERIFY(window->hasTransient(transient, false));
0937 
0938     // minimize the main window, the transient should be minimized as well
0939     window->minimize();
0940     QVERIFY(window->isMinimized());
0941     QVERIFY(transient->isMinimized());
0942 
0943     // unminimize the main window, the transient should be unminimized as well
0944     window->unminimize();
0945     QVERIFY(!window->isMinimized());
0946     QVERIFY(!transient->isMinimized());
0947 }
0948 
0949 void TestXdgShellWindow::testXdgDecoration_data()
0950 {
0951     QTest::addColumn<Test::XdgToplevelDecorationV1::mode>("requestedMode");
0952     QTest::addColumn<Test::XdgToplevelDecorationV1::mode>("expectedMode");
0953 
0954     QTest::newRow("client side requested") << Test::XdgToplevelDecorationV1::mode_client_side << Test::XdgToplevelDecorationV1::mode_client_side;
0955     QTest::newRow("server side requested") << Test::XdgToplevelDecorationV1::mode_server_side << Test::XdgToplevelDecorationV1::mode_server_side;
0956 }
0957 
0958 void TestXdgShellWindow::testXdgDecoration()
0959 {
0960     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0961     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0962     std::unique_ptr<Test::XdgToplevelDecorationV1> deco(Test::createXdgToplevelDecorationV1(shellSurface.get()));
0963 
0964     QSignalSpy decorationConfigureRequestedSpy(deco.get(), &Test::XdgToplevelDecorationV1::configureRequested);
0965     QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
0966 
0967     QFETCH(Test::XdgToplevelDecorationV1::mode, requestedMode);
0968     QFETCH(Test::XdgToplevelDecorationV1::mode, expectedMode);
0969 
0970     // request a mode
0971     deco->set_mode(requestedMode);
0972 
0973     // kwin will send a configure
0974     QVERIFY(surfaceConfigureRequestedSpy.wait());
0975 
0976     QCOMPARE(decorationConfigureRequestedSpy.count(), 1);
0977     QCOMPARE(decorationConfigureRequestedSpy.last()[0].value<Test::XdgToplevelDecorationV1::mode>(), expectedMode);
0978     QVERIFY(decorationConfigureRequestedSpy.count() > 0);
0979 
0980     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last()[0].toInt());
0981     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0982     QCOMPARE(window->isDecorated(), expectedMode == Test::XdgToplevelDecorationV1::mode_server_side);
0983 }
0984 
0985 void TestXdgShellWindow::testXdgNeverCommitted()
0986 {
0987     // check we don't crash if we create a shell object but delete the XdgShellClient before committing it
0988     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0989     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly));
0990 }
0991 
0992 void TestXdgShellWindow::testXdgInitialState()
0993 {
0994     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0995     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly));
0996     QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
0997     QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
0998     surface->commit(KWayland::Client::Surface::CommitFlag::None);
0999 
1000     surfaceConfigureRequestedSpy.wait();
1001     QCOMPARE(surfaceConfigureRequestedSpy.count(), 1);
1002 
1003     const auto size = toplevelConfigureRequestedSpy.first()[0].value<QSize>();
1004 
1005     QCOMPARE(size, QSize(0, 0)); // window should chose it's preferred size
1006 
1007     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.first()[0].toUInt());
1008 
1009     auto window = Test::renderAndWaitForShown(surface.get(), QSize(200, 100), Qt::blue);
1010     QCOMPARE(window->size(), QSize(200, 100));
1011 }
1012 
1013 void TestXdgShellWindow::testXdgInitiallyMaximised()
1014 {
1015     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
1016     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly));
1017     QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
1018     QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
1019 
1020     shellSurface->set_maximized();
1021     surface->commit(KWayland::Client::Surface::CommitFlag::None);
1022 
1023     surfaceConfigureRequestedSpy.wait();
1024 
1025     QCOMPARE(surfaceConfigureRequestedSpy.count(), 1);
1026 
1027     const auto size = toplevelConfigureRequestedSpy.first()[0].value<QSize>();
1028     const auto state = toplevelConfigureRequestedSpy.first()[1].value<Test::XdgToplevel::States>();
1029 
1030     QCOMPARE(size, QSize(1280, 1024));
1031     QVERIFY(state & Test::XdgToplevel::State::Maximized);
1032 
1033     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.first()[0].toUInt());
1034 
1035     auto window = Test::renderAndWaitForShown(surface.get(), size, Qt::blue);
1036     QCOMPARE(window->maximizeMode(), MaximizeFull);
1037     QCOMPARE(window->size(), QSize(1280, 1024));
1038 }
1039 
1040 void TestXdgShellWindow::testXdgInitiallyFullscreen()
1041 {
1042     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
1043     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly));
1044     QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
1045     QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
1046 
1047     shellSurface->set_fullscreen(nullptr);
1048     surface->commit(KWayland::Client::Surface::CommitFlag::None);
1049 
1050     surfaceConfigureRequestedSpy.wait();
1051 
1052     QCOMPARE(surfaceConfigureRequestedSpy.count(), 1);
1053 
1054     const auto size = toplevelConfigureRequestedSpy.first()[0].value<QSize>();
1055     const auto state = toplevelConfigureRequestedSpy.first()[1].value<Test::XdgToplevel::States>();
1056 
1057     QCOMPARE(size, QSize(1280, 1024));
1058     QVERIFY(state & Test::XdgToplevel::State::Fullscreen);
1059 
1060     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.first()[0].toUInt());
1061 
1062     auto window = Test::renderAndWaitForShown(surface.get(), size, Qt::blue);
1063     QCOMPARE(window->isFullScreen(), true);
1064     QCOMPARE(window->size(), QSize(1280, 1024));
1065 }
1066 
1067 void TestXdgShellWindow::testXdgInitiallyMinimized()
1068 {
1069     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
1070     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly));
1071     QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
1072     QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
1073     shellSurface->set_minimized();
1074     surface->commit(KWayland::Client::Surface::CommitFlag::None);
1075 
1076     surfaceConfigureRequestedSpy.wait();
1077     QCOMPARE(surfaceConfigureRequestedSpy.count(), 1);
1078 
1079     const auto size = toplevelConfigureRequestedSpy.first()[0].value<QSize>();
1080     const auto state = toplevelConfigureRequestedSpy.first()[1].value<Test::XdgToplevel::States>();
1081 
1082     QCOMPARE(size, QSize(0, 0));
1083     QCOMPARE(state, 0);
1084 
1085     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.first()[0].toUInt());
1086 
1087     QEXPECT_FAIL("", "Client created in a minimised state is not exposed to kwin bug 404838", Abort);
1088     auto window = Test::renderAndWaitForShown(surface.get(), size, Qt::blue, QImage::Format_ARGB32, 10);
1089     QVERIFY(window);
1090     QVERIFY(window->isMinimized());
1091 }
1092 
1093 void TestXdgShellWindow::testXdgWindowGeometryIsntSet()
1094 {
1095     // This test verifies that the effective window geometry corresponds to the
1096     // bounding rectangle of the main surface and its sub-surfaces if no window
1097     // geometry is set by the window.
1098 
1099     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
1100     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
1101     Window *window = Test::renderAndWaitForShown(surface.get(), QSize(200, 100), Qt::red);
1102     QVERIFY(window);
1103     QCOMPARE(window->bufferGeometry().size(), QSize(200, 100));
1104     QCOMPARE(window->frameGeometry().size(), QSize(200, 100));
1105 
1106     const QPointF oldPosition = window->pos();
1107 
1108     QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
1109     Test::render(surface.get(), QSize(100, 50), Qt::blue);
1110     QVERIFY(frameGeometryChangedSpy.wait());
1111     QCOMPARE(window->frameGeometry().topLeft(), oldPosition);
1112     QCOMPARE(window->frameGeometry().size(), QSize(100, 50));
1113     QCOMPARE(window->bufferGeometry().topLeft(), oldPosition);
1114     QCOMPARE(window->bufferGeometry().size(), QSize(100, 50));
1115 
1116     std::unique_ptr<KWayland::Client::Surface> childSurface(Test::createSurface());
1117     std::unique_ptr<KWayland::Client::SubSurface> subSurface(Test::createSubSurface(childSurface.get(), surface.get()));
1118     QVERIFY(subSurface);
1119     subSurface->setPosition(QPoint(-20, -10));
1120     Test::render(childSurface.get(), QSize(100, 50), Qt::blue);
1121     surface->commit(KWayland::Client::Surface::CommitFlag::None);
1122     QVERIFY(frameGeometryChangedSpy.wait());
1123     QCOMPARE(window->frameGeometry().topLeft(), oldPosition);
1124     QCOMPARE(window->frameGeometry().size(), QSize(120, 60));
1125     QCOMPARE(window->bufferGeometry().topLeft(), oldPosition + QPoint(20, 10));
1126     QCOMPARE(window->bufferGeometry().size(), QSize(100, 50));
1127 }
1128 
1129 void TestXdgShellWindow::testXdgWindowGeometryAttachBuffer()
1130 {
1131     // This test verifies that the effective window geometry remains the same when
1132     // a new buffer is attached and xdg_surface.set_window_geometry is not called
1133     // again. Notice that the window geometry must remain the same even if the new
1134     // buffer is smaller.
1135 
1136     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
1137     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
1138     Window *window = Test::renderAndWaitForShown(surface.get(), QSize(200, 100), Qt::red);
1139     QVERIFY(window);
1140     QCOMPARE(window->bufferGeometry().size(), QSize(200, 100));
1141     QCOMPARE(window->frameGeometry().size(), QSize(200, 100));
1142 
1143     const QPointF oldPosition = window->pos();
1144 
1145     QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
1146     shellSurface->xdgSurface()->set_window_geometry(10, 10, 180, 80);
1147     surface->commit(KWayland::Client::Surface::CommitFlag::None);
1148     QVERIFY(frameGeometryChangedSpy.wait());
1149     QCOMPARE(frameGeometryChangedSpy.count(), 1);
1150     QCOMPARE(window->frameGeometry().topLeft(), oldPosition);
1151     QCOMPARE(window->frameGeometry().size(), QSize(180, 80));
1152     QCOMPARE(window->bufferGeometry().topLeft(), oldPosition - QPoint(10, 10));
1153     QCOMPARE(window->bufferGeometry().size(), QSize(200, 100));
1154 
1155     Test::render(surface.get(), QSize(100, 50), Qt::blue);
1156     QVERIFY(frameGeometryChangedSpy.wait());
1157     QCOMPARE(frameGeometryChangedSpy.count(), 2);
1158     QCOMPARE(window->frameGeometry().topLeft(), oldPosition);
1159     QCOMPARE(window->frameGeometry().size(), QSize(90, 40));
1160     QCOMPARE(window->bufferGeometry().topLeft(), oldPosition - QPoint(10, 10));
1161     QCOMPARE(window->bufferGeometry().size(), QSize(100, 50));
1162 
1163     shellSurface->xdgSurface()->set_window_geometry(0, 0, 100, 50);
1164     surface->commit(KWayland::Client::Surface::CommitFlag::None);
1165     QVERIFY(frameGeometryChangedSpy.wait());
1166     QCOMPARE(frameGeometryChangedSpy.count(), 3);
1167     QCOMPARE(window->frameGeometry().topLeft(), oldPosition);
1168     QCOMPARE(window->frameGeometry().size(), QSize(100, 50));
1169     QCOMPARE(window->bufferGeometry().topLeft(), oldPosition);
1170     QCOMPARE(window->bufferGeometry().size(), QSize(100, 50));
1171 
1172     shellSurface.reset();
1173     QVERIFY(Test::waitForWindowDestroyed(window));
1174 }
1175 
1176 void TestXdgShellWindow::testXdgWindowGeometryAttachSubSurface()
1177 {
1178     // This test verifies that the effective window geometry remains the same
1179     // when a new sub-surface is added and xdg_surface.set_window_geometry is
1180     // not called again.
1181 
1182     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
1183     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
1184     Window *window = Test::renderAndWaitForShown(surface.get(), QSize(200, 100), Qt::red);
1185     QVERIFY(window);
1186     QCOMPARE(window->bufferGeometry().size(), QSize(200, 100));
1187     QCOMPARE(window->frameGeometry().size(), QSize(200, 100));
1188 
1189     const QPointF oldPosition = window->pos();
1190 
1191     QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
1192     shellSurface->xdgSurface()->set_window_geometry(10, 10, 180, 80);
1193     surface->commit(KWayland::Client::Surface::CommitFlag::None);
1194     QVERIFY(frameGeometryChangedSpy.wait());
1195     QCOMPARE(window->frameGeometry().topLeft(), oldPosition);
1196     QCOMPARE(window->frameGeometry().size(), QSize(180, 80));
1197     QCOMPARE(window->bufferGeometry().topLeft(), oldPosition - QPoint(10, 10));
1198     QCOMPARE(window->bufferGeometry().size(), QSize(200, 100));
1199 
1200     std::unique_ptr<KWayland::Client::Surface> childSurface(Test::createSurface());
1201     std::unique_ptr<KWayland::Client::SubSurface> subSurface(Test::createSubSurface(childSurface.get(), surface.get()));
1202     QVERIFY(subSurface);
1203     subSurface->setPosition(QPoint(-20, -20));
1204     Test::render(childSurface.get(), QSize(100, 50), Qt::blue);
1205     surface->commit(KWayland::Client::Surface::CommitFlag::None);
1206     QCOMPARE(window->frameGeometry().topLeft(), oldPosition);
1207     QCOMPARE(window->frameGeometry().size(), QSize(180, 80));
1208     QCOMPARE(window->bufferGeometry().topLeft(), oldPosition - QPoint(10, 10));
1209     QCOMPARE(window->bufferGeometry().size(), QSize(200, 100));
1210 
1211     shellSurface->xdgSurface()->set_window_geometry(-15, -15, 50, 40);
1212     surface->commit(KWayland::Client::Surface::CommitFlag::None);
1213     QVERIFY(frameGeometryChangedSpy.wait());
1214     QCOMPARE(window->frameGeometry().topLeft(), oldPosition);
1215     QCOMPARE(window->frameGeometry().size(), QSize(50, 40));
1216     QCOMPARE(window->bufferGeometry().topLeft(), oldPosition - QPoint(-15, -15));
1217     QCOMPARE(window->bufferGeometry().size(), QSize(200, 100));
1218 }
1219 
1220 void TestXdgShellWindow::testXdgWindowGeometryInteractiveResize()
1221 {
1222     // This test verifies that correct window geometry is provided along each
1223     // configure event when an xdg-shell is being interactively resized.
1224 
1225     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
1226     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
1227     Window *window = Test::renderAndWaitForShown(surface.get(), QSize(200, 100), Qt::red);
1228     QVERIFY(window);
1229     QVERIFY(window->isActive());
1230     QCOMPARE(window->bufferGeometry().size(), QSize(200, 100));
1231     QCOMPARE(window->frameGeometry().size(), QSize(200, 100));
1232 
1233     QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
1234     QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
1235     QVERIFY(surfaceConfigureRequestedSpy.wait());
1236     QCOMPARE(surfaceConfigureRequestedSpy.count(), 1);
1237 
1238     QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
1239     shellSurface->xdgSurface()->set_window_geometry(10, 10, 180, 80);
1240     surface->commit(KWayland::Client::Surface::CommitFlag::None);
1241     QVERIFY(frameGeometryChangedSpy.wait());
1242     QCOMPARE(window->bufferGeometry().size(), QSize(200, 100));
1243     QCOMPARE(window->frameGeometry().size(), QSize(180, 80));
1244 
1245     QSignalSpy clientStartMoveResizedSpy(window, &Window::clientStartUserMovedResized);
1246     QSignalSpy clientStepUserMovedResizedSpy(window, &Window::clientStepUserMovedResized);
1247     QSignalSpy clientFinishUserMovedResizedSpy(window, &Window::clientFinishUserMovedResized);
1248 
1249     // Start interactively resizing the window.
1250     QCOMPARE(workspace()->moveResizeWindow(), nullptr);
1251     workspace()->slotWindowResize();
1252     QCOMPARE(workspace()->moveResizeWindow(), window);
1253     QCOMPARE(clientStartMoveResizedSpy.count(), 1);
1254     QVERIFY(surfaceConfigureRequestedSpy.wait());
1255     QCOMPARE(surfaceConfigureRequestedSpy.count(), 2);
1256     Test::XdgToplevel::States states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
1257     QVERIFY(states.testFlag(Test::XdgToplevel::State::Resizing));
1258 
1259     // Go right.
1260     QPoint cursorPos = KWin::Cursors::self()->mouse()->pos();
1261     window->keyPressEvent(Qt::Key_Right);
1262     window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos());
1263     QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 0));
1264     QVERIFY(surfaceConfigureRequestedSpy.wait());
1265     QCOMPARE(surfaceConfigureRequestedSpy.count(), 3);
1266     states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
1267     QVERIFY(states.testFlag(Test::XdgToplevel::State::Resizing));
1268     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(188, 80));
1269     shellSurface->xdgSurface()->set_window_geometry(10, 10, 188, 80);
1270     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
1271     Test::render(surface.get(), QSize(208, 100), Qt::blue);
1272     QVERIFY(frameGeometryChangedSpy.wait());
1273     QCOMPARE(clientStepUserMovedResizedSpy.count(), 1);
1274     QCOMPARE(window->bufferGeometry().size(), QSize(208, 100));
1275     QCOMPARE(window->frameGeometry().size(), QSize(188, 80));
1276 
1277     // Go down.
1278     cursorPos = KWin::Cursors::self()->mouse()->pos();
1279     window->keyPressEvent(Qt::Key_Down);
1280     window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos());
1281     QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(0, 8));
1282     QVERIFY(surfaceConfigureRequestedSpy.wait());
1283     QCOMPARE(surfaceConfigureRequestedSpy.count(), 4);
1284     states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
1285     QVERIFY(states.testFlag(Test::XdgToplevel::State::Resizing));
1286     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(188, 88));
1287     shellSurface->xdgSurface()->set_window_geometry(10, 10, 188, 88);
1288     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
1289     Test::render(surface.get(), QSize(208, 108), Qt::blue);
1290     QVERIFY(frameGeometryChangedSpy.wait());
1291     QCOMPARE(clientStepUserMovedResizedSpy.count(), 2);
1292     QCOMPARE(window->bufferGeometry().size(), QSize(208, 108));
1293     QCOMPARE(window->frameGeometry().size(), QSize(188, 88));
1294 
1295     // Finish resizing the window.
1296     window->keyPressEvent(Qt::Key_Enter);
1297     QCOMPARE(clientFinishUserMovedResizedSpy.count(), 1);
1298     QCOMPARE(workspace()->moveResizeWindow(), nullptr);
1299     QVERIFY(surfaceConfigureRequestedSpy.wait());
1300     QCOMPARE(surfaceConfigureRequestedSpy.count(), 5);
1301     states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
1302     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Resizing));
1303 
1304     shellSurface.reset();
1305     QVERIFY(Test::waitForWindowDestroyed(window));
1306 }
1307 
1308 void TestXdgShellWindow::testXdgWindowGeometryFullScreen()
1309 {
1310     // This test verifies that an xdg-shell receives correct window geometry when
1311     // its fullscreen state gets changed.
1312 
1313     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
1314     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
1315     Window *window = Test::renderAndWaitForShown(surface.get(), QSize(200, 100), Qt::red);
1316     QVERIFY(window);
1317     QVERIFY(window->isActive());
1318     QCOMPARE(window->bufferGeometry().size(), QSize(200, 100));
1319     QCOMPARE(window->frameGeometry().size(), QSize(200, 100));
1320 
1321     QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
1322     QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
1323     QVERIFY(surfaceConfigureRequestedSpy.wait());
1324     QCOMPARE(surfaceConfigureRequestedSpy.count(), 1);
1325 
1326     QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
1327     shellSurface->xdgSurface()->set_window_geometry(10, 10, 180, 80);
1328     surface->commit(KWayland::Client::Surface::CommitFlag::None);
1329     QVERIFY(frameGeometryChangedSpy.wait());
1330     QCOMPARE(window->bufferGeometry().size(), QSize(200, 100));
1331     QCOMPARE(window->frameGeometry().size(), QSize(180, 80));
1332 
1333     workspace()->slotWindowFullScreen();
1334     QVERIFY(surfaceConfigureRequestedSpy.wait());
1335     QCOMPARE(surfaceConfigureRequestedSpy.count(), 2);
1336     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(1280, 1024));
1337     Test::XdgToplevel::States states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
1338     QVERIFY(states.testFlag(Test::XdgToplevel::State::Fullscreen));
1339     shellSurface->xdgSurface()->set_window_geometry(0, 0, 1280, 1024);
1340     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
1341     Test::render(surface.get(), QSize(1280, 1024), Qt::blue);
1342     QVERIFY(frameGeometryChangedSpy.wait());
1343     QCOMPARE(window->bufferGeometry().size(), QSize(1280, 1024));
1344     QCOMPARE(window->frameGeometry().size(), QSize(1280, 1024));
1345 
1346     workspace()->slotWindowFullScreen();
1347     QVERIFY(surfaceConfigureRequestedSpy.wait());
1348     QCOMPARE(surfaceConfigureRequestedSpy.count(), 3);
1349     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(180, 80));
1350     states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
1351     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Fullscreen));
1352     shellSurface->xdgSurface()->set_window_geometry(10, 10, 180, 80);
1353     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
1354     Test::render(surface.get(), QSize(200, 100), Qt::blue);
1355     QVERIFY(frameGeometryChangedSpy.wait());
1356     QCOMPARE(window->bufferGeometry().size(), QSize(200, 100));
1357     QCOMPARE(window->frameGeometry().size(), QSize(180, 80));
1358 
1359     shellSurface.reset();
1360     QVERIFY(Test::waitForWindowDestroyed(window));
1361 }
1362 
1363 void TestXdgShellWindow::testXdgWindowGeometryMaximize()
1364 {
1365     // This test verifies that an xdg-shell receives correct window geometry when
1366     // its maximized state gets changed.
1367 
1368     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
1369     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
1370     Window *window = Test::renderAndWaitForShown(surface.get(), QSize(200, 100), Qt::red);
1371     QVERIFY(window);
1372     QVERIFY(window->isActive());
1373     QCOMPARE(window->bufferGeometry().size(), QSize(200, 100));
1374     QCOMPARE(window->frameGeometry().size(), QSize(200, 100));
1375 
1376     QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
1377     QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
1378     QVERIFY(surfaceConfigureRequestedSpy.wait());
1379     QCOMPARE(surfaceConfigureRequestedSpy.count(), 1);
1380 
1381     QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
1382     shellSurface->xdgSurface()->set_window_geometry(10, 10, 180, 80);
1383     surface->commit(KWayland::Client::Surface::CommitFlag::None);
1384     QVERIFY(frameGeometryChangedSpy.wait());
1385     QCOMPARE(window->bufferGeometry().size(), QSize(200, 100));
1386     QCOMPARE(window->frameGeometry().size(), QSize(180, 80));
1387 
1388     workspace()->slotWindowMaximize();
1389     QVERIFY(surfaceConfigureRequestedSpy.wait());
1390     QCOMPARE(surfaceConfigureRequestedSpy.count(), 2);
1391     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(1280, 1024));
1392     Test::XdgToplevel::States states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
1393     QVERIFY(states.testFlag(Test::XdgToplevel::State::Maximized));
1394     shellSurface->xdgSurface()->set_window_geometry(0, 0, 1280, 1024);
1395     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
1396     Test::render(surface.get(), QSize(1280, 1024), Qt::blue);
1397     QVERIFY(frameGeometryChangedSpy.wait());
1398     QCOMPARE(window->bufferGeometry().size(), QSize(1280, 1024));
1399     QCOMPARE(window->frameGeometry().size(), QSize(1280, 1024));
1400 
1401     workspace()->slotWindowMaximize();
1402     QVERIFY(surfaceConfigureRequestedSpy.wait());
1403     QCOMPARE(surfaceConfigureRequestedSpy.count(), 3);
1404     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(180, 80));
1405     states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
1406     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
1407     shellSurface->xdgSurface()->set_window_geometry(10, 10, 180, 80);
1408     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
1409     Test::render(surface.get(), QSize(200, 100), Qt::blue);
1410     QVERIFY(frameGeometryChangedSpy.wait());
1411     QCOMPARE(window->bufferGeometry().size(), QSize(200, 100));
1412     QCOMPARE(window->frameGeometry().size(), QSize(180, 80));
1413 
1414     shellSurface.reset();
1415     QVERIFY(Test::waitForWindowDestroyed(window));
1416 }
1417 
1418 void TestXdgShellWindow::testPointerInputTransform()
1419 {
1420     // This test verifies that XdgToplevelWindow provides correct input transform matrix.
1421     // The input transform matrix is used by seat to map pointer events from the global
1422     // screen coordinates to the surface-local coordinates.
1423 
1424     // Get a wl_pointer object on the client side.
1425     std::unique_ptr<KWayland::Client::Pointer> pointer(Test::waylandSeat()->createPointer());
1426     QVERIFY(pointer);
1427     QVERIFY(pointer->isValid());
1428     QSignalSpy pointerEnteredSpy(pointer.get(), &KWayland::Client::Pointer::entered);
1429     QSignalSpy pointerMotionSpy(pointer.get(), &KWayland::Client::Pointer::motion);
1430 
1431     // Create an xdg_toplevel surface and wait for the compositor to catch up.
1432     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
1433     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
1434     Window *window = Test::renderAndWaitForShown(surface.get(), QSize(200, 100), Qt::red);
1435     QVERIFY(window);
1436     QVERIFY(window->isActive());
1437     QCOMPARE(window->bufferGeometry().size(), QSize(200, 100));
1438     QCOMPARE(window->frameGeometry().size(), QSize(200, 100));
1439 
1440     // Enter the surface.
1441     quint32 timestamp = 0;
1442     Test::pointerMotion(window->pos(), timestamp++);
1443     QVERIFY(pointerEnteredSpy.wait());
1444 
1445     // Move the pointer to (10, 5) relative to the upper left frame corner, which is located
1446     // at (0, 0) in the surface-local coordinates.
1447     Test::pointerMotion(window->pos() + QPointF(10, 5), timestamp++);
1448     QVERIFY(pointerMotionSpy.wait());
1449     QCOMPARE(pointerMotionSpy.last().first().toPointF(), QPointF(10, 5));
1450 
1451     // Let's pretend that the window has changed the extents of the client-side drop-shadow
1452     // but the frame geometry didn't change.
1453     QSignalSpy bufferGeometryChangedSpy(window, &Window::bufferGeometryChanged);
1454     QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
1455     shellSurface->xdgSurface()->set_window_geometry(10, 20, 200, 100);
1456     Test::render(surface.get(), QSize(220, 140), Qt::blue);
1457     QVERIFY(bufferGeometryChangedSpy.wait());
1458     QCOMPARE(frameGeometryChangedSpy.count(), 0);
1459     QCOMPARE(window->frameGeometry().size(), QSize(200, 100));
1460     QCOMPARE(window->bufferGeometry().size(), QSize(220, 140));
1461 
1462     // Move the pointer to (20, 50) relative to the upper left frame corner, which is located
1463     // at (10, 20) in the surface-local coordinates.
1464     Test::pointerMotion(window->pos() + QPointF(20, 50), timestamp++);
1465     QVERIFY(pointerMotionSpy.wait());
1466     QCOMPARE(pointerMotionSpy.last().first().toPointF(), QPointF(10, 20) + QPointF(20, 50));
1467 
1468     // Destroy the xdg-toplevel surface.
1469     shellSurface.reset();
1470     QVERIFY(Test::waitForWindowDestroyed(window));
1471 }
1472 
1473 void TestXdgShellWindow::testReentrantSetFrameGeometry()
1474 {
1475     // This test verifies that calling moveResize() from a slot connected directly
1476     // to the frameGeometryChanged() signal won't cause an infinite recursion.
1477 
1478     // Create an xdg-toplevel surface and wait for the compositor to catch up.
1479     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
1480     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
1481     Window *window = Test::renderAndWaitForShown(surface.get(), QSize(200, 100), Qt::red);
1482     QVERIFY(window);
1483     QCOMPARE(window->pos(), QPoint(0, 0));
1484 
1485     // Let's pretend that there is a script that really wants the window to be at (100, 100).
1486     connect(window, &Window::frameGeometryChanged, this, [window]() {
1487         window->moveResize(QRectF(QPointF(100, 100), window->size()));
1488     });
1489 
1490     // Trigger the lambda above.
1491     window->move(QPoint(40, 50));
1492 
1493     // Eventually, the window will end up at (100, 100).
1494     QCOMPARE(window->pos(), QPoint(100, 100));
1495 
1496     // Destroy the xdg-toplevel surface.
1497     shellSurface.reset();
1498     QVERIFY(Test::waitForWindowDestroyed(window));
1499 }
1500 
1501 void TestXdgShellWindow::testDoubleMaximize()
1502 {
1503     // This test verifies that the case where a window issues two set_maximized() requests
1504     // separated by the initial commit is handled properly.
1505 
1506     // Create the test surface.
1507     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
1508     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
1509     shellSurface->set_maximized();
1510     surface->commit(KWayland::Client::Surface::CommitFlag::None);
1511 
1512     // Wait for the compositor to respond with a configure event.
1513     QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
1514     QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
1515     QVERIFY(surfaceConfigureRequestedSpy.wait());
1516     QCOMPARE(surfaceConfigureRequestedSpy.count(), 1);
1517 
1518     QSize size = toplevelConfigureRequestedSpy.last().at(0).toSize();
1519     QCOMPARE(size, QSize(1280, 1024));
1520     Test::XdgToplevel::States states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
1521     QVERIFY(states.testFlag(Test::XdgToplevel::State::Maximized));
1522 
1523     // Send another set_maximized() request, but do not attach any buffer yet.
1524     shellSurface->set_maximized();
1525     surface->commit(KWayland::Client::Surface::CommitFlag::None);
1526 
1527     // The compositor must respond with another configure event even if the state hasn't changed.
1528     QVERIFY(surfaceConfigureRequestedSpy.wait());
1529     QCOMPARE(surfaceConfigureRequestedSpy.count(), 2);
1530     size = toplevelConfigureRequestedSpy.last().at(0).toSize();
1531     QCOMPARE(size, QSize(1280, 1024));
1532     states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
1533     QVERIFY(states.testFlag(Test::XdgToplevel::State::Maximized));
1534 }
1535 
1536 void TestXdgShellWindow::testDoubleFullscreenSeparatedByCommit()
1537 {
1538     // Some applications do weird things at startup and this is one of them. This test verifies
1539     // that the window will have good frame geometry if the window has issued several
1540     // xdg_toplevel.set_fullscreen requests and they are separated by a surface commit with
1541     // no attached buffer.
1542 
1543     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
1544     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
1545     QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
1546     QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
1547 
1548     // Tell the compositor that we want the window to be shown in fullscreen mode.
1549     shellSurface->set_fullscreen(nullptr);
1550     QVERIFY(surfaceConfigureRequestedSpy.wait());
1551     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), QSize(1280, 1024));
1552     QVERIFY(toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>() & Test::XdgToplevel::State::Fullscreen);
1553 
1554     // Ask again.
1555     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
1556     surface->commit(KWayland::Client::Surface::CommitFlag::None);
1557     shellSurface->set_fullscreen(nullptr);
1558     QVERIFY(surfaceConfigureRequestedSpy.wait());
1559     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), QSize(1280, 1024));
1560     QVERIFY(toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>() & Test::XdgToplevel::State::Fullscreen);
1561 
1562     // Map the window.
1563     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
1564     auto window = Test::renderAndWaitForShown(surface.get(), QSize(1280, 1024), Qt::blue);
1565     QVERIFY(window->isFullScreen());
1566     QCOMPARE(window->frameGeometry(), QRect(0, 0, 1280, 1024));
1567 }
1568 
1569 void TestXdgShellWindow::testMaximizeHorizontal()
1570 {
1571     // Create the test window.
1572     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
1573     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly));
1574 
1575     QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
1576     QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
1577     surface->commit(KWayland::Client::Surface::CommitFlag::None);
1578 
1579     // Wait for the initial configure event.
1580     Test::XdgToplevel::States states;
1581     QVERIFY(surfaceConfigureRequestedSpy.wait());
1582     QCOMPARE(surfaceConfigureRequestedSpy.count(), 1);
1583     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(0, 0));
1584     states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
1585     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Activated));
1586     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
1587 
1588     // Map the window.
1589     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
1590     Window *window = Test::renderAndWaitForShown(surface.get(), QSize(800, 600), Qt::blue);
1591     QVERIFY(window);
1592     QVERIFY(window->isActive());
1593     QVERIFY(window->isMaximizable());
1594     QCOMPARE(window->maximizeMode(), MaximizeMode::MaximizeRestore);
1595     QCOMPARE(window->requestedMaximizeMode(), MaximizeMode::MaximizeRestore);
1596     QCOMPARE(window->size(), QSize(800, 600));
1597 
1598     // We should receive a configure event when the window becomes active.
1599     QVERIFY(surfaceConfigureRequestedSpy.wait());
1600     QCOMPARE(surfaceConfigureRequestedSpy.count(), 2);
1601     states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
1602     QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
1603     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
1604 
1605     // Maximize the test window in horizontal direction.
1606     workspace()->slotWindowMaximizeHorizontal();
1607     QCOMPARE(window->requestedMaximizeMode(), MaximizeHorizontal);
1608     QCOMPARE(window->maximizeMode(), MaximizeRestore);
1609     QVERIFY(surfaceConfigureRequestedSpy.wait());
1610     QCOMPARE(surfaceConfigureRequestedSpy.count(), 3);
1611     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(1280, 600));
1612     states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
1613     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
1614 
1615     // Draw contents of the maximized window.
1616     QSignalSpy geometryChangedSpy(window, &Window::geometryChanged);
1617     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
1618     Test::render(surface.get(), QSize(1280, 600), Qt::blue);
1619     QVERIFY(geometryChangedSpy.wait());
1620     QCOMPARE(window->size(), QSize(1280, 600));
1621     QCOMPARE(window->requestedMaximizeMode(), MaximizeHorizontal);
1622     QCOMPARE(window->maximizeMode(), MaximizeHorizontal);
1623 
1624     // Restore the window.
1625     workspace()->slotWindowMaximizeHorizontal();
1626     QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
1627     QCOMPARE(window->maximizeMode(), MaximizeHorizontal);
1628     QVERIFY(surfaceConfigureRequestedSpy.wait());
1629     QCOMPARE(surfaceConfigureRequestedSpy.count(), 4);
1630     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(800, 600));
1631     states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
1632     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
1633 
1634     // Draw contents of the restored window.
1635     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
1636     Test::render(surface.get(), QSize(800, 600), Qt::blue);
1637     QVERIFY(geometryChangedSpy.wait());
1638     QCOMPARE(window->size(), QSize(800, 600));
1639     QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
1640     QCOMPARE(window->maximizeMode(), MaximizeRestore);
1641 
1642     // Destroy the window.
1643     shellSurface.reset();
1644     surface.reset();
1645     QVERIFY(Test::waitForWindowDestroyed(window));
1646 }
1647 
1648 void TestXdgShellWindow::testMaximizeVertical()
1649 {
1650     // Create the test window.
1651     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
1652     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly));
1653 
1654     QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
1655     QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
1656     surface->commit(KWayland::Client::Surface::CommitFlag::None);
1657 
1658     // Wait for the initial configure event.
1659     Test::XdgToplevel::States states;
1660     QVERIFY(surfaceConfigureRequestedSpy.wait());
1661     QCOMPARE(surfaceConfigureRequestedSpy.count(), 1);
1662     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(0, 0));
1663     states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
1664     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Activated));
1665     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
1666 
1667     // Map the window.
1668     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
1669     Window *window = Test::renderAndWaitForShown(surface.get(), QSize(800, 600), Qt::blue);
1670     QVERIFY(window);
1671     QVERIFY(window->isActive());
1672     QVERIFY(window->isMaximizable());
1673     QCOMPARE(window->maximizeMode(), MaximizeMode::MaximizeRestore);
1674     QCOMPARE(window->requestedMaximizeMode(), MaximizeMode::MaximizeRestore);
1675     QCOMPARE(window->size(), QSize(800, 600));
1676 
1677     // We should receive a configure event when the window becomes active.
1678     QVERIFY(surfaceConfigureRequestedSpy.wait());
1679     QCOMPARE(surfaceConfigureRequestedSpy.count(), 2);
1680     states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
1681     QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
1682     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
1683 
1684     // Maximize the test window in vertical direction.
1685     workspace()->slotWindowMaximizeVertical();
1686     QCOMPARE(window->requestedMaximizeMode(), MaximizeVertical);
1687     QCOMPARE(window->maximizeMode(), MaximizeRestore);
1688     QVERIFY(surfaceConfigureRequestedSpy.wait());
1689     QCOMPARE(surfaceConfigureRequestedSpy.count(), 3);
1690     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(800, 1024));
1691     states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
1692     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
1693 
1694     // Draw contents of the maximized window.
1695     QSignalSpy geometryChangedSpy(window, &Window::geometryChanged);
1696     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
1697     Test::render(surface.get(), QSize(800, 1024), Qt::blue);
1698     QVERIFY(geometryChangedSpy.wait());
1699     QCOMPARE(window->size(), QSize(800, 1024));
1700     QCOMPARE(window->requestedMaximizeMode(), MaximizeVertical);
1701     QCOMPARE(window->maximizeMode(), MaximizeVertical);
1702 
1703     // Restore the window.
1704     workspace()->slotWindowMaximizeVertical();
1705     QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
1706     QCOMPARE(window->maximizeMode(), MaximizeVertical);
1707     QVERIFY(surfaceConfigureRequestedSpy.wait());
1708     QCOMPARE(surfaceConfigureRequestedSpy.count(), 4);
1709     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(800, 600));
1710     states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
1711     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
1712 
1713     // Draw contents of the restored window.
1714     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
1715     Test::render(surface.get(), QSize(800, 600), Qt::blue);
1716     QVERIFY(geometryChangedSpy.wait());
1717     QCOMPARE(window->size(), QSize(800, 600));
1718     QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
1719     QCOMPARE(window->maximizeMode(), MaximizeRestore);
1720 
1721     // Destroy the window.
1722     shellSurface.reset();
1723     surface.reset();
1724     QVERIFY(Test::waitForWindowDestroyed(window));
1725 }
1726 
1727 void TestXdgShellWindow::testMaximizeFull()
1728 {
1729     // Create the test window.
1730     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
1731     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly));
1732 
1733     QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
1734     QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
1735     surface->commit(KWayland::Client::Surface::CommitFlag::None);
1736 
1737     // Wait for the initial configure event.
1738     Test::XdgToplevel::States states;
1739     QVERIFY(surfaceConfigureRequestedSpy.wait());
1740     QCOMPARE(surfaceConfigureRequestedSpy.count(), 1);
1741     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(0, 0));
1742     states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
1743     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Activated));
1744     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
1745 
1746     // Map the window.
1747     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
1748     Window *window = Test::renderAndWaitForShown(surface.get(), QSize(800, 600), Qt::blue);
1749     QVERIFY(window);
1750     QVERIFY(window->isActive());
1751     QVERIFY(window->isMaximizable());
1752     QCOMPARE(window->maximizeMode(), MaximizeMode::MaximizeRestore);
1753     QCOMPARE(window->requestedMaximizeMode(), MaximizeMode::MaximizeRestore);
1754     QCOMPARE(window->size(), QSize(800, 600));
1755 
1756     // We should receive a configure event when the window becomes active.
1757     QVERIFY(surfaceConfigureRequestedSpy.wait());
1758     QCOMPARE(surfaceConfigureRequestedSpy.count(), 2);
1759     states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
1760     QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
1761     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
1762 
1763     // Maximize the test window.
1764     workspace()->slotWindowMaximize();
1765     QCOMPARE(window->requestedMaximizeMode(), MaximizeFull);
1766     QCOMPARE(window->maximizeMode(), MaximizeRestore);
1767     QVERIFY(surfaceConfigureRequestedSpy.wait());
1768     QCOMPARE(surfaceConfigureRequestedSpy.count(), 3);
1769     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(1280, 1024));
1770     states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
1771     QVERIFY(states.testFlag(Test::XdgToplevel::State::Maximized));
1772 
1773     // Draw contents of the maximized window.
1774     QSignalSpy geometryChangedSpy(window, &Window::geometryChanged);
1775     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
1776     Test::render(surface.get(), QSize(1280, 1024), Qt::blue);
1777     QVERIFY(geometryChangedSpy.wait());
1778     QCOMPARE(window->size(), QSize(1280, 1024));
1779     QCOMPARE(window->requestedMaximizeMode(), MaximizeFull);
1780     QCOMPARE(window->maximizeMode(), MaximizeFull);
1781 
1782     // Restore the window.
1783     workspace()->slotWindowMaximize();
1784     QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
1785     QCOMPARE(window->maximizeMode(), MaximizeFull);
1786     QVERIFY(surfaceConfigureRequestedSpy.wait());
1787     QCOMPARE(surfaceConfigureRequestedSpy.count(), 4);
1788     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(800, 600));
1789     states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>();
1790     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
1791 
1792     // Draw contents of the restored window.
1793     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
1794     Test::render(surface.get(), QSize(800, 600), Qt::blue);
1795     QVERIFY(geometryChangedSpy.wait());
1796     QCOMPARE(window->size(), QSize(800, 600));
1797     QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
1798     QCOMPARE(window->maximizeMode(), MaximizeRestore);
1799 
1800     // Destroy the window.
1801     shellSurface.reset();
1802     surface.reset();
1803     QVERIFY(Test::waitForWindowDestroyed(window));
1804 }
1805 
1806 void TestXdgShellWindow::testSendMaximizedWindowToAnotherOutput()
1807 {
1808     // This test verifies that the maximized window will have correct geometry restore
1809     // after it's sent to another output.
1810 
1811     const auto outputs = workspace()->outputs();
1812 
1813     // Create the window.
1814     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
1815     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
1816     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
1817     QVERIFY(window);
1818 
1819     // Wait for the compositor to send a configure event with the activated state.
1820     QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
1821     QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
1822     QVERIFY(surfaceConfigureRequestedSpy.wait());
1823 
1824     // Move the window to the left monitor.
1825     window->move(QPointF(10, 20));
1826     QCOMPARE(window->frameGeometry(), QRectF(10, 20, 100, 50));
1827     QCOMPARE(window->output(), outputs[0]);
1828 
1829     // Make the window maximized.
1830     QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
1831     shellSurface->set_maximized();
1832     QVERIFY(surfaceConfigureRequestedSpy.wait());
1833     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
1834     Test::render(surface.get(), toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), Qt::red);
1835     QVERIFY(frameGeometryChangedSpy.wait());
1836     QCOMPARE(window->maximizeMode(), MaximizeFull);
1837     QCOMPARE(window->frameGeometry(), QRectF(0, 0, 1280, 1024));
1838     QCOMPARE(window->geometryRestore(), QRectF(10, 20, 100, 50));
1839     QCOMPARE(window->output(), outputs[0]);
1840 
1841     // Send the window to another output.
1842     workspace()->sendWindowToOutput(window, outputs[1]);
1843     QCOMPARE(window->maximizeMode(), MaximizeFull);
1844     QCOMPARE(window->frameGeometry(), QRectF(1280, 0, 1280, 1024));
1845     QCOMPARE(window->geometryRestore(), QRectF(1280 + 10, 20, 100, 50));
1846     QCOMPARE(window->output(), outputs[1]);
1847 }
1848 
1849 void TestXdgShellWindow::testMaximizeAndChangeDecorationModeAfterInitialCommit()
1850 {
1851     // Ideally, the app would initialize the xdg-toplevel surface before the initial commit, but
1852     // many don't do it. They initialize the surface after the first commit.
1853     // This test verifies that the window will receive a configure event with correct size
1854     // if an xdg-toplevel surface is set maximized and decoration mode changes after initial commit.
1855 
1856     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
1857     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly));
1858     std::unique_ptr<Test::XdgToplevelDecorationV1> decoration(Test::createXdgToplevelDecorationV1(shellSurface.get()));
1859     QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
1860     QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
1861 
1862     // Commit the initial state.
1863     surface->commit(KWayland::Client::Surface::CommitFlag::None);
1864     QVERIFY(surfaceConfigureRequestedSpy.wait());
1865     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), QSize(0, 0));
1866 
1867     // Request maximized mode and set decoration mode, i.e. perform late initialization.
1868     shellSurface->set_maximized();
1869     decoration->set_mode(Test::XdgToplevelDecorationV1::mode_client_side);
1870 
1871     // The compositor will respond with a new configure event, which should contain maximized state.
1872     QVERIFY(surfaceConfigureRequestedSpy.wait());
1873     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), QSize(1280, 1024));
1874     QCOMPARE(toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>(), Test::XdgToplevel::State::Maximized);
1875 }
1876 
1877 void TestXdgShellWindow::testFullScreenAndChangeDecorationModeAfterInitialCommit()
1878 {
1879     // Ideally, the app would initialize the xdg-toplevel surface before the initial commit, but
1880     // many don't do it. They initialize the surface after the first commit.
1881     // This test verifies that the window will receive a configure event with correct size
1882     // if an xdg-toplevel surface is set fullscreen and decoration mode changes after initial commit.
1883 
1884     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
1885     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly));
1886     std::unique_ptr<Test::XdgToplevelDecorationV1> decoration(Test::createXdgToplevelDecorationV1(shellSurface.get()));
1887     QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
1888     QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
1889 
1890     // Commit the initial state.
1891     surface->commit(KWayland::Client::Surface::CommitFlag::None);
1892     QVERIFY(surfaceConfigureRequestedSpy.wait());
1893     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), QSize(0, 0));
1894 
1895     // Request fullscreen mode and set decoration mode, i.e. perform late initialization.
1896     shellSurface->set_fullscreen(nullptr);
1897     decoration->set_mode(Test::XdgToplevelDecorationV1::mode_client_side);
1898 
1899     // The compositor will respond with a new configure event, which should contain fullscreen state.
1900     QVERIFY(surfaceConfigureRequestedSpy.wait());
1901     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), QSize(1280, 1024));
1902     QCOMPARE(toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>(), Test::XdgToplevel::State::Fullscreen);
1903 }
1904 
1905 void TestXdgShellWindow::testChangeDecorationModeAfterInitialCommit()
1906 {
1907     // This test verifies that the compositor will respond with a good configure event when
1908     // the decoration mode changes after the first surface commit but before the surface is mapped.
1909 
1910     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
1911     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly));
1912     std::unique_ptr<Test::XdgToplevelDecorationV1> decoration(Test::createXdgToplevelDecorationV1(shellSurface.get()));
1913     QSignalSpy decorationConfigureRequestedSpy(decoration.get(), &Test::XdgToplevelDecorationV1::configureRequested);
1914     QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
1915     QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
1916 
1917     // Perform the initial commit.
1918     surface->commit(KWayland::Client::Surface::CommitFlag::None);
1919     QVERIFY(surfaceConfigureRequestedSpy.wait());
1920     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), QSize(0, 0));
1921     QCOMPARE(decorationConfigureRequestedSpy.last().at(0).value<Test::XdgToplevelDecorationV1::mode>(), Test::XdgToplevelDecorationV1::mode_server_side);
1922 
1923     // Change decoration mode.
1924     decoration->set_mode(Test::XdgToplevelDecorationV1::mode_client_side);
1925 
1926     // The configure event should still have 0x0 size.
1927     QVERIFY(surfaceConfigureRequestedSpy.wait());
1928     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), QSize(0, 0));
1929     QCOMPARE(decorationConfigureRequestedSpy.last().at(0).value<Test::XdgToplevelDecorationV1::mode>(), Test::XdgToplevelDecorationV1::mode_client_side);
1930 }
1931 
1932 WAYLANDTEST_MAIN(TestXdgShellWindow)
1933 #include "xdgshellwindow_test.moc"