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

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