File indexing completed on 2024-05-19 09:23:23

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