File indexing completed on 2024-05-12 05:30:37

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"