File indexing completed on 2024-05-12 05:30:48

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