File indexing completed on 2024-05-05 17:35:53

0001 /*
0002     SPDX-FileCopyrightText: 2021 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "kwin_wayland_test.h"
0008 
0009 #include "core/output.h"
0010 #include "core/outputbackend.h"
0011 #include "core/outputconfiguration.h"
0012 #include "cursor.h"
0013 #include "wayland_server.h"
0014 #include "window.h"
0015 #include "workspace.h"
0016 
0017 #include <KWayland/Client/surface.h>
0018 
0019 namespace KWin
0020 {
0021 
0022 static const QString s_socketName = QStringLiteral("wayland_test_output_changes-0");
0023 
0024 class OutputChangesTest : public QObject
0025 {
0026     Q_OBJECT
0027 
0028 private Q_SLOTS:
0029     void initTestCase();
0030     void init();
0031     void cleanup();
0032 
0033     void testWindowSticksToOutputAfterOutputIsDisabled();
0034     void testWindowSticksToOutputAfterAnotherOutputIsDisabled();
0035     void testWindowSticksToOutputAfterOutputIsMoved();
0036     void testWindowSticksToOutputAfterOutputsAreSwappedLeftToRight();
0037     void testWindowSticksToOutputAfterOutputsAreSwappedRightToLeft();
0038 
0039     void testWindowRestoredAfterEnablingOutput();
0040     void testMaximizedWindowRestoredAfterEnablingOutput();
0041     void testFullScreenWindowRestoredAfterEnablingOutput();
0042     void testWindowRestoredAfterChangingScale();
0043     void testMaximizeStateRestoredAfterEnablingOutput();
0044 
0045     void testWindowNotRestoredAfterMovingWindowAndEnablingOutput();
0046 };
0047 
0048 void OutputChangesTest::initTestCase()
0049 {
0050     qRegisterMetaType<Window *>();
0051 
0052     QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
0053     QVERIFY(waylandServer()->init(s_socketName));
0054     QMetaObject::invokeMethod(kwinApp()->outputBackend(), "setVirtualOutputs", Qt::DirectConnection, Q_ARG(QVector<QRect>, QVector<QRect>() << QRect(0, 0, 1280, 1024) << QRect(1280, 0, 1280, 1024)));
0055 
0056     kwinApp()->start();
0057     QVERIFY(applicationStartedSpy.wait());
0058     const auto outputs = workspace()->outputs();
0059     QCOMPARE(outputs.count(), 2);
0060     QCOMPARE(outputs[0]->geometry(), QRect(0, 0, 1280, 1024));
0061     QCOMPARE(outputs[1]->geometry(), QRect(1280, 0, 1280, 1024));
0062 }
0063 
0064 void OutputChangesTest::init()
0065 {
0066     QMetaObject::invokeMethod(kwinApp()->outputBackend(), "setVirtualOutputs", Qt::DirectConnection, Q_ARG(QVector<QRect>, QVector<QRect>() << QRect(0, 0, 1280, 1024) << QRect(1280, 0, 1280, 1024)));
0067     QVERIFY(Test::setupWaylandConnection());
0068 
0069     workspace()->setActiveOutput(QPoint(640, 512));
0070     Cursors::self()->mouse()->setPos(QPoint(640, 512));
0071 }
0072 
0073 void OutputChangesTest::cleanup()
0074 {
0075     Test::destroyWaylandConnection();
0076 }
0077 
0078 void OutputChangesTest::testWindowSticksToOutputAfterOutputIsDisabled()
0079 {
0080     auto outputs = kwinApp()->outputBackend()->outputs();
0081 
0082     // Create a window.
0083     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0084     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0085     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0086     QVERIFY(window);
0087 
0088     // Move the window to some predefined position so the test is more robust.
0089     window->move(QPoint(42, 67));
0090     QCOMPARE(window->frameGeometry(), QRect(42, 67, 100, 50));
0091 
0092     // Disable the output where the window is on.
0093     OutputConfiguration config;
0094     {
0095         auto changeSet = config.changeSet(outputs[0]);
0096         changeSet->enabled = false;
0097     }
0098     workspace()->applyOutputConfiguration(config);
0099 
0100     // The window will be sent to the second output, which is at (1280, 0).
0101     QCOMPARE(window->frameGeometry(), QRect(1280 + 42, 0 + 67, 100, 50));
0102 }
0103 
0104 void OutputChangesTest::testWindowSticksToOutputAfterAnotherOutputIsDisabled()
0105 {
0106     auto outputs = kwinApp()->outputBackend()->outputs();
0107 
0108     // Create a window.
0109     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0110     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0111     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0112     QVERIFY(window);
0113 
0114     // Move the window to the second output.
0115     window->move(QPoint(1280 + 42, 67));
0116     QCOMPARE(window->frameGeometry(), QRect(1280 + 42, 67, 100, 50));
0117 
0118     // Disable the first output.
0119     OutputConfiguration config;
0120     {
0121         auto changeSet = config.changeSet(outputs[0]);
0122         changeSet->enabled = false;
0123     }
0124     {
0125         auto changeSet = config.changeSet(outputs[1]);
0126         changeSet->pos = QPoint(0, 0);
0127     }
0128     workspace()->applyOutputConfiguration(config);
0129 
0130     // The position of the window relative to its output should remain the same.
0131     QCOMPARE(window->frameGeometry(), QRect(42, 67, 100, 50));
0132 }
0133 
0134 void OutputChangesTest::testWindowSticksToOutputAfterOutputIsMoved()
0135 {
0136     auto outputs = kwinApp()->outputBackend()->outputs();
0137 
0138     // Create a window.
0139     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0140     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0141     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0142     QVERIFY(window);
0143 
0144     // Move the window to some predefined position so the test is more robust.
0145     window->move(QPoint(42, 67));
0146     QCOMPARE(window->frameGeometry(), QRect(42, 67, 100, 50));
0147 
0148     // Disable the first output.
0149     OutputConfiguration config;
0150     {
0151         auto changeSet = config.changeSet(outputs[0]);
0152         changeSet->pos = QPoint(-10, 20);
0153     }
0154     workspace()->applyOutputConfiguration(config);
0155 
0156     // The position of the window relative to its output should remain the same.
0157     QCOMPARE(window->frameGeometry(), QRect(-10 + 42, 20 + 67, 100, 50));
0158 }
0159 
0160 void OutputChangesTest::testWindowSticksToOutputAfterOutputsAreSwappedLeftToRight()
0161 {
0162     // This test verifies that a window placed on the left monitor sticks
0163     // to that monitor even after the monitors are swapped horizontally.
0164 
0165     const auto outputs = kwinApp()->outputBackend()->outputs();
0166 
0167     // Create a window.
0168     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0169     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0170     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0171     QVERIFY(window);
0172 
0173     // Move the window to the left output.
0174     window->move(QPointF(0, 0));
0175     QCOMPARE(window->output(), outputs[0]);
0176     QCOMPARE(window->frameGeometry(), QRectF(0, 0, 100, 50));
0177 
0178     // Swap outputs.
0179     OutputConfiguration config;
0180     {
0181         auto changeSet1 = config.changeSet(outputs[0]);
0182         changeSet1->pos = QPoint(1280, 0);
0183         auto changeSet2 = config.changeSet(outputs[1]);
0184         changeSet2->pos = QPoint(0, 0);
0185     }
0186     workspace()->applyOutputConfiguration(config);
0187 
0188     // The window should be still on its original output.
0189     QCOMPARE(window->output(), outputs[0]);
0190     QCOMPARE(window->frameGeometry(), QRectF(1280, 0, 100, 50));
0191 }
0192 
0193 void OutputChangesTest::testWindowSticksToOutputAfterOutputsAreSwappedRightToLeft()
0194 {
0195     // This test verifies that a window placed on the right monitor sticks
0196     // to that monitor even after the monitors are swapped horizontally.
0197 
0198     const auto outputs = kwinApp()->outputBackend()->outputs();
0199 
0200     // Create a window.
0201     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0202     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0203     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0204     QVERIFY(window);
0205 
0206     // Move the window to the right output.
0207     window->move(QPointF(1280, 0));
0208     QCOMPARE(window->output(), outputs[1]);
0209     QCOMPARE(window->frameGeometry(), QRectF(1280, 0, 100, 50));
0210 
0211     // Swap outputs.
0212     OutputConfiguration config;
0213     {
0214         auto changeSet1 = config.changeSet(outputs[0]);
0215         changeSet1->pos = QPoint(1280, 0);
0216         auto changeSet2 = config.changeSet(outputs[1]);
0217         changeSet2->pos = QPoint(0, 0);
0218     }
0219     workspace()->applyOutputConfiguration(config);
0220 
0221     // The window should be still on its original output.
0222     QCOMPARE(window->output(), outputs[1]);
0223     QCOMPARE(window->frameGeometry(), QRectF(0, 0, 100, 50));
0224 }
0225 
0226 void OutputChangesTest::testWindowRestoredAfterEnablingOutput()
0227 {
0228     // This test verifies that a window will be moved back to its original output when it's hotplugged.
0229 
0230     const auto outputs = kwinApp()->outputBackend()->outputs();
0231 
0232     // Create a window.
0233     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0234     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0235     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0236     QVERIFY(window);
0237 
0238     // Move the window to the right output.
0239     window->move(QPointF(1280 + 50, 100));
0240     QCOMPARE(window->output(), outputs[1]);
0241     QCOMPARE(window->frameGeometry(), QRectF(1280 + 50, 100, 100, 50));
0242 
0243     // Disable the right output.
0244     OutputConfiguration config1;
0245     {
0246         auto changeSet = config1.changeSet(outputs[1]);
0247         changeSet->enabled = false;
0248     }
0249     workspace()->applyOutputConfiguration(config1);
0250 
0251     // The window will be moved to the left monitor.
0252     QCOMPARE(window->output(), outputs[0]);
0253     QCOMPARE(window->frameGeometry(), QRectF(50, 100, 100, 50));
0254 
0255     // Enable the right monitor.
0256     OutputConfiguration config2;
0257     {
0258         auto changeSet = config2.changeSet(outputs[1]);
0259         changeSet->enabled = true;
0260     }
0261     workspace()->applyOutputConfiguration(config2);
0262 
0263     // The window will be moved back to the right monitor.
0264     QCOMPARE(window->output(), outputs[1]);
0265     QCOMPARE(window->frameGeometry(), QRectF(1280 + 50, 100, 100, 50));
0266 }
0267 
0268 void OutputChangesTest::testWindowNotRestoredAfterMovingWindowAndEnablingOutput()
0269 {
0270     // This test verifies that a window won't be moved to its original output when it's
0271     // hotplugged because the window was moved manually by the user.
0272 
0273     const auto outputs = kwinApp()->outputBackend()->outputs();
0274 
0275     // Create a window.
0276     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0277     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0278     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0279     QVERIFY(window);
0280 
0281     // Move the window to the right output.
0282     window->move(QPointF(1280 + 50, 100));
0283     QCOMPARE(window->output(), outputs[1]);
0284     QCOMPARE(window->frameGeometry(), QRectF(1280 + 50, 100, 100, 50));
0285 
0286     // Disable the right output.
0287     OutputConfiguration config1;
0288     {
0289         auto changeSet = config1.changeSet(outputs[1]);
0290         changeSet->enabled = false;
0291     }
0292     workspace()->applyOutputConfiguration(config1);
0293 
0294     // The window will be moved to the left monitor.
0295     QCOMPARE(window->output(), outputs[0]);
0296     QCOMPARE(window->frameGeometry(), QRectF(50, 100, 100, 50));
0297 
0298     // Pretend that the user moved the window.
0299     workspace()->slotWindowMove();
0300     QVERIFY(window->isInteractiveMove());
0301     window->keyPressEvent(Qt::Key_Right);
0302     window->keyPressEvent(Qt::Key_Enter);
0303     QCOMPARE(window->frameGeometry(), QRectF(58, 100, 100, 50));
0304 
0305     // Enable the right monitor.
0306     OutputConfiguration config2;
0307     {
0308         auto changeSet = config2.changeSet(outputs[1]);
0309         changeSet->enabled = true;
0310     }
0311     workspace()->applyOutputConfiguration(config2);
0312 
0313     // The window is still on the left monitor because user manually moved it.
0314     QCOMPARE(window->output(), outputs[0]);
0315     QCOMPARE(window->frameGeometry(), QRectF(58, 100, 100, 50));
0316 }
0317 
0318 void OutputChangesTest::testMaximizedWindowRestoredAfterEnablingOutput()
0319 {
0320     // This test verifies that a maximized window will be moved to its original
0321     // output when it's re-enabled.
0322 
0323     const auto outputs = kwinApp()->outputBackend()->outputs();
0324 
0325     // Create a window.
0326     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0327     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0328     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0329     QVERIFY(window);
0330 
0331     // kwin will send a configure event with the actived state.
0332     QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
0333     QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
0334     QVERIFY(surfaceConfigureRequestedSpy.wait());
0335 
0336     // Move the window to the right monitor and make it maximized.
0337     QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
0338     window->move(QPointF(1280 + 50, 100));
0339     window->maximize(MaximizeFull);
0340     QVERIFY(surfaceConfigureRequestedSpy.wait());
0341     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), QSize(1280, 1024));
0342     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
0343     Test::render(surface.get(), QSize(1280, 1024), Qt::blue);
0344     QVERIFY(frameGeometryChangedSpy.wait());
0345     QCOMPARE(window->frameGeometry(), QRectF(1280, 0, 1280, 1024));
0346     QCOMPARE(window->moveResizeGeometry(), QRectF(1280, 0, 1280, 1024));
0347     QCOMPARE(window->output(), outputs[1]);
0348     QCOMPARE(window->maximizeMode(), MaximizeFull);
0349     QCOMPARE(window->requestedMaximizeMode(), MaximizeFull);
0350     QCOMPARE(window->geometryRestore(), QRectF(1280 + 50, 100, 100, 50));
0351 
0352     // Disable the right output.
0353     OutputConfiguration config1;
0354     {
0355         auto changeSet = config1.changeSet(outputs[1]);
0356         changeSet->enabled = false;
0357     }
0358     workspace()->applyOutputConfiguration(config1);
0359 
0360     // The window will be moved to the left monitor, the geometry restore will be updated too.
0361     QCOMPARE(window->frameGeometry(), QRectF(0, 0, 1280, 1024));
0362     QCOMPARE(window->moveResizeGeometry(), QRectF(0, 0, 1280, 1024));
0363     QCOMPARE(window->output(), outputs[0]);
0364     QCOMPARE(window->maximizeMode(), MaximizeFull);
0365     QCOMPARE(window->requestedMaximizeMode(), MaximizeFull);
0366     QCOMPARE(window->geometryRestore(), QRectF(50, 100, 100, 50));
0367 
0368     // Enable the right monitor.
0369     OutputConfiguration config2;
0370     {
0371         auto changeSet = config2.changeSet(outputs[1]);
0372         changeSet->enabled = true;
0373     }
0374     workspace()->applyOutputConfiguration(config2);
0375 
0376     // The window will be moved back to the right monitor, the geometry restore will be updated too.
0377     QCOMPARE(window->frameGeometry(), QRectF(1280, 0, 1280, 1024));
0378     QCOMPARE(window->moveResizeGeometry(), QRectF(1280, 0, 1280, 1024));
0379     QCOMPARE(window->output(), outputs[1]);
0380     QCOMPARE(window->maximizeMode(), MaximizeFull);
0381     QCOMPARE(window->requestedMaximizeMode(), MaximizeFull);
0382     QCOMPARE(window->geometryRestore(), QRectF(1280 + 50, 100, 100, 50));
0383 }
0384 
0385 void OutputChangesTest::testFullScreenWindowRestoredAfterEnablingOutput()
0386 {
0387     // This test verifies that a fullscreen window will be moved to its original
0388     // output when it's re-enabled.
0389 
0390     const auto outputs = kwinApp()->outputBackend()->outputs();
0391 
0392     // Create a window.
0393     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0394     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0395     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0396     QVERIFY(window);
0397 
0398     // kwin will send a configure event with the actived state.
0399     QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
0400     QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
0401     QVERIFY(surfaceConfigureRequestedSpy.wait());
0402 
0403     // Move the window to the right monitor and make it fullscreen.
0404     QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
0405     window->move(QPointF(1280 + 50, 100));
0406     window->setFullScreen(true);
0407     QVERIFY(surfaceConfigureRequestedSpy.wait());
0408     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), QSize(1280, 1024));
0409     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
0410     Test::render(surface.get(), QSize(1280, 1024), Qt::blue);
0411     QVERIFY(frameGeometryChangedSpy.wait());
0412     QCOMPARE(window->frameGeometry(), QRectF(1280, 0, 1280, 1024));
0413     QCOMPARE(window->moveResizeGeometry(), QRectF(1280, 0, 1280, 1024));
0414     QCOMPARE(window->output(), outputs[1]);
0415     QCOMPARE(window->isFullScreen(), true);
0416     QCOMPARE(window->isRequestedFullScreen(), true);
0417     QCOMPARE(window->fullscreenGeometryRestore(), QRectF(1280 + 50, 100, 100, 50));
0418 
0419     // Disable the right output.
0420     OutputConfiguration config1;
0421     {
0422         auto changeSet = config1.changeSet(outputs[1]);
0423         changeSet->enabled = false;
0424     }
0425     workspace()->applyOutputConfiguration(config1);
0426 
0427     // The window will be moved to the left monitor, the geometry restore will be updated too.
0428     QCOMPARE(window->frameGeometry(), QRectF(0, 0, 1280, 1024));
0429     QCOMPARE(window->moveResizeGeometry(), QRectF(0, 0, 1280, 1024));
0430     QCOMPARE(window->output(), outputs[0]);
0431     QCOMPARE(window->isFullScreen(), true);
0432     QCOMPARE(window->isRequestedFullScreen(), true);
0433     QCOMPARE(window->fullscreenGeometryRestore(), QRectF(50, 100, 100, 50));
0434 
0435     // Enable the right monitor.
0436     OutputConfiguration config2;
0437     {
0438         auto changeSet = config2.changeSet(outputs[1]);
0439         changeSet->enabled = true;
0440     }
0441     workspace()->applyOutputConfiguration(config2);
0442 
0443     // The window will be moved back to the right monitor, the geometry restore will be updated too.
0444     QCOMPARE(window->frameGeometry(), QRectF(1280, 0, 1280, 1024));
0445     QCOMPARE(window->moveResizeGeometry(), QRectF(1280, 0, 1280, 1024));
0446     QCOMPARE(window->output(), outputs[1]);
0447     QCOMPARE(window->isFullScreen(), true);
0448     QCOMPARE(window->isRequestedFullScreen(), true);
0449     QCOMPARE(window->fullscreenGeometryRestore(), QRectF(1280 + 50, 100, 100, 50));
0450 }
0451 
0452 void OutputChangesTest::testWindowRestoredAfterChangingScale()
0453 {
0454     // This test verifies that a window will be moved to its original position after changing the scale of an output
0455 
0456     const auto output = kwinApp()->outputBackend()->outputs().front();
0457 
0458     // Create a window.
0459     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0460     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0461     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0462     QVERIFY(window);
0463 
0464     // Move the window to the bottom right
0465     const QPointF originalPosition(output->geometry().width() - window->width(), output->geometry().height() - window->height());
0466     window->move(originalPosition);
0467     QCOMPARE(window->pos(), originalPosition);
0468     QCOMPARE(window->output(), output);
0469 
0470     // change the scale of the output
0471     OutputConfiguration config1;
0472     {
0473         auto changeSet = config1.changeSet(output);
0474         changeSet->scale = 2;
0475     }
0476     workspace()->applyOutputConfiguration(config1);
0477 
0478     // The window will be moved to still be in the monitor
0479     QCOMPARE(window->pos(), QPointF(output->geometry().width() - window->width(), output->geometry().height() - window->height()));
0480     QCOMPARE(window->output(), output);
0481 
0482     // Change scale back
0483     OutputConfiguration config2;
0484     {
0485         auto changeSet = config2.changeSet(output);
0486         changeSet->scale = 1;
0487     }
0488     workspace()->applyOutputConfiguration(config2);
0489 
0490     // The window will be moved back to where it was before
0491     QCOMPARE(window->pos(), originalPosition);
0492     QCOMPARE(window->output(), output);
0493 }
0494 
0495 void OutputChangesTest::testMaximizeStateRestoredAfterEnablingOutput()
0496 {
0497     // This test verifies that the window state will get restored after disabling and enabling an output,
0498     // even if its maximize state changed in the process
0499 
0500     const auto outputs = kwinApp()->outputBackend()->outputs();
0501 
0502     // Disable the right output
0503     {
0504         OutputConfiguration config;
0505         auto changeSet = config.changeSet(outputs[1]);
0506         changeSet->enabled = false;
0507         workspace()->applyOutputConfiguration(config);
0508     }
0509 
0510     // Create a window.
0511     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0512     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0513     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0514     QVERIFY(window);
0515 
0516     // kwin will send a configure event with the actived state.
0517     QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
0518     QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
0519     QVERIFY(surfaceConfigureRequestedSpy.wait());
0520 
0521     const QRectF originalGeometry = window->moveResizeGeometry();
0522 
0523     // Enable the right output
0524     {
0525         OutputConfiguration config;
0526         auto changeSet = config.changeSet(outputs[1]);
0527         changeSet->enabled = true;
0528         workspace()->applyOutputConfiguration(config);
0529     }
0530 
0531     // Move the window to the right monitor and make it maximized.
0532     QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
0533     window->move(QPointF(1280 + 50, 100));
0534     window->maximize(MaximizeFull);
0535     QVERIFY(surfaceConfigureRequestedSpy.wait());
0536     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), QSize(1280, 1024));
0537     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
0538     Test::render(surface.get(), QSize(1280, 1024), Qt::blue);
0539     QVERIFY(frameGeometryChangedSpy.wait());
0540     QCOMPARE(window->frameGeometry(), QRectF(1280, 0, 1280, 1024));
0541     QCOMPARE(window->moveResizeGeometry(), QRectF(1280, 0, 1280, 1024));
0542     QCOMPARE(window->output(), outputs[1]);
0543     QCOMPARE(window->maximizeMode(), MaximizeFull);
0544     QCOMPARE(window->requestedMaximizeMode(), MaximizeFull);
0545     QCOMPARE(window->geometryRestore(), QRectF(1280 + 50, 100, 100, 50));
0546 
0547     // Disable the right output
0548     {
0549         OutputConfiguration config;
0550         auto changeSet = config.changeSet(outputs[1]);
0551         changeSet->enabled = false;
0552         workspace()->applyOutputConfiguration(config);
0553     }
0554 
0555     // The window will be moved to its prior position on the left monitor and unmaximized
0556     QVERIFY(surfaceConfigureRequestedSpy.wait());
0557     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), originalGeometry.size().toSize());
0558     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
0559     Test::render(surface.get(), originalGeometry.size().toSize(), Qt::blue);
0560     QVERIFY(frameGeometryChangedSpy.wait());
0561     QCOMPARE(window->frameGeometry(), originalGeometry);
0562     QCOMPARE(window->moveResizeGeometry(), originalGeometry);
0563     QCOMPARE(window->output(), outputs[0]);
0564     QCOMPARE(window->maximizeMode(), MaximizeRestore);
0565     QCOMPARE(window->requestedMaximizeMode(), MaximizeRestore);
0566 
0567     // Enable the right output again
0568     {
0569         OutputConfiguration config;
0570         auto changeSet = config.changeSet(outputs[1]);
0571         changeSet->enabled = true;
0572         workspace()->applyOutputConfiguration(config);
0573     }
0574 
0575     // The window will be moved back to the right monitor, maximized and the geometry restore will be updated
0576     QVERIFY(surfaceConfigureRequestedSpy.wait());
0577     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).value<QSize>(), outputs[1]->geometry().size());
0578     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
0579     Test::render(surface.get(), outputs[1]->geometry().size(), Qt::blue);
0580     QVERIFY(frameGeometryChangedSpy.wait());
0581     QCOMPARE(window->frameGeometry(), QRectF(1280, 0, 1280, 1024));
0582     QCOMPARE(window->moveResizeGeometry(), QRectF(1280, 0, 1280, 1024));
0583     QCOMPARE(window->output(), outputs[1]);
0584     QCOMPARE(window->maximizeMode(), MaximizeFull);
0585     QCOMPARE(window->requestedMaximizeMode(), MaximizeFull);
0586     QCOMPARE(window->geometryRestore(), QRectF(1280 + 50, 100, 100, 50));
0587 }
0588 
0589 } // namespace KWin
0590 
0591 WAYLANDTEST_MAIN(KWin::OutputChangesTest)
0592 #include "outputchanges_test.moc"