File indexing completed on 2025-03-23 13:48:00
0001 0002 /* 0003 KWin - the KDE window manager 0004 This file is part of the KDE project. 0005 0006 SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org> 0007 0008 SPDX-License-Identifier: GPL-2.0-or-later 0009 */ 0010 #include "kwin_wayland_test.h" 0011 0012 #include "atoms.h" 0013 #include "core/output.h" 0014 #include "core/outputbackend.h" 0015 #include "cursor.h" 0016 #include "deleted.h" 0017 #include "effects.h" 0018 #include "placement.h" 0019 #include "wayland_server.h" 0020 #include "window.h" 0021 #include "workspace.h" 0022 #include "x11window.h" 0023 0024 #include <KWayland/Client/compositor.h> 0025 #include <KWayland/Client/connection_thread.h> 0026 #include <KWayland/Client/plasmashell.h> 0027 #include <KWayland/Client/pointer.h> 0028 #include <KWayland/Client/seat.h> 0029 #include <KWayland/Client/surface.h> 0030 0031 #include <linux/input.h> 0032 #include <xcb/xcb_icccm.h> 0033 0034 Q_DECLARE_METATYPE(KWin::QuickTileMode) 0035 Q_DECLARE_METATYPE(KWin::MaximizeMode) 0036 0037 namespace KWin 0038 { 0039 0040 static const QString s_socketName = QStringLiteral("wayland_test_kwin_quick_tiling-0"); 0041 0042 class MoveResizeWindowTest : public QObject 0043 { 0044 Q_OBJECT 0045 private Q_SLOTS: 0046 void initTestCase(); 0047 void init(); 0048 void cleanup(); 0049 void testMove(); 0050 void testResize(); 0051 void testPackTo_data(); 0052 void testPackTo(); 0053 void testPackAgainstClient_data(); 0054 void testPackAgainstClient(); 0055 void testGrowShrink_data(); 0056 void testGrowShrink(); 0057 void testPointerMoveEnd_data(); 0058 void testPointerMoveEnd(); 0059 void testClientSideMove(); 0060 void testPlasmaShellSurfaceMovable_data(); 0061 void testPlasmaShellSurfaceMovable(); 0062 void testNetMove(); 0063 void testAdjustClientGeometryOfAutohidingX11Panel_data(); 0064 void testAdjustClientGeometryOfAutohidingX11Panel(); 0065 void testAdjustClientGeometryOfAutohidingWaylandPanel_data(); 0066 void testAdjustClientGeometryOfAutohidingWaylandPanel(); 0067 void testResizeForVirtualKeyboard_data(); 0068 void testResizeForVirtualKeyboard(); 0069 void testResizeForVirtualKeyboardWithMaximize(); 0070 void testResizeForVirtualKeyboardWithFullScreen(); 0071 void testDestroyMoveClient(); 0072 void testDestroyResizeClient(); 0073 void testCancelInteractiveMoveResize_data(); 0074 void testCancelInteractiveMoveResize(); 0075 0076 private: 0077 KWayland::Client::ConnectionThread *m_connection = nullptr; 0078 KWayland::Client::Compositor *m_compositor = nullptr; 0079 }; 0080 0081 void MoveResizeWindowTest::initTestCase() 0082 { 0083 qRegisterMetaType<KWin::Window *>(); 0084 qRegisterMetaType<KWin::Deleted *>(); 0085 qRegisterMetaType<KWin::MaximizeMode>("MaximizeMode"); 0086 QSignalSpy applicationStartedSpy(kwinApp(), &Application::started); 0087 QVERIFY(waylandServer()->init(s_socketName)); 0088 QMetaObject::invokeMethod(kwinApp()->outputBackend(), "setVirtualOutputs", Qt::DirectConnection, Q_ARG(QVector<QRect>, QVector<QRect>() << QRect(0, 0, 1280, 1024))); 0089 kwinApp()->start(); 0090 QVERIFY(applicationStartedSpy.wait()); 0091 const auto outputs = workspace()->outputs(); 0092 QCOMPARE(outputs.count(), 1); 0093 QCOMPARE(outputs[0]->geometry(), QRect(0, 0, 1280, 1024)); 0094 } 0095 0096 void MoveResizeWindowTest::init() 0097 { 0098 QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::PlasmaShell | Test::AdditionalWaylandInterface::Seat)); 0099 QVERIFY(Test::waitForWaylandPointer()); 0100 m_connection = Test::waylandConnection(); 0101 m_compositor = Test::waylandCompositor(); 0102 0103 workspace()->setActiveOutput(QPoint(640, 512)); 0104 } 0105 0106 void MoveResizeWindowTest::cleanup() 0107 { 0108 Test::destroyWaylandConnection(); 0109 } 0110 0111 void MoveResizeWindowTest::testMove() 0112 { 0113 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface()); 0114 QVERIFY(surface != nullptr); 0115 0116 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get())); 0117 QVERIFY(shellSurface != nullptr); 0118 // let's render 0119 auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue); 0120 0121 QVERIFY(window); 0122 QCOMPARE(workspace()->activeWindow(), window); 0123 QCOMPARE(window->frameGeometry(), QRect(0, 0, 100, 50)); 0124 QSignalSpy startMoveResizedSpy(window, &Window::clientStartUserMovedResized); 0125 QSignalSpy moveResizedChangedSpy(window, &Window::moveResizedChanged); 0126 QSignalSpy clientStepUserMovedResizedSpy(window, &Window::clientStepUserMovedResized); 0127 QSignalSpy clientFinishUserMovedResizedSpy(window, &Window::clientFinishUserMovedResized); 0128 0129 // effects signal handlers 0130 QSignalSpy windowStartUserMovedResizedSpy(effects, &EffectsHandler::windowStartUserMovedResized); 0131 QSignalSpy windowStepUserMovedResizedSpy(effects, &EffectsHandler::windowStepUserMovedResized); 0132 QSignalSpy windowFinishUserMovedResizedSpy(effects, &EffectsHandler::windowFinishUserMovedResized); 0133 0134 // begin move 0135 QVERIFY(workspace()->moveResizeWindow() == nullptr); 0136 QCOMPARE(window->isInteractiveMove(), false); 0137 workspace()->slotWindowMove(); 0138 QCOMPARE(workspace()->moveResizeWindow(), window); 0139 QCOMPARE(startMoveResizedSpy.count(), 1); 0140 QCOMPARE(moveResizedChangedSpy.count(), 1); 0141 QCOMPARE(windowStartUserMovedResizedSpy.count(), 1); 0142 QCOMPARE(window->isInteractiveMove(), true); 0143 QCOMPARE(window->geometryRestore(), QRect()); 0144 0145 // send some key events, not going through input redirection 0146 const QPoint cursorPos = Cursors::self()->mouse()->pos(); 0147 window->keyPressEvent(Qt::Key_Right); 0148 window->updateInteractiveMoveResize(Cursors::self()->mouse()->pos()); 0149 QCOMPARE(Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 0)); 0150 QEXPECT_FAIL("", "First event is ignored", Continue); 0151 QCOMPARE(clientStepUserMovedResizedSpy.count(), 1); 0152 clientStepUserMovedResizedSpy.clear(); 0153 windowStepUserMovedResizedSpy.clear(); 0154 0155 window->keyPressEvent(Qt::Key_Right); 0156 window->updateInteractiveMoveResize(Cursors::self()->mouse()->pos()); 0157 QCOMPARE(Cursors::self()->mouse()->pos(), cursorPos + QPoint(16, 0)); 0158 QCOMPARE(clientStepUserMovedResizedSpy.count(), 1); 0159 QCOMPARE(windowStepUserMovedResizedSpy.count(), 1); 0160 0161 window->keyPressEvent(Qt::Key_Down | Qt::ALT); 0162 window->updateInteractiveMoveResize(Cursors::self()->mouse()->pos()); 0163 QCOMPARE(clientStepUserMovedResizedSpy.count(), 2); 0164 QCOMPARE(windowStepUserMovedResizedSpy.count(), 2); 0165 QCOMPARE(window->frameGeometry(), QRect(16, 32, 100, 50)); 0166 QCOMPARE(Cursors::self()->mouse()->pos(), cursorPos + QPoint(16, 32)); 0167 0168 // let's end 0169 QCOMPARE(clientFinishUserMovedResizedSpy.count(), 0); 0170 window->keyPressEvent(Qt::Key_Enter); 0171 QCOMPARE(clientFinishUserMovedResizedSpy.count(), 1); 0172 QCOMPARE(moveResizedChangedSpy.count(), 2); 0173 QCOMPARE(windowFinishUserMovedResizedSpy.count(), 1); 0174 QCOMPARE(window->frameGeometry(), QRect(16, 32, 100, 50)); 0175 QCOMPARE(window->isInteractiveMove(), false); 0176 QVERIFY(workspace()->moveResizeWindow() == nullptr); 0177 surface.reset(); 0178 QVERIFY(Test::waitForWindowDestroyed(window)); 0179 } 0180 0181 void MoveResizeWindowTest::testResize() 0182 { 0183 // a test case which manually resizes a window 0184 0185 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface()); 0186 QVERIFY(surface != nullptr); 0187 0188 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly)); 0189 QVERIFY(shellSurface != nullptr); 0190 0191 // Wait for the initial configure event. 0192 Test::XdgToplevel::States states; 0193 QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested); 0194 QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested); 0195 surface->commit(KWayland::Client::Surface::CommitFlag::None); 0196 QVERIFY(surfaceConfigureRequestedSpy.wait()); 0197 QCOMPARE(surfaceConfigureRequestedSpy.count(), 1); 0198 QCOMPARE(toplevelConfigureRequestedSpy.count(), 1); 0199 states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>(); 0200 QVERIFY(!states.testFlag(Test::XdgToplevel::State::Activated)); 0201 QVERIFY(!states.testFlag(Test::XdgToplevel::State::Resizing)); 0202 0203 // Let's render. 0204 shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>()); 0205 auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue); 0206 0207 // We have to receive a configure event when the client becomes active. 0208 QVERIFY(surfaceConfigureRequestedSpy.wait()); 0209 QCOMPARE(surfaceConfigureRequestedSpy.count(), 2); 0210 QCOMPARE(toplevelConfigureRequestedSpy.count(), 2); 0211 states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>(); 0212 QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated)); 0213 QVERIFY(!states.testFlag(Test::XdgToplevel::State::Resizing)); 0214 0215 QVERIFY(window); 0216 QCOMPARE(workspace()->activeWindow(), window); 0217 QCOMPARE(window->frameGeometry(), QRect(0, 0, 100, 50)); 0218 QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged); 0219 QSignalSpy startMoveResizedSpy(window, &Window::clientStartUserMovedResized); 0220 QSignalSpy moveResizedChangedSpy(window, &Window::moveResizedChanged); 0221 QSignalSpy clientStepUserMovedResizedSpy(window, &Window::clientStepUserMovedResized); 0222 QSignalSpy clientFinishUserMovedResizedSpy(window, &Window::clientFinishUserMovedResized); 0223 0224 // begin resize 0225 QCOMPARE(workspace()->moveResizeWindow(), nullptr); 0226 QCOMPARE(window->isInteractiveMove(), false); 0227 QCOMPARE(window->isInteractiveResize(), false); 0228 workspace()->slotWindowResize(); 0229 QCOMPARE(workspace()->moveResizeWindow(), window); 0230 QCOMPARE(startMoveResizedSpy.count(), 1); 0231 QCOMPARE(moveResizedChangedSpy.count(), 1); 0232 QCOMPARE(window->isInteractiveResize(), true); 0233 QCOMPARE(window->geometryRestore(), QRect()); 0234 QVERIFY(surfaceConfigureRequestedSpy.wait()); 0235 QCOMPARE(surfaceConfigureRequestedSpy.count(), 3); 0236 QCOMPARE(toplevelConfigureRequestedSpy.count(), 3); 0237 states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>(); 0238 QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated)); 0239 QVERIFY(states.testFlag(Test::XdgToplevel::State::Resizing)); 0240 0241 // Trigger a change. 0242 const QPoint cursorPos = Cursors::self()->mouse()->pos(); 0243 window->keyPressEvent(Qt::Key_Right); 0244 window->updateInteractiveMoveResize(Cursors::self()->mouse()->pos()); 0245 QCOMPARE(Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 0)); 0246 0247 // The client should receive a configure event with the new size. 0248 QVERIFY(surfaceConfigureRequestedSpy.wait()); 0249 QCOMPARE(surfaceConfigureRequestedSpy.count(), 4); 0250 QCOMPARE(toplevelConfigureRequestedSpy.count(), 4); 0251 states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>(); 0252 QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated)); 0253 QVERIFY(states.testFlag(Test::XdgToplevel::State::Resizing)); 0254 QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(108, 50)); 0255 QCOMPARE(clientStepUserMovedResizedSpy.count(), 1); 0256 0257 // Now render new size. 0258 shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>()); 0259 Test::render(surface.get(), QSize(108, 50), Qt::blue); 0260 QVERIFY(frameGeometryChangedSpy.wait()); 0261 QCOMPARE(window->frameGeometry(), QRect(0, 0, 108, 50)); 0262 QCOMPARE(clientStepUserMovedResizedSpy.count(), 1); 0263 0264 // Go down. 0265 window->keyPressEvent(Qt::Key_Down); 0266 window->updateInteractiveMoveResize(Cursors::self()->mouse()->pos()); 0267 QCOMPARE(Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 8)); 0268 0269 // The client should receive another configure event. 0270 QVERIFY(surfaceConfigureRequestedSpy.wait()); 0271 QCOMPARE(surfaceConfigureRequestedSpy.count(), 5); 0272 QCOMPARE(toplevelConfigureRequestedSpy.count(), 5); 0273 states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>(); 0274 QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated)); 0275 QVERIFY(states.testFlag(Test::XdgToplevel::State::Resizing)); 0276 QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(108, 58)); 0277 0278 // Now render new size. 0279 shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>()); 0280 Test::render(surface.get(), QSize(108, 58), Qt::blue); 0281 QVERIFY(frameGeometryChangedSpy.wait()); 0282 QCOMPARE(window->frameGeometry(), QRect(0, 0, 108, 58)); 0283 QCOMPARE(clientStepUserMovedResizedSpy.count(), 2); 0284 0285 // Let's finalize the resize operation. 0286 QCOMPARE(clientFinishUserMovedResizedSpy.count(), 0); 0287 window->keyPressEvent(Qt::Key_Enter); 0288 QCOMPARE(clientFinishUserMovedResizedSpy.count(), 1); 0289 QCOMPARE(moveResizedChangedSpy.count(), 2); 0290 QCOMPARE(window->isInteractiveResize(), false); 0291 QCOMPARE(workspace()->moveResizeWindow(), nullptr); 0292 QVERIFY(surfaceConfigureRequestedSpy.wait()); 0293 QCOMPARE(surfaceConfigureRequestedSpy.count(), 6); 0294 QCOMPARE(toplevelConfigureRequestedSpy.count(), 6); 0295 states = toplevelConfigureRequestedSpy.last().at(1).value<Test::XdgToplevel::States>(); 0296 QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated)); 0297 QVERIFY(!states.testFlag(Test::XdgToplevel::State::Resizing)); 0298 0299 // Destroy the client. 0300 shellSurface.reset(); 0301 QVERIFY(Test::waitForWindowDestroyed(window)); 0302 } 0303 0304 void MoveResizeWindowTest::testPackTo_data() 0305 { 0306 QTest::addColumn<QString>("methodCall"); 0307 QTest::addColumn<QRectF>("expectedGeometry"); 0308 0309 QTest::newRow("left") << QStringLiteral("slotWindowMoveLeft") << QRectF(0, 487, 100, 50); 0310 QTest::newRow("up") << QStringLiteral("slotWindowMoveUp") << QRectF(590, 0, 100, 50); 0311 QTest::newRow("right") << QStringLiteral("slotWindowMoveRight") << QRectF(1180, 487, 100, 50); 0312 QTest::newRow("down") << QStringLiteral("slotWindowMoveDown") << QRectF(590, 974, 100, 50); 0313 } 0314 0315 void MoveResizeWindowTest::testPackTo() 0316 { 0317 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface()); 0318 QVERIFY(surface != nullptr); 0319 0320 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get())); 0321 QVERIFY(shellSurface != nullptr); 0322 // let's render 0323 auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue); 0324 0325 QVERIFY(window); 0326 QCOMPARE(workspace()->activeWindow(), window); 0327 QCOMPARE(window->frameGeometry(), QRect(0, 0, 100, 50)); 0328 0329 // let's place it centered 0330 workspace()->placement()->placeCentered(window, QRect(0, 0, 1280, 1024)); 0331 QCOMPARE(window->frameGeometry(), QRect(590, 487, 100, 50)); 0332 0333 QFETCH(QString, methodCall); 0334 QMetaObject::invokeMethod(workspace(), methodCall.toLocal8Bit().constData()); 0335 QTEST(window->frameGeometry(), "expectedGeometry"); 0336 surface.reset(); 0337 QVERIFY(Test::waitForWindowDestroyed(window)); 0338 } 0339 0340 void MoveResizeWindowTest::testPackAgainstClient_data() 0341 { 0342 QTest::addColumn<QString>("methodCall"); 0343 QTest::addColumn<QRectF>("expectedGeometry"); 0344 0345 QTest::newRow("left") << QStringLiteral("slotWindowMoveLeft") << QRectF(10, 487, 100, 50); 0346 QTest::newRow("up") << QStringLiteral("slotWindowMoveUp") << QRectF(590, 10, 100, 50); 0347 QTest::newRow("right") << QStringLiteral("slotWindowMoveRight") << QRectF(1170, 487, 100, 50); 0348 QTest::newRow("down") << QStringLiteral("slotWindowMoveDown") << QRectF(590, 964, 100, 50); 0349 } 0350 0351 void MoveResizeWindowTest::testPackAgainstClient() 0352 { 0353 std::unique_ptr<KWayland::Client::Surface> surface1(Test::createSurface()); 0354 QVERIFY(surface1 != nullptr); 0355 std::unique_ptr<KWayland::Client::Surface> surface2(Test::createSurface()); 0356 QVERIFY(surface2 != nullptr); 0357 std::unique_ptr<KWayland::Client::Surface> surface3(Test::createSurface()); 0358 QVERIFY(surface3 != nullptr); 0359 std::unique_ptr<KWayland::Client::Surface> surface4(Test::createSurface()); 0360 QVERIFY(surface4 != nullptr); 0361 0362 std::unique_ptr<Test::XdgToplevel> shellSurface1(Test::createXdgToplevelSurface(surface1.get())); 0363 QVERIFY(shellSurface1 != nullptr); 0364 std::unique_ptr<Test::XdgToplevel> shellSurface2(Test::createXdgToplevelSurface(surface2.get())); 0365 QVERIFY(shellSurface2 != nullptr); 0366 std::unique_ptr<Test::XdgToplevel> shellSurface3(Test::createXdgToplevelSurface(surface3.get())); 0367 QVERIFY(shellSurface3 != nullptr); 0368 std::unique_ptr<Test::XdgToplevel> shellSurface4(Test::createXdgToplevelSurface(surface4.get())); 0369 QVERIFY(shellSurface4 != nullptr); 0370 auto renderWindow = [](KWayland::Client::Surface *surface, const QString &methodCall, const QRect &expectedGeometry) { 0371 // let's render 0372 auto window = Test::renderAndWaitForShown(surface, QSize(10, 10), Qt::blue); 0373 0374 QVERIFY(window); 0375 QCOMPARE(workspace()->activeWindow(), window); 0376 QCOMPARE(window->frameGeometry().size(), QSize(10, 10)); 0377 // let's place it centered 0378 workspace()->placement()->placeCentered(window, QRect(0, 0, 1280, 1024)); 0379 QCOMPARE(window->frameGeometry(), QRect(635, 507, 10, 10)); 0380 QMetaObject::invokeMethod(workspace(), methodCall.toLocal8Bit().constData()); 0381 QCOMPARE(window->frameGeometry(), expectedGeometry); 0382 }; 0383 renderWindow(surface1.get(), QStringLiteral("slotWindowMoveLeft"), QRect(0, 507, 10, 10)); 0384 renderWindow(surface2.get(), QStringLiteral("slotWindowMoveUp"), QRect(635, 0, 10, 10)); 0385 renderWindow(surface3.get(), QStringLiteral("slotWindowMoveRight"), QRect(1270, 507, 10, 10)); 0386 renderWindow(surface4.get(), QStringLiteral("slotWindowMoveDown"), QRect(635, 1014, 10, 10)); 0387 0388 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface()); 0389 QVERIFY(surface != nullptr); 0390 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get())); 0391 QVERIFY(shellSurface != nullptr); 0392 auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue); 0393 0394 QVERIFY(window); 0395 QCOMPARE(workspace()->activeWindow(), window); 0396 // let's place it centered 0397 workspace()->placement()->placeCentered(window, QRect(0, 0, 1280, 1024)); 0398 QCOMPARE(window->frameGeometry(), QRect(590, 487, 100, 50)); 0399 0400 QFETCH(QString, methodCall); 0401 QMetaObject::invokeMethod(workspace(), methodCall.toLocal8Bit().constData()); 0402 QTEST(window->frameGeometry(), "expectedGeometry"); 0403 } 0404 0405 void MoveResizeWindowTest::testGrowShrink_data() 0406 { 0407 QTest::addColumn<QString>("methodCall"); 0408 QTest::addColumn<QRectF>("expectedGeometry"); 0409 0410 QTest::newRow("grow vertical") << QStringLiteral("slotWindowExpandVertical") << QRectF(590, 487, 100, 537); 0411 QTest::newRow("grow horizontal") << QStringLiteral("slotWindowExpandHorizontal") << QRectF(590, 487, 690, 50); 0412 QTest::newRow("shrink vertical") << QStringLiteral("slotWindowShrinkVertical") << QRectF(590, 487, 100, 23); 0413 QTest::newRow("shrink horizontal") << QStringLiteral("slotWindowShrinkHorizontal") << QRectF(590, 487, 40, 50); 0414 } 0415 0416 void MoveResizeWindowTest::testGrowShrink() 0417 { 0418 // block geometry helper 0419 std::unique_ptr<KWayland::Client::Surface> surface1(Test::createSurface()); 0420 QVERIFY(surface1 != nullptr); 0421 std::unique_ptr<Test::XdgToplevel> shellSurface1(Test::createXdgToplevelSurface(surface1.get())); 0422 QVERIFY(shellSurface1 != nullptr); 0423 Test::render(surface1.get(), QSize(650, 514), Qt::blue); 0424 QVERIFY(Test::waitForWaylandWindowShown()); 0425 workspace()->slotWindowMoveRight(); 0426 workspace()->slotWindowMoveDown(); 0427 0428 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface()); 0429 QVERIFY(surface != nullptr); 0430 0431 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get())); 0432 QVERIFY(shellSurface != nullptr); 0433 QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested); 0434 QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested); 0435 // let's render 0436 auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue); 0437 QVERIFY(toplevelConfigureRequestedSpy.wait()); 0438 0439 QVERIFY(window); 0440 QCOMPARE(workspace()->activeWindow(), window); 0441 0442 // let's place it centered 0443 workspace()->placement()->placeCentered(window, QRect(0, 0, 1280, 1024)); 0444 QCOMPARE(window->frameGeometry(), QRect(590, 487, 100, 50)); 0445 0446 QFETCH(QString, methodCall); 0447 QMetaObject::invokeMethod(workspace(), methodCall.toLocal8Bit().constData()); 0448 QVERIFY(surfaceConfigureRequestedSpy.wait()); 0449 Test::render(surface.get(), toplevelConfigureRequestedSpy.last().first().toSize(), Qt::red); 0450 0451 QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged); 0452 m_connection->flush(); 0453 QVERIFY(frameGeometryChangedSpy.wait()); 0454 QTEST(window->frameGeometry(), "expectedGeometry"); 0455 } 0456 0457 void MoveResizeWindowTest::testPointerMoveEnd_data() 0458 { 0459 QTest::addColumn<int>("additionalButton"); 0460 0461 QTest::newRow("BTN_RIGHT") << BTN_RIGHT; 0462 QTest::newRow("BTN_MIDDLE") << BTN_MIDDLE; 0463 QTest::newRow("BTN_SIDE") << BTN_SIDE; 0464 QTest::newRow("BTN_EXTRA") << BTN_EXTRA; 0465 QTest::newRow("BTN_FORWARD") << BTN_FORWARD; 0466 QTest::newRow("BTN_BACK") << BTN_BACK; 0467 QTest::newRow("BTN_TASK") << BTN_TASK; 0468 for (int i = BTN_TASK + 1; i < BTN_JOYSTICK; i++) { 0469 QTest::newRow(QByteArray::number(i, 16).constData()) << i; 0470 } 0471 } 0472 0473 void MoveResizeWindowTest::testPointerMoveEnd() 0474 { 0475 // this test verifies that moving a window through pointer only ends if all buttons are released 0476 0477 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface()); 0478 QVERIFY(surface != nullptr); 0479 0480 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get())); 0481 QVERIFY(shellSurface != nullptr); 0482 // let's render 0483 auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue); 0484 0485 QVERIFY(window); 0486 QCOMPARE(window, workspace()->activeWindow()); 0487 QVERIFY(!window->isInteractiveMove()); 0488 0489 // let's trigger the left button 0490 quint32 timestamp = 1; 0491 Test::pointerButtonPressed(BTN_LEFT, timestamp++); 0492 QVERIFY(!window->isInteractiveMove()); 0493 workspace()->slotWindowMove(); 0494 QVERIFY(window->isInteractiveMove()); 0495 0496 // let's press another button 0497 QFETCH(int, additionalButton); 0498 Test::pointerButtonPressed(additionalButton, timestamp++); 0499 QVERIFY(window->isInteractiveMove()); 0500 0501 // release the left button, should still have the window moving 0502 Test::pointerButtonReleased(BTN_LEFT, timestamp++); 0503 QVERIFY(window->isInteractiveMove()); 0504 0505 // but releasing the other button should now end moving 0506 Test::pointerButtonReleased(additionalButton, timestamp++); 0507 QVERIFY(!window->isInteractiveMove()); 0508 surface.reset(); 0509 QVERIFY(Test::waitForWindowDestroyed(window)); 0510 } 0511 void MoveResizeWindowTest::testClientSideMove() 0512 { 0513 Cursors::self()->mouse()->setPos(640, 512); 0514 std::unique_ptr<KWayland::Client::Pointer> pointer(Test::waylandSeat()->createPointer()); 0515 QSignalSpy pointerEnteredSpy(pointer.get(), &KWayland::Client::Pointer::entered); 0516 QSignalSpy pointerLeftSpy(pointer.get(), &KWayland::Client::Pointer::left); 0517 QSignalSpy buttonSpy(pointer.get(), &KWayland::Client::Pointer::buttonStateChanged); 0518 0519 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface()); 0520 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get())); 0521 auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue); 0522 QVERIFY(window); 0523 0524 // move pointer into center of geometry 0525 const QRectF startGeometry = window->frameGeometry(); 0526 Cursors::self()->mouse()->setPos(startGeometry.center()); 0527 QVERIFY(pointerEnteredSpy.wait()); 0528 QCOMPARE(pointerEnteredSpy.first().last().toPoint(), QPoint(50, 25)); 0529 // simulate press 0530 quint32 timestamp = 1; 0531 Test::pointerButtonPressed(BTN_LEFT, timestamp++); 0532 QVERIFY(buttonSpy.wait()); 0533 QSignalSpy moveStartSpy(window, &Window::clientStartUserMovedResized); 0534 shellSurface->move(*Test::waylandSeat(), buttonSpy.first().first().value<quint32>()); 0535 QVERIFY(moveStartSpy.wait()); 0536 QCOMPARE(window->isInteractiveMove(), true); 0537 QVERIFY(pointerLeftSpy.wait()); 0538 0539 // move a bit 0540 QSignalSpy clientMoveStepSpy(window, &Window::clientStepUserMovedResized); 0541 const QPointF startPoint = startGeometry.center(); 0542 const int dragDistance = QApplication::startDragDistance(); 0543 // Why? 0544 Test::pointerMotion(startPoint + QPoint(dragDistance, dragDistance) + QPoint(6, 6), timestamp++); 0545 QCOMPARE(clientMoveStepSpy.count(), 1); 0546 0547 // and release again 0548 Test::pointerButtonReleased(BTN_LEFT, timestamp++); 0549 QVERIFY(pointerEnteredSpy.wait()); 0550 QCOMPARE(window->isInteractiveMove(), false); 0551 QCOMPARE(window->frameGeometry(), startGeometry.translated(QPoint(dragDistance, dragDistance) + QPoint(6, 6))); 0552 QCOMPARE(pointerEnteredSpy.last().last().toPoint(), QPoint(50, 25)); 0553 } 0554 0555 void MoveResizeWindowTest::testPlasmaShellSurfaceMovable_data() 0556 { 0557 QTest::addColumn<KWayland::Client::PlasmaShellSurface::Role>("role"); 0558 QTest::addColumn<bool>("movable"); 0559 QTest::addColumn<bool>("movableAcrossScreens"); 0560 QTest::addColumn<bool>("resizable"); 0561 0562 QTest::newRow("normal") << KWayland::Client::PlasmaShellSurface::Role::Normal << true << true << true; 0563 QTest::newRow("desktop") << KWayland::Client::PlasmaShellSurface::Role::Desktop << false << false << false; 0564 QTest::newRow("panel") << KWayland::Client::PlasmaShellSurface::Role::Panel << false << false << false; 0565 QTest::newRow("osd") << KWayland::Client::PlasmaShellSurface::Role::OnScreenDisplay << false << false << false; 0566 } 0567 0568 void MoveResizeWindowTest::testPlasmaShellSurfaceMovable() 0569 { 0570 // this test verifies that certain window types from PlasmaShellSurface are not moveable or resizable 0571 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface()); 0572 QVERIFY(surface != nullptr); 0573 0574 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get())); 0575 QVERIFY(shellSurface != nullptr); 0576 // and a PlasmaShellSurface 0577 std::unique_ptr<KWayland::Client::PlasmaShellSurface> plasmaSurface(Test::waylandPlasmaShell()->createSurface(surface.get())); 0578 QVERIFY(plasmaSurface != nullptr); 0579 QFETCH(KWayland::Client::PlasmaShellSurface::Role, role); 0580 plasmaSurface->setRole(role); 0581 // let's render 0582 auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue); 0583 0584 QVERIFY(window); 0585 QTEST(window->isMovable(), "movable"); 0586 QTEST(window->isMovableAcrossScreens(), "movableAcrossScreens"); 0587 QTEST(window->isResizable(), "resizable"); 0588 surface.reset(); 0589 QVERIFY(Test::waitForWindowDestroyed(window)); 0590 } 0591 0592 struct XcbConnectionDeleter 0593 { 0594 void operator()(xcb_connection_t *pointer) 0595 { 0596 xcb_disconnect(pointer); 0597 } 0598 }; 0599 0600 void MoveResizeWindowTest::testNetMove() 0601 { 0602 // this test verifies that a move request for an X11 window through NET API works 0603 // create an xcb window 0604 std::unique_ptr<xcb_connection_t, XcbConnectionDeleter> c(xcb_connect(nullptr, nullptr)); 0605 QVERIFY(!xcb_connection_has_error(c.get())); 0606 0607 xcb_window_t windowId = xcb_generate_id(c.get()); 0608 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(), 0609 0, 0, 100, 100, 0610 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr); 0611 xcb_size_hints_t hints; 0612 memset(&hints, 0, sizeof(hints)); 0613 xcb_icccm_size_hints_set_position(&hints, 1, 0, 0); 0614 xcb_icccm_size_hints_set_size(&hints, 1, 100, 100); 0615 xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints); 0616 // let's set a no-border 0617 NETWinInfo winInfo(c.get(), windowId, rootWindow(), NET::WMWindowType, NET::Properties2()); 0618 winInfo.setWindowType(NET::Override); 0619 xcb_map_window(c.get(), windowId); 0620 xcb_flush(c.get()); 0621 0622 QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded); 0623 QVERIFY(windowCreatedSpy.wait()); 0624 X11Window *window = windowCreatedSpy.first().first().value<X11Window *>(); 0625 QVERIFY(window); 0626 QCOMPARE(window->window(), windowId); 0627 const QRectF origGeo = window->frameGeometry(); 0628 0629 // let's move the cursor outside the window 0630 Cursors::self()->mouse()->setPos(workspace()->activeOutput()->geometry().center()); 0631 QVERIFY(!origGeo.contains(Cursors::self()->mouse()->pos())); 0632 0633 QSignalSpy moveStartSpy(window, &X11Window::clientStartUserMovedResized); 0634 QSignalSpy moveEndSpy(window, &X11Window::clientFinishUserMovedResized); 0635 QSignalSpy moveStepSpy(window, &X11Window::clientStepUserMovedResized); 0636 QVERIFY(!workspace()->moveResizeWindow()); 0637 0638 // use NETRootInfo to trigger a move request 0639 NETRootInfo root(c.get(), NET::Properties()); 0640 root.moveResizeRequest(windowId, origGeo.center().x(), origGeo.center().y(), NET::Move); 0641 xcb_flush(c.get()); 0642 0643 QVERIFY(moveStartSpy.wait()); 0644 QCOMPARE(workspace()->moveResizeWindow(), window); 0645 QVERIFY(window->isInteractiveMove()); 0646 QCOMPARE(window->geometryRestore(), origGeo); 0647 QCOMPARE(Cursors::self()->mouse()->pos(), origGeo.center()); 0648 0649 // let's move a step 0650 Cursors::self()->mouse()->setPos(Cursors::self()->mouse()->pos() + QPoint(10, 10)); 0651 QCOMPARE(moveStepSpy.count(), 1); 0652 QCOMPARE(moveStepSpy.first().last(), origGeo.translated(10, 10)); 0653 0654 // let's cancel the move resize again through the net API 0655 root.moveResizeRequest(windowId, window->frameGeometry().center().x(), window->frameGeometry().center().y(), NET::MoveResizeCancel); 0656 xcb_flush(c.get()); 0657 QVERIFY(moveEndSpy.wait()); 0658 0659 // and destroy the window again 0660 xcb_unmap_window(c.get(), windowId); 0661 xcb_destroy_window(c.get(), windowId); 0662 xcb_flush(c.get()); 0663 c.reset(); 0664 0665 QSignalSpy windowClosedSpy(window, &X11Window::windowClosed); 0666 QVERIFY(windowClosedSpy.wait()); 0667 } 0668 0669 void MoveResizeWindowTest::testAdjustClientGeometryOfAutohidingX11Panel_data() 0670 { 0671 QTest::addColumn<QRect>("panelGeometry"); 0672 QTest::addColumn<QPoint>("targetPoint"); 0673 QTest::addColumn<QPoint>("expectedAdjustedPoint"); 0674 QTest::addColumn<quint32>("hideLocation"); 0675 0676 QTest::newRow("top") << QRect(0, 0, 100, 20) << QPoint(50, 25) << QPoint(50, 20) << 0u; 0677 QTest::newRow("bottom") << QRect(0, 1024 - 20, 100, 20) << QPoint(50, 1024 - 25 - 50) << QPoint(50, 1024 - 20 - 50) << 2u; 0678 QTest::newRow("left") << QRect(0, 0, 20, 100) << QPoint(25, 50) << QPoint(20, 50) << 3u; 0679 QTest::newRow("right") << QRect(1280 - 20, 0, 20, 100) << QPoint(1280 - 25 - 100, 50) << QPoint(1280 - 20 - 100, 50) << 1u; 0680 } 0681 0682 void MoveResizeWindowTest::testAdjustClientGeometryOfAutohidingX11Panel() 0683 { 0684 // this test verifies that auto hiding panels are ignored when adjusting client geometry 0685 // see BUG 365892 0686 0687 // first create our panel 0688 std::unique_ptr<xcb_connection_t, XcbConnectionDeleter> c(xcb_connect(nullptr, nullptr)); 0689 QVERIFY(!xcb_connection_has_error(c.get())); 0690 0691 xcb_window_t windowId = xcb_generate_id(c.get()); 0692 QFETCH(QRect, panelGeometry); 0693 xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(), 0694 panelGeometry.x(), panelGeometry.y(), panelGeometry.width(), panelGeometry.height(), 0695 0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr); 0696 xcb_size_hints_t hints; 0697 memset(&hints, 0, sizeof(hints)); 0698 xcb_icccm_size_hints_set_position(&hints, 1, panelGeometry.x(), panelGeometry.y()); 0699 xcb_icccm_size_hints_set_size(&hints, 1, panelGeometry.width(), panelGeometry.height()); 0700 xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints); 0701 NETWinInfo winInfo(c.get(), windowId, rootWindow(), NET::WMWindowType, NET::Properties2()); 0702 winInfo.setWindowType(NET::Dock); 0703 xcb_map_window(c.get(), windowId); 0704 xcb_flush(c.get()); 0705 0706 QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded); 0707 QVERIFY(windowCreatedSpy.wait()); 0708 X11Window *panel = windowCreatedSpy.first().first().value<X11Window *>(); 0709 QVERIFY(panel); 0710 QCOMPARE(panel->window(), windowId); 0711 QCOMPARE(panel->frameGeometry(), panelGeometry); 0712 QVERIFY(panel->isDock()); 0713 0714 // let's create a window 0715 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface()); 0716 QVERIFY(surface != nullptr); 0717 0718 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get())); 0719 QVERIFY(shellSurface != nullptr); 0720 auto testWindow = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue); 0721 0722 QVERIFY(testWindow); 0723 QVERIFY(testWindow->isMovable()); 0724 // panel is not yet hidden, we should snap against it 0725 QFETCH(QPoint, targetPoint); 0726 QTEST(Workspace::self()->adjustWindowPosition(testWindow, targetPoint, false).toPoint(), "expectedAdjustedPoint"); 0727 0728 // now let's hide the panel 0729 QSignalSpy panelHiddenSpy(panel, &Window::windowHidden); 0730 QFETCH(quint32, hideLocation); 0731 xcb_change_property(c.get(), XCB_PROP_MODE_REPLACE, windowId, atoms->kde_screen_edge_show, XCB_ATOM_CARDINAL, 32, 1, &hideLocation); 0732 xcb_flush(c.get()); 0733 QVERIFY(panelHiddenSpy.wait()); 0734 0735 // now try to snap again 0736 QCOMPARE(Workspace::self()->adjustWindowPosition(testWindow, targetPoint, false), targetPoint); 0737 0738 // and destroy the panel again 0739 xcb_unmap_window(c.get(), windowId); 0740 xcb_destroy_window(c.get(), windowId); 0741 xcb_flush(c.get()); 0742 c.reset(); 0743 0744 QSignalSpy panelClosedSpy(panel, &X11Window::windowClosed); 0745 QVERIFY(panelClosedSpy.wait()); 0746 0747 // snap once more 0748 QCOMPARE(Workspace::self()->adjustWindowPosition(testWindow, targetPoint, false), targetPoint); 0749 0750 // and close 0751 QSignalSpy windowClosedSpy(testWindow, &Window::windowClosed); 0752 shellSurface.reset(); 0753 surface.reset(); 0754 QVERIFY(windowClosedSpy.wait()); 0755 } 0756 0757 void MoveResizeWindowTest::testAdjustClientGeometryOfAutohidingWaylandPanel_data() 0758 { 0759 QTest::addColumn<QRect>("panelGeometry"); 0760 QTest::addColumn<QPoint>("targetPoint"); 0761 QTest::addColumn<QPoint>("expectedAdjustedPoint"); 0762 0763 QTest::newRow("top") << QRect(0, 0, 100, 20) << QPoint(50, 25) << QPoint(50, 20); 0764 QTest::newRow("bottom") << QRect(0, 1024 - 20, 100, 20) << QPoint(50, 1024 - 25 - 50) << QPoint(50, 1024 - 20 - 50); 0765 QTest::newRow("left") << QRect(0, 0, 20, 100) << QPoint(25, 50) << QPoint(20, 50); 0766 QTest::newRow("right") << QRect(1280 - 20, 0, 20, 100) << QPoint(1280 - 25 - 100, 50) << QPoint(1280 - 20 - 100, 50); 0767 } 0768 0769 void MoveResizeWindowTest::testAdjustClientGeometryOfAutohidingWaylandPanel() 0770 { 0771 // this test verifies that auto hiding panels are ignored when adjusting client geometry 0772 // see BUG 365892 0773 0774 // first create our panel 0775 std::unique_ptr<KWayland::Client::Surface> panelSurface(Test::createSurface()); 0776 QVERIFY(panelSurface != nullptr); 0777 std::unique_ptr<Test::XdgToplevel> panelShellSurface(Test::createXdgToplevelSurface(panelSurface.get())); 0778 QVERIFY(panelShellSurface != nullptr); 0779 std::unique_ptr<KWayland::Client::PlasmaShellSurface> plasmaSurface(Test::waylandPlasmaShell()->createSurface(panelSurface.get())); 0780 QVERIFY(plasmaSurface != nullptr); 0781 plasmaSurface->setRole(KWayland::Client::PlasmaShellSurface::Role::Panel); 0782 plasmaSurface->setPanelBehavior(KWayland::Client::PlasmaShellSurface::PanelBehavior::AutoHide); 0783 QFETCH(QRect, panelGeometry); 0784 plasmaSurface->setPosition(panelGeometry.topLeft()); 0785 // let's render 0786 auto panel = Test::renderAndWaitForShown(panelSurface.get(), panelGeometry.size(), Qt::blue); 0787 QVERIFY(panel); 0788 QCOMPARE(panel->frameGeometry(), panelGeometry); 0789 QVERIFY(panel->isDock()); 0790 0791 // let's create a window 0792 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface()); 0793 QVERIFY(surface != nullptr); 0794 0795 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get())); 0796 QVERIFY(shellSurface != nullptr); 0797 auto testWindow = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue); 0798 0799 QVERIFY(testWindow); 0800 QVERIFY(testWindow->isMovable()); 0801 // panel is not yet hidden, we should snap against it 0802 QFETCH(QPoint, targetPoint); 0803 QTEST(Workspace::self()->adjustWindowPosition(testWindow, targetPoint, false).toPoint(), "expectedAdjustedPoint"); 0804 0805 // now let's hide the panel 0806 QSignalSpy panelHiddenSpy(panel, &Window::windowHidden); 0807 plasmaSurface->requestHideAutoHidingPanel(); 0808 QVERIFY(panelHiddenSpy.wait()); 0809 0810 // now try to snap again 0811 QCOMPARE(Workspace::self()->adjustWindowPosition(testWindow, targetPoint, false), targetPoint); 0812 0813 // and destroy the panel again 0814 QSignalSpy panelClosedSpy(panel, &Window::windowClosed); 0815 plasmaSurface.reset(); 0816 panelShellSurface.reset(); 0817 panelSurface.reset(); 0818 QVERIFY(panelClosedSpy.wait()); 0819 0820 // snap once more 0821 QCOMPARE(Workspace::self()->adjustWindowPosition(testWindow, targetPoint, false), targetPoint); 0822 0823 // and close 0824 QSignalSpy windowClosedSpy(testWindow, &Window::windowClosed); 0825 shellSurface.reset(); 0826 surface.reset(); 0827 QVERIFY(windowClosedSpy.wait()); 0828 } 0829 0830 void MoveResizeWindowTest::testResizeForVirtualKeyboard_data() 0831 { 0832 QTest::addColumn<QRect>("windowRect"); 0833 QTest::addColumn<QRect>("keyboardRect"); 0834 QTest::addColumn<QRect>("resizedWindowRect"); 0835 0836 QTest::newRow("standard") << QRect(100, 300, 500, 800) << QRect(0, 100, 1280, 500) << QRect(100, 0, 500, 100); 0837 QTest::newRow("same size") << QRect(100, 300, 500, 500) << QRect(0, 600, 1280, 400) << QRect(100, 100, 500, 500); 0838 QTest::newRow("smaller width") << QRect(100, 300, 500, 800) << QRect(300, 100, 100, 500) << QRect(100, 0, 500, 100); 0839 QTest::newRow("no height change") << QRect(100, 300, 500, 500) << QRect(0, 900, 1280, 124) << QRect(100, 300, 500, 500); 0840 QTest::newRow("no width change") << QRect(100, 300, 500, 500) << QRect(0, 400, 100, 500) << QRect(100, 300, 500, 500); 0841 } 0842 0843 void MoveResizeWindowTest::testResizeForVirtualKeyboard() 0844 { 0845 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface()); 0846 QVERIFY(surface != nullptr); 0847 0848 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get())); 0849 QVERIFY(shellSurface != nullptr); 0850 0851 QFETCH(QRect, windowRect); 0852 QFETCH(QRect, keyboardRect); 0853 QFETCH(QRect, resizedWindowRect); 0854 0855 // There are three things that may happen when the virtual keyboard geometry 0856 // is set: We move the window to the top and resize it, we move the window 0857 // but don't change its size (if the window is already small enough) or we 0858 // do not change anything because the virtual keyboard does not overlap the 0859 // window. We should verify that, for the first, we get both a position and 0860 // a size change, for the second we only get a position change and for the 0861 // last we get no changes. 0862 bool sizeChange = windowRect.size() != resizedWindowRect.size(); 0863 bool positionChange = windowRect.topLeft() != resizedWindowRect.topLeft(); 0864 0865 // let's render 0866 auto window = Test::renderAndWaitForShown(surface.get(), windowRect.size(), Qt::blue); 0867 QVERIFY(window); 0868 0869 // The client should receive a configure event upon becoming active. 0870 QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested); 0871 QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested); 0872 QVERIFY(surfaceConfigureRequestedSpy.wait()); 0873 surfaceConfigureRequestedSpy.clear(); 0874 0875 window->move(windowRect.topLeft()); 0876 QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged); 0877 0878 QCOMPARE(window->frameGeometry(), windowRect); 0879 window->setVirtualKeyboardGeometry(keyboardRect); 0880 0881 if (sizeChange) { 0882 QVERIFY(surfaceConfigureRequestedSpy.wait()); 0883 shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last()[0].toInt()); 0884 } else { 0885 QVERIFY(surfaceConfigureRequestedSpy.count() == 0); 0886 } 0887 // render at the new size 0888 Test::render(surface.get(), toplevelConfigureRequestedSpy.last().first().toSize(), Qt::blue); 0889 0890 if (positionChange || sizeChange) { 0891 QVERIFY(frameGeometryChangedSpy.count() > 0 || frameGeometryChangedSpy.wait()); 0892 frameGeometryChangedSpy.clear(); 0893 } else { 0894 QVERIFY(frameGeometryChangedSpy.count() == 0); 0895 } 0896 0897 QCOMPARE(window->frameGeometry(), resizedWindowRect); 0898 window->setVirtualKeyboardGeometry(QRect()); 0899 0900 if (sizeChange) { 0901 QVERIFY(surfaceConfigureRequestedSpy.wait()); 0902 shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last()[0].toInt()); 0903 } 0904 // render at the new size 0905 Test::render(surface.get(), toplevelConfigureRequestedSpy.last().first().toSize(), Qt::blue); 0906 0907 if (positionChange || sizeChange) { 0908 QVERIFY(frameGeometryChangedSpy.count() > 0 || frameGeometryChangedSpy.wait()); 0909 } else { 0910 QVERIFY(frameGeometryChangedSpy.count() == 0); 0911 } 0912 0913 QCOMPARE(window->frameGeometry(), windowRect); 0914 } 0915 0916 void MoveResizeWindowTest::testResizeForVirtualKeyboardWithMaximize() 0917 { 0918 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface()); 0919 QVERIFY(surface != nullptr); 0920 0921 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get())); 0922 QVERIFY(shellSurface != nullptr); 0923 0924 // let's render 0925 auto window = Test::renderAndWaitForShown(surface.get(), QSize(500, 800), Qt::blue); 0926 QVERIFY(window); 0927 0928 // The client should receive a configure event upon becoming active. 0929 QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested); 0930 QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested); 0931 QVERIFY(surfaceConfigureRequestedSpy.wait()); 0932 0933 window->move(QPoint(100, 300)); 0934 QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged); 0935 0936 QCOMPARE(window->frameGeometry(), QRect(100, 300, 500, 800)); 0937 window->setVirtualKeyboardGeometry(QRect(0, 100, 1280, 500)); 0938 QVERIFY(surfaceConfigureRequestedSpy.wait()); 0939 0940 shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last()[0].toInt()); 0941 // render at the new size 0942 Test::render(surface.get(), toplevelConfigureRequestedSpy.last().first().toSize(), Qt::blue); 0943 QVERIFY(frameGeometryChangedSpy.wait()); 0944 QCOMPARE(window->frameGeometry(), QRect(100, 0, 500, 100)); 0945 0946 window->setMaximize(true, true); 0947 QVERIFY(surfaceConfigureRequestedSpy.wait()); 0948 shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last()[0].toInt()); 0949 Test::render(surface.get(), toplevelConfigureRequestedSpy.last().first().toSize(), Qt::blue); 0950 QVERIFY(frameGeometryChangedSpy.wait()); 0951 QCOMPARE(window->frameGeometry(), QRect(0, 0, 1280, 1024)); 0952 0953 window->setVirtualKeyboardGeometry(QRect()); 0954 QVERIFY(!surfaceConfigureRequestedSpy.wait(10)); 0955 0956 // render at the size of the configureRequested.. it won't have changed 0957 Test::render(surface.get(), toplevelConfigureRequestedSpy.last().first().toSize(), Qt::blue); 0958 QVERIFY(!frameGeometryChangedSpy.wait(10)); 0959 0960 // Size will NOT be restored 0961 QCOMPARE(window->frameGeometry(), QRect(0, 0, 1280, 1024)); 0962 } 0963 0964 void MoveResizeWindowTest::testResizeForVirtualKeyboardWithFullScreen() 0965 { 0966 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface()); 0967 QVERIFY(surface != nullptr); 0968 0969 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get())); 0970 QVERIFY(shellSurface != nullptr); 0971 0972 // let's render 0973 auto window = Test::renderAndWaitForShown(surface.get(), QSize(500, 800), Qt::blue); 0974 QVERIFY(window); 0975 0976 // The client should receive a configure event upon becoming active. 0977 QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested); 0978 QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested); 0979 QVERIFY(surfaceConfigureRequestedSpy.wait()); 0980 0981 window->move(QPoint(100, 300)); 0982 QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged); 0983 0984 QCOMPARE(window->frameGeometry(), QRect(100, 300, 500, 800)); 0985 window->setVirtualKeyboardGeometry(QRect(0, 100, 1280, 500)); 0986 QVERIFY(surfaceConfigureRequestedSpy.wait()); 0987 0988 shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last()[0].toInt()); 0989 // render at the new size 0990 Test::render(surface.get(), toplevelConfigureRequestedSpy.last().first().toSize(), Qt::blue); 0991 QVERIFY(frameGeometryChangedSpy.wait()); 0992 QCOMPARE(window->frameGeometry(), QRect(100, 0, 500, 100)); 0993 0994 window->setFullScreen(true, true); 0995 QVERIFY(surfaceConfigureRequestedSpy.wait()); 0996 shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last()[0].toInt()); 0997 Test::render(surface.get(), toplevelConfigureRequestedSpy.last().first().toSize(), Qt::blue); 0998 QVERIFY(frameGeometryChangedSpy.wait()); 0999 QCOMPARE(window->frameGeometry(), QRect(0, 0, 1280, 1024)); 1000 1001 window->setVirtualKeyboardGeometry(QRect()); 1002 QVERIFY(!surfaceConfigureRequestedSpy.wait(10)); 1003 1004 // render at the size of the configureRequested.. it won't have changed 1005 Test::render(surface.get(), toplevelConfigureRequestedSpy.last().first().toSize(), Qt::blue); 1006 QVERIFY(!frameGeometryChangedSpy.wait(10)); 1007 // Size will NOT be restored 1008 QCOMPARE(window->frameGeometry(), QRect(0, 0, 1280, 1024)); 1009 } 1010 1011 void MoveResizeWindowTest::testDestroyMoveClient() 1012 { 1013 // This test verifies that active move operation gets finished when 1014 // the associated client is destroyed. 1015 1016 // Create the test client. 1017 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface()); 1018 QVERIFY(surface != nullptr); 1019 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get())); 1020 QVERIFY(shellSurface != nullptr); 1021 Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue); 1022 QVERIFY(window); 1023 1024 // Start moving the client. 1025 QSignalSpy clientStartMoveResizedSpy(window, &Window::clientStartUserMovedResized); 1026 QSignalSpy clientFinishUserMovedResizedSpy(window, &Window::clientFinishUserMovedResized); 1027 1028 QCOMPARE(workspace()->moveResizeWindow(), nullptr); 1029 QCOMPARE(window->isInteractiveMove(), false); 1030 QCOMPARE(window->isInteractiveResize(), false); 1031 workspace()->slotWindowMove(); 1032 QCOMPARE(clientStartMoveResizedSpy.count(), 1); 1033 QCOMPARE(workspace()->moveResizeWindow(), window); 1034 QCOMPARE(window->isInteractiveMove(), true); 1035 QCOMPARE(window->isInteractiveResize(), false); 1036 1037 // Let's pretend that the client crashed. 1038 shellSurface.reset(); 1039 surface.reset(); 1040 QVERIFY(Test::waitForWindowDestroyed(window)); 1041 QCOMPARE(clientFinishUserMovedResizedSpy.count(), 1); 1042 QCOMPARE(workspace()->moveResizeWindow(), nullptr); 1043 } 1044 1045 void MoveResizeWindowTest::testDestroyResizeClient() 1046 { 1047 // This test verifies that active resize operation gets finished when 1048 // the associated client is destroyed. 1049 1050 // Create the test client. 1051 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface()); 1052 QVERIFY(surface != nullptr); 1053 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get())); 1054 QVERIFY(shellSurface != nullptr); 1055 Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue); 1056 QVERIFY(window); 1057 1058 // Start resizing the client. 1059 QSignalSpy clientStartMoveResizedSpy(window, &Window::clientStartUserMovedResized); 1060 QSignalSpy clientFinishUserMovedResizedSpy(window, &Window::clientFinishUserMovedResized); 1061 1062 QCOMPARE(workspace()->moveResizeWindow(), nullptr); 1063 QCOMPARE(window->isInteractiveMove(), false); 1064 QCOMPARE(window->isInteractiveResize(), false); 1065 workspace()->slotWindowResize(); 1066 QCOMPARE(clientStartMoveResizedSpy.count(), 1); 1067 QCOMPARE(workspace()->moveResizeWindow(), window); 1068 QCOMPARE(window->isInteractiveMove(), false); 1069 QCOMPARE(window->isInteractiveResize(), true); 1070 1071 // Let's pretend that the client crashed. 1072 shellSurface.reset(); 1073 surface.reset(); 1074 QVERIFY(Test::waitForWindowDestroyed(window)); 1075 QCOMPARE(clientFinishUserMovedResizedSpy.count(), 1); 1076 QCOMPARE(workspace()->moveResizeWindow(), nullptr); 1077 } 1078 1079 void MoveResizeWindowTest::testCancelInteractiveMoveResize_data() 1080 { 1081 QTest::addColumn<QuickTileMode>("quickTileMode"); 1082 QTest::addColumn<MaximizeMode>("maximizeMode"); 1083 1084 QTest::newRow("quicktile_bottom") << QuickTileMode(QuickTileFlag::Bottom) << MaximizeMode::MaximizeRestore; 1085 QTest::newRow("quicktile_top") << QuickTileMode(QuickTileFlag::Top) << MaximizeMode::MaximizeRestore; 1086 QTest::newRow("quicktile_left") << QuickTileMode(QuickTileFlag::Left) << MaximizeMode::MaximizeRestore; 1087 QTest::newRow("quicktile_right") << QuickTileMode(QuickTileFlag::Right) << MaximizeMode::MaximizeRestore; 1088 QTest::newRow("maximize_vertical") << QuickTileMode(QuickTileFlag::None) << MaximizeMode::MaximizeVertical; 1089 QTest::newRow("maximize_horizontal") << QuickTileMode(QuickTileFlag::None) << MaximizeMode::MaximizeHorizontal; 1090 QTest::newRow("maximize_full") << QuickTileMode(QuickTileFlag::Maximize) << MaximizeMode::MaximizeFull; 1091 } 1092 1093 void MoveResizeWindowTest::testCancelInteractiveMoveResize() 1094 { 1095 // This test verifies that after moveresize is cancelled, all relevant window states are restored 1096 // to what they were before moveresize began 1097 1098 // Create the test client. 1099 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface()); 1100 QVERIFY(surface != nullptr); 1101 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get())); 1102 QVERIFY(shellSurface != nullptr); 1103 Window *window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue); 1104 QVERIFY(window); 1105 1106 // tile / maximize window 1107 QFETCH(QuickTileMode, quickTileMode); 1108 QFETCH(MaximizeMode, maximizeMode); 1109 if (maximizeMode) { 1110 window->setMaximize(maximizeMode & MaximizeMode::MaximizeVertical, maximizeMode & MaximizeMode::MaximizeHorizontal); 1111 } else { 1112 window->setQuickTileMode(quickTileMode, true); 1113 } 1114 QCOMPARE(window->quickTileMode(), quickTileMode); 1115 QCOMPARE(window->requestedMaximizeMode(), maximizeMode); 1116 1117 QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested); 1118 QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested); 1119 QVERIFY(surfaceConfigureRequestedSpy.wait()); 1120 Test::render(surface.get(), toplevelConfigureRequestedSpy.last().first().toSize(), Qt::blue); 1121 1122 const QRectF geometry = window->moveResizeGeometry(); 1123 const QRectF geometryRestore = window->geometryRestore(); 1124 1125 // Start resizing the client. 1126 QSignalSpy clientStartMoveResizedSpy(window, &Window::clientStartUserMovedResized); 1127 QSignalSpy clientFinishUserMovedResizedSpy(window, &Window::clientFinishUserMovedResized); 1128 1129 QCOMPARE(workspace()->moveResizeWindow(), nullptr); 1130 QCOMPARE(window->isInteractiveMove(), false); 1131 QCOMPARE(window->isInteractiveResize(), false); 1132 workspace()->slotWindowResize(); 1133 QCOMPARE(clientStartMoveResizedSpy.count(), 1); 1134 QCOMPARE(workspace()->moveResizeWindow(), window); 1135 QCOMPARE(window->isInteractiveMove(), false); 1136 QCOMPARE(window->isInteractiveResize(), true); 1137 1138 Test::pointerMotionRelative(QPoint(1, 1), 1); 1139 QCOMPARE(window->quickTileMode(), QuickTileMode()); 1140 QCOMPARE(window->requestedMaximizeMode(), MaximizeMode::MaximizeRestore); 1141 1142 // cancel moveresize, all state from before should be restored 1143 window->keyPressEvent(Qt::Key::Key_Escape); 1144 QCOMPARE(window->moveResizeGeometry(), geometry); 1145 QCOMPARE(window->quickTileMode(), quickTileMode); 1146 QCOMPARE(window->requestedMaximizeMode(), maximizeMode); 1147 QCOMPARE(window->geometryRestore(), geometryRestore); 1148 } 1149 } 1150 1151 WAYLANDTEST_MAIN(KWin::MoveResizeWindowTest) 1152 #include "move_resize_window_test.moc"