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