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