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

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2015 Martin Gräßlin <mgraesslin@kde.org>
0006 
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 #include "kwin_wayland_test.h"
0010 
0011 #include "core/output.h"
0012 #include "core/outputbackend.h"
0013 #include "cursor.h"
0014 #include "decorations/decorationbridge.h"
0015 #include "decorations/settings.h"
0016 #include "scripting/scripting.h"
0017 #include "utils/common.h"
0018 #include "wayland_server.h"
0019 #include "window.h"
0020 #include "workspace.h"
0021 #include "x11window.h"
0022 
0023 #include <KDecoration2/DecoratedClient>
0024 #include <KDecoration2/Decoration>
0025 #include <KDecoration2/DecorationSettings>
0026 
0027 #include <KWayland/Client/compositor.h>
0028 #include <KWayland/Client/connection_thread.h>
0029 #include <KWayland/Client/server_decoration.h>
0030 #include <KWayland/Client/surface.h>
0031 
0032 #include <QDBusConnection>
0033 #include <QDBusMessage>
0034 #include <QDBusPendingCall>
0035 #include <QTemporaryFile>
0036 #include <QTextStream>
0037 
0038 #include <netwm.h>
0039 #include <xcb/xcb_icccm.h>
0040 
0041 #include <linux/input.h>
0042 
0043 Q_DECLARE_METATYPE(KWin::QuickTileMode)
0044 Q_DECLARE_METATYPE(KWin::MaximizeMode)
0045 
0046 namespace KWin
0047 {
0048 
0049 static const QString s_socketName = QStringLiteral("wayland_test_kwin_quick_tiling-0");
0050 
0051 class QuickTilingTest : public QObject
0052 {
0053     Q_OBJECT
0054 private Q_SLOTS:
0055     void initTestCase();
0056     void init();
0057     void cleanup();
0058     void testQuickTiling_data();
0059     void testQuickTiling();
0060     void testQuickMaximizing_data();
0061     void testQuickMaximizing();
0062     void testQuickTilingKeyboardMove_data();
0063     void testQuickTilingKeyboardMove();
0064     void testQuickTilingPointerMove_data();
0065     void testQuickTilingPointerMove();
0066     void testQuickTilingTouchMove_data();
0067     void testQuickTilingTouchMove();
0068     void testX11QuickTiling_data();
0069     void testX11QuickTiling();
0070     void testX11QuickTilingAfterVertMaximize_data();
0071     void testX11QuickTilingAfterVertMaximize();
0072     void testShortcut_data();
0073     void testShortcut();
0074     void testScript_data();
0075     void testScript();
0076 
0077 private:
0078     KWayland::Client::ConnectionThread *m_connection = nullptr;
0079     KWayland::Client::Compositor *m_compositor = nullptr;
0080 };
0081 
0082 void QuickTilingTest::initTestCase()
0083 {
0084     qRegisterMetaType<KWin::Window *>();
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) << QRect(1280, 0, 1280, 1024)));
0089 
0090     // set custom config which disables the Outline
0091     KSharedConfig::Ptr config = KSharedConfig::openConfig(QString(), KConfig::SimpleConfig);
0092     KConfigGroup group = config->group("Outline");
0093     group.writeEntry(QStringLiteral("QmlPath"), QString("/does/not/exist.qml"));
0094     group.sync();
0095 
0096     kwinApp()->setConfig(config);
0097 
0098     qputenv("XKB_DEFAULT_RULES", "evdev");
0099 
0100     kwinApp()->start();
0101     QVERIFY(applicationStartedSpy.wait());
0102 
0103     const auto outputs = workspace()->outputs();
0104     QCOMPARE(outputs.count(), 2);
0105     QCOMPARE(outputs[0]->geometry(), QRect(0, 0, 1280, 1024));
0106     QCOMPARE(outputs[1]->geometry(), QRect(1280, 0, 1280, 1024));
0107 }
0108 
0109 void QuickTilingTest::init()
0110 {
0111     QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::Decoration));
0112     m_connection = Test::waylandConnection();
0113     m_compositor = Test::waylandCompositor();
0114 
0115     workspace()->setActiveOutput(QPoint(640, 512));
0116     Cursors::self()->mouse()->setPos(QPoint(640, 512));
0117 }
0118 
0119 void QuickTilingTest::cleanup()
0120 {
0121     Test::destroyWaylandConnection();
0122 }
0123 
0124 void QuickTilingTest::testQuickTiling_data()
0125 {
0126     QTest::addColumn<QuickTileMode>("mode");
0127     QTest::addColumn<QRectF>("expectedGeometry");
0128     QTest::addColumn<QRectF>("secondScreen");
0129     QTest::addColumn<QuickTileMode>("expectedModeAfterToggle");
0130 
0131 #define FLAG(name) QuickTileMode(QuickTileFlag::name)
0132 
0133     QTest::newRow("left") << FLAG(Left) << QRectF(0, 0, 640, 1024) << QRectF(1280, 0, 640, 1024) << FLAG(Right);
0134     QTest::newRow("top") << FLAG(Top) << QRectF(0, 0, 1280, 512) << QRectF(1280, 0, 1280, 512) << QuickTileMode();
0135     QTest::newRow("right") << FLAG(Right) << QRectF(640, 0, 640, 1024) << QRectF(1920, 0, 640, 1024) << QuickTileMode();
0136     QTest::newRow("bottom") << FLAG(Bottom) << QRectF(0, 512, 1280, 512) << QRectF(1280, 512, 1280, 512) << QuickTileMode();
0137 
0138     QTest::newRow("top left") << (FLAG(Left) | FLAG(Top)) << QRectF(0, 0, 640, 512) << QRectF(1280, 0, 640, 512) << (FLAG(Right) | FLAG(Top));
0139     QTest::newRow("top right") << (FLAG(Right) | FLAG(Top)) << QRectF(640, 0, 640, 512) << QRectF(1920, 0, 640, 512) << QuickTileMode();
0140     QTest::newRow("bottom left") << (FLAG(Left) | FLAG(Bottom)) << QRectF(0, 512, 640, 512) << QRectF(1280, 512, 640, 512) << (FLAG(Right) | FLAG(Bottom));
0141     QTest::newRow("bottom right") << (FLAG(Right) | FLAG(Bottom)) << QRectF(640, 512, 640, 512) << QRectF(1920, 512, 640, 512) << QuickTileMode();
0142 
0143     QTest::newRow("maximize") << FLAG(Maximize) << QRectF(0, 0, 1280, 1024) << QRectF(1280, 0, 1280, 1024) << QuickTileMode();
0144 
0145 #undef FLAG
0146 }
0147 
0148 void QuickTilingTest::testQuickTiling()
0149 {
0150     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0151     QVERIFY(surface != nullptr);
0152     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0153     QVERIFY(shellSurface != nullptr);
0154 
0155     // Map the window.
0156     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0157     QVERIFY(window);
0158     QCOMPARE(workspace()->activeWindow(), window);
0159     QCOMPARE(window->frameGeometry(), QRect(0, 0, 100, 50));
0160     QCOMPARE(window->quickTileMode(), QuickTileMode(QuickTileFlag::None));
0161 
0162     // We have to receive a configure event when the window becomes active.
0163     QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
0164     QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
0165     QVERIFY(surfaceConfigureRequestedSpy.wait());
0166     QCOMPARE(surfaceConfigureRequestedSpy.count(), 1);
0167 
0168     QSignalSpy quickTileChangedSpy(window, &Window::quickTileModeChanged);
0169     QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
0170 
0171     QFETCH(QuickTileMode, mode);
0172     QFETCH(QRectF, expectedGeometry);
0173     window->setQuickTileMode(mode, true);
0174     QCOMPARE(quickTileChangedSpy.count(), 1);
0175     // at this point the geometry did not yet change
0176     QCOMPARE(window->frameGeometry(), QRect(0, 0, 100, 50));
0177     // but quick tile mode already changed
0178     QCOMPARE(window->quickTileMode(), mode);
0179 
0180     // but we got requested a new geometry
0181     QVERIFY(surfaceConfigureRequestedSpy.wait());
0182     QCOMPARE(surfaceConfigureRequestedSpy.count(), 2);
0183     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), expectedGeometry.size());
0184 
0185     // attach a new image
0186     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
0187     Test::render(surface.get(), expectedGeometry.size().toSize(), Qt::red);
0188 
0189     QVERIFY(frameGeometryChangedSpy.wait());
0190     QCOMPARE(frameGeometryChangedSpy.count(), 1);
0191     QCOMPARE(window->frameGeometry(), expectedGeometry);
0192 
0193     // send window to other screen
0194     QList<Output *> outputs = workspace()->outputs();
0195     QCOMPARE(window->output(), outputs[0]);
0196     window->sendToOutput(outputs[1]);
0197     QCOMPARE(window->output(), outputs[1]);
0198     // quick tile should not be changed
0199     QCOMPARE(window->quickTileMode(), mode);
0200     QTEST(window->frameGeometry(), "secondScreen");
0201 
0202     // now try to toggle again
0203     window->setQuickTileMode(mode, true);
0204     QTEST(window->quickTileMode(), "expectedModeAfterToggle");
0205 }
0206 
0207 void QuickTilingTest::testQuickMaximizing_data()
0208 {
0209     QTest::addColumn<QuickTileMode>("mode");
0210 
0211 #define FLAG(name) QuickTileMode(QuickTileFlag::name)
0212 
0213     QTest::newRow("maximize") << FLAG(Maximize);
0214     QTest::newRow("none") << FLAG(None);
0215 
0216 #undef FLAG
0217 }
0218 
0219 void QuickTilingTest::testQuickMaximizing()
0220 {
0221     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0222     QVERIFY(surface != nullptr);
0223     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0224     QVERIFY(shellSurface != nullptr);
0225 
0226     // Map the window.
0227     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0228     QVERIFY(window);
0229     QCOMPARE(workspace()->activeWindow(), window);
0230     QCOMPARE(window->frameGeometry(), QRect(0, 0, 100, 50));
0231     QCOMPARE(window->quickTileMode(), QuickTileMode(QuickTileFlag::None));
0232     QCOMPARE(window->maximizeMode(), MaximizeRestore);
0233 
0234     // We have to receive a configure event upon becoming active.
0235     QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
0236     QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
0237     QVERIFY(surfaceConfigureRequestedSpy.wait());
0238     QCOMPARE(surfaceConfigureRequestedSpy.count(), 1);
0239 
0240     QSignalSpy quickTileChangedSpy(window, &Window::quickTileModeChanged);
0241     QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
0242     QSignalSpy maximizeChangedSpy1(window, qOverload<Window *, MaximizeMode>(&Window::clientMaximizedStateChanged));
0243     QSignalSpy maximizeChangedSpy2(window, qOverload<Window *, bool, bool>(&Window::clientMaximizedStateChanged));
0244 
0245     window->setQuickTileMode(QuickTileFlag::Maximize, true);
0246     QCOMPARE(quickTileChangedSpy.count(), 1);
0247 
0248     // at this point the geometry did not yet change
0249     QCOMPARE(window->frameGeometry(), QRect(0, 0, 100, 50));
0250     // but quick tile mode already changed
0251     QCOMPARE(window->quickTileMode(), QuickTileFlag::Maximize);
0252     QCOMPARE(window->geometryRestore(), QRect(0, 0, 100, 50));
0253 
0254     // but we got requested a new geometry
0255     QVERIFY(surfaceConfigureRequestedSpy.wait());
0256     QCOMPARE(surfaceConfigureRequestedSpy.count(), 2);
0257     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(1280, 1024));
0258 
0259     // attach a new image
0260     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
0261     Test::render(surface.get(), QSize(1280, 1024), Qt::red);
0262 
0263     QVERIFY(frameGeometryChangedSpy.wait());
0264     QCOMPARE(frameGeometryChangedSpy.count(), 1);
0265     QCOMPARE(window->frameGeometry(), QRect(0, 0, 1280, 1024));
0266     QCOMPARE(window->geometryRestore(), QRect(0, 0, 100, 50));
0267 
0268     // window is now set to maximised
0269     QCOMPARE(maximizeChangedSpy1.count(), 1);
0270     QCOMPARE(maximizeChangedSpy1.first().first().value<KWin::Window *>(), window);
0271     QCOMPARE(maximizeChangedSpy1.first().last().value<KWin::MaximizeMode>(), MaximizeFull);
0272     QCOMPARE(maximizeChangedSpy2.count(), 1);
0273     QCOMPARE(maximizeChangedSpy2.first().first().value<KWin::Window *>(), window);
0274     QCOMPARE(maximizeChangedSpy2.first().at(1).toBool(), true);
0275     QCOMPARE(maximizeChangedSpy2.first().at(2).toBool(), true);
0276     QCOMPARE(window->maximizeMode(), MaximizeFull);
0277 
0278     // go back to quick tile none
0279     QFETCH(QuickTileMode, mode);
0280     window->setQuickTileMode(mode, true);
0281     QCOMPARE(window->quickTileMode(), QuickTileMode(QuickTileFlag::None));
0282     QCOMPARE(quickTileChangedSpy.count(), 2);
0283     // geometry not yet changed
0284     QCOMPARE(window->frameGeometry(), QRect(0, 0, 1280, 1024));
0285     QCOMPARE(window->geometryRestore(), QRect(0, 0, 100, 50));
0286     // we got requested a new geometry
0287     QVERIFY(surfaceConfigureRequestedSpy.wait());
0288     QCOMPARE(surfaceConfigureRequestedSpy.count(), 3);
0289     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(100, 50));
0290 
0291     // render again
0292     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
0293     Test::render(surface.get(), QSize(100, 50), Qt::yellow);
0294 
0295     QVERIFY(frameGeometryChangedSpy.wait());
0296     QCOMPARE(frameGeometryChangedSpy.count(), 2);
0297     QCOMPARE(window->frameGeometry(), QRect(0, 0, 100, 50));
0298     QCOMPARE(window->geometryRestore(), QRect(0, 0, 100, 50));
0299     QCOMPARE(maximizeChangedSpy1.count(), 2);
0300     QCOMPARE(maximizeChangedSpy1.last().first().value<KWin::Window *>(), window);
0301     QCOMPARE(maximizeChangedSpy1.last().last().value<KWin::MaximizeMode>(), MaximizeRestore);
0302     QCOMPARE(maximizeChangedSpy2.count(), 2);
0303     QCOMPARE(maximizeChangedSpy2.last().first().value<KWin::Window *>(), window);
0304     QCOMPARE(maximizeChangedSpy2.last().at(1).toBool(), false);
0305     QCOMPARE(maximizeChangedSpy2.last().at(2).toBool(), false);
0306 }
0307 
0308 void QuickTilingTest::testQuickTilingKeyboardMove_data()
0309 {
0310     QTest::addColumn<QPoint>("targetPos");
0311     QTest::addColumn<QuickTileMode>("expectedMode");
0312 
0313     QTest::newRow("topRight") << QPoint(2559, 24) << QuickTileMode(QuickTileFlag::Top | QuickTileFlag::Right);
0314     QTest::newRow("right") << QPoint(2559, 512) << QuickTileMode(QuickTileFlag::Right);
0315     QTest::newRow("bottomRight") << QPoint(2559, 1023) << QuickTileMode(QuickTileFlag::Bottom | QuickTileFlag::Right);
0316     QTest::newRow("bottomLeft") << QPoint(0, 1023) << QuickTileMode(QuickTileFlag::Bottom | QuickTileFlag::Left);
0317     QTest::newRow("Left") << QPoint(0, 512) << QuickTileMode(QuickTileFlag::Left);
0318     QTest::newRow("topLeft") << QPoint(0, 24) << QuickTileMode(QuickTileFlag::Top | QuickTileFlag::Left);
0319 }
0320 
0321 void QuickTilingTest::testQuickTilingKeyboardMove()
0322 {
0323     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0324     QVERIFY(surface != nullptr);
0325 
0326     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0327     QVERIFY(shellSurface != nullptr);
0328     // let's render
0329     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0330 
0331     QVERIFY(window);
0332     QCOMPARE(workspace()->activeWindow(), window);
0333     QCOMPARE(window->frameGeometry(), QRect(0, 0, 100, 50));
0334     QCOMPARE(window->quickTileMode(), QuickTileMode(QuickTileFlag::None));
0335     QCOMPARE(window->maximizeMode(), MaximizeRestore);
0336 
0337     QSignalSpy quickTileChangedSpy(window, &Window::quickTileModeChanged);
0338 
0339     workspace()->performWindowOperation(window, Options::UnrestrictedMoveOp);
0340     QCOMPARE(window, workspace()->moveResizeWindow());
0341     QCOMPARE(Cursors::self()->mouse()->pos(), QPoint(50, 25));
0342 
0343     QFETCH(QPoint, targetPos);
0344     quint32 timestamp = 1;
0345     Test::keyboardKeyPressed(KEY_LEFTCTRL, timestamp++);
0346     while (Cursors::self()->mouse()->pos().x() > targetPos.x()) {
0347         Test::keyboardKeyPressed(KEY_LEFT, timestamp++);
0348         Test::keyboardKeyReleased(KEY_LEFT, timestamp++);
0349     }
0350     while (Cursors::self()->mouse()->pos().x() < targetPos.x()) {
0351         Test::keyboardKeyPressed(KEY_RIGHT, timestamp++);
0352         Test::keyboardKeyReleased(KEY_RIGHT, timestamp++);
0353     }
0354     while (Cursors::self()->mouse()->pos().y() < targetPos.y()) {
0355         Test::keyboardKeyPressed(KEY_DOWN, timestamp++);
0356         Test::keyboardKeyReleased(KEY_DOWN, timestamp++);
0357     }
0358     while (Cursors::self()->mouse()->pos().y() > targetPos.y()) {
0359         Test::keyboardKeyPressed(KEY_UP, timestamp++);
0360         Test::keyboardKeyReleased(KEY_UP, timestamp++);
0361     }
0362     Test::keyboardKeyReleased(KEY_LEFTCTRL, timestamp++);
0363     Test::keyboardKeyPressed(KEY_ENTER, timestamp++);
0364     Test::keyboardKeyReleased(KEY_ENTER, timestamp++);
0365     QCOMPARE(Cursors::self()->mouse()->pos(), targetPos);
0366     QVERIFY(!workspace()->moveResizeWindow());
0367 
0368     QCOMPARE(quickTileChangedSpy.count(), 1);
0369     QTEST(window->quickTileMode(), "expectedMode");
0370 }
0371 
0372 void QuickTilingTest::testQuickTilingPointerMove_data()
0373 {
0374     QTest::addColumn<QPoint>("pointerPos");
0375     QTest::addColumn<QSize>("tileSize");
0376     QTest::addColumn<QuickTileMode>("expectedMode");
0377 
0378     QTest::newRow("topRight") << QPoint(2559, 24) << QSize(640, 512) << QuickTileMode(QuickTileFlag::Top | QuickTileFlag::Right);
0379     QTest::newRow("right") << QPoint(2559, 512) << QSize(640, 1024) << QuickTileMode(QuickTileFlag::Right);
0380     QTest::newRow("bottomRight") << QPoint(2559, 1023) << QSize(640, 512) << QuickTileMode(QuickTileFlag::Bottom | QuickTileFlag::Right);
0381     QTest::newRow("bottomLeft") << QPoint(0, 1023) << QSize(640, 512) << QuickTileMode(QuickTileFlag::Bottom | QuickTileFlag::Left);
0382     QTest::newRow("Left") << QPoint(0, 512) << QSize(640, 1024) << QuickTileMode(QuickTileFlag::Left);
0383     QTest::newRow("topLeft") << QPoint(0, 24) << QSize(640, 512) << QuickTileMode(QuickTileFlag::Top | QuickTileFlag::Left);
0384 }
0385 
0386 void QuickTilingTest::testQuickTilingPointerMove()
0387 {
0388     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0389     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0390 
0391     // let's render
0392     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0393     QVERIFY(window);
0394     QCOMPARE(workspace()->activeWindow(), window);
0395     QCOMPARE(window->frameGeometry(), QRect(0, 0, 100, 50));
0396     QCOMPARE(window->quickTileMode(), QuickTileMode(QuickTileFlag::None));
0397     QCOMPARE(window->maximizeMode(), MaximizeRestore);
0398 
0399     // we have to receive a configure event when the window becomes active
0400     QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
0401     QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
0402     QVERIFY(surfaceConfigureRequestedSpy.wait());
0403     QCOMPARE(surfaceConfigureRequestedSpy.count(), 1);
0404 
0405     // verify that basic quick tile mode works as expected, i.e. the window is going to be
0406     // tiled if the user drags it to a screen edge or a corner
0407     QSignalSpy quickTileChangedSpy(window, &Window::quickTileModeChanged);
0408     workspace()->performWindowOperation(window, Options::UnrestrictedMoveOp);
0409     QCOMPARE(window, workspace()->moveResizeWindow());
0410     QCOMPARE(Cursors::self()->mouse()->pos(), QPoint(50, 25));
0411 
0412     QFETCH(QPoint, pointerPos);
0413     QFETCH(QSize, tileSize);
0414     quint32 timestamp = 1;
0415     Test::pointerButtonPressed(BTN_LEFT, timestamp++);
0416     Test::pointerMotion(pointerPos, timestamp++);
0417     Test::pointerButtonReleased(BTN_LEFT, timestamp++);
0418     QCOMPARE(quickTileChangedSpy.count(), 1);
0419     QTEST(window->quickTileMode(), "expectedMode");
0420     QCOMPARE(window->geometryRestore(), QRect(0, 0, 100, 50));
0421     QVERIFY(surfaceConfigureRequestedSpy.wait());
0422     QCOMPARE(surfaceConfigureRequestedSpy.count(), 2);
0423     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), tileSize);
0424 
0425     // verify that geometry restore is correct after user untiles the window, but changes
0426     // their mind and tiles the window again while still holding left button
0427     workspace()->performWindowOperation(window, Options::UnrestrictedMoveOp);
0428     QCOMPARE(window, workspace()->moveResizeWindow());
0429 
0430     Test::pointerButtonPressed(BTN_LEFT, timestamp++); // untile the window
0431     Test::pointerMotion(QPoint(1280, 1024) / 2, timestamp++);
0432     QCOMPARE(quickTileChangedSpy.count(), 2);
0433     QCOMPARE(window->quickTileMode(), QuickTileMode(QuickTileFlag::None));
0434     QVERIFY(surfaceConfigureRequestedSpy.wait());
0435     QCOMPARE(surfaceConfigureRequestedSpy.count(), 3);
0436     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), QSize(100, 50));
0437 
0438     Test::pointerMotion(pointerPos, timestamp++); // tile the window again
0439     Test::pointerButtonReleased(BTN_LEFT, timestamp++);
0440     QCOMPARE(quickTileChangedSpy.count(), 3);
0441     QTEST(window->quickTileMode(), "expectedMode");
0442     QCOMPARE(window->geometryRestore(), QRect(0, 0, 100, 50));
0443     QVERIFY(surfaceConfigureRequestedSpy.wait());
0444     QCOMPARE(surfaceConfigureRequestedSpy.count(), 4);
0445     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), tileSize);
0446 }
0447 
0448 void QuickTilingTest::testQuickTilingTouchMove_data()
0449 {
0450     QTest::addColumn<QPoint>("targetPos");
0451     QTest::addColumn<QuickTileMode>("expectedMode");
0452 
0453     QTest::newRow("topRight") << QPoint(2559, 24) << QuickTileMode(QuickTileFlag::Top | QuickTileFlag::Right);
0454     QTest::newRow("right") << QPoint(2559, 512) << QuickTileMode(QuickTileFlag::Right);
0455     QTest::newRow("bottomRight") << QPoint(2559, 1023) << QuickTileMode(QuickTileFlag::Bottom | QuickTileFlag::Right);
0456     QTest::newRow("bottomLeft") << QPoint(0, 1023) << QuickTileMode(QuickTileFlag::Bottom | QuickTileFlag::Left);
0457     QTest::newRow("Left") << QPoint(0, 512) << QuickTileMode(QuickTileFlag::Left);
0458     QTest::newRow("topLeft") << QPoint(0, 24) << QuickTileMode(QuickTileFlag::Top | QuickTileFlag::Left);
0459 }
0460 
0461 void QuickTilingTest::testQuickTilingTouchMove()
0462 {
0463     // test verifies that touch on decoration also allows quick tiling
0464     // see BUG: 390113
0465 
0466     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0467     QVERIFY(surface != nullptr);
0468     std::unique_ptr<KWayland::Client::ServerSideDecoration> deco(Test::waylandServerSideDecoration()->create(surface.get()));
0469 
0470     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get(), Test::CreationSetup::CreateOnly));
0471     QVERIFY(shellSurface != nullptr);
0472 
0473     // wait for the initial configure event
0474     QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
0475     QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
0476     surface->commit(KWayland::Client::Surface::CommitFlag::None);
0477     QVERIFY(surfaceConfigureRequestedSpy.wait());
0478     QCOMPARE(surfaceConfigureRequestedSpy.count(), 1);
0479 
0480     // let's render
0481     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
0482     auto window = Test::renderAndWaitForShown(surface.get(), QSize(1000, 50), Qt::blue);
0483 
0484     QVERIFY(window);
0485     QVERIFY(window->isDecorated());
0486     const auto decoration = window->decoration();
0487     QCOMPARE(workspace()->activeWindow(), window);
0488     QCOMPARE(window->frameGeometry(), QRect(-decoration->borderLeft(), 0, 1000 + decoration->borderLeft() + decoration->borderRight(), 50 + decoration->borderTop() + decoration->borderBottom()));
0489     QCOMPARE(window->quickTileMode(), QuickTileMode(QuickTileFlag::None));
0490     QCOMPARE(window->maximizeMode(), MaximizeRestore);
0491 
0492     // we have to receive a configure event when the window becomes active
0493     QVERIFY(surfaceConfigureRequestedSpy.wait());
0494     QTRY_COMPARE(surfaceConfigureRequestedSpy.count(), 2);
0495 
0496     QSignalSpy quickTileChangedSpy(window, &Window::quickTileModeChanged);
0497 
0498     // Note that interactive move will be started with a delay.
0499     quint32 timestamp = 1;
0500     QSignalSpy clientStartUserMovedResizedSpy(window, &Window::clientStartUserMovedResized);
0501     Test::touchDown(0, QPointF(window->frameGeometry().center().x(), window->frameGeometry().y() + decoration->borderTop() / 2), timestamp++);
0502     QVERIFY(clientStartUserMovedResizedSpy.wait());
0503     QCOMPARE(window, workspace()->moveResizeWindow());
0504 
0505     QFETCH(QPoint, targetPos);
0506     Test::touchMotion(0, targetPos, timestamp++);
0507     Test::touchUp(0, timestamp++);
0508     QVERIFY(!workspace()->moveResizeWindow());
0509 
0510     // When there are no borders, there is no change to them when quick-tiling.
0511     // TODO: we should test both cases with fixed fake decoration for autotests.
0512     const bool hasBorders = Workspace::self()->decorationBridge()->settings()->borderSize() != KDecoration2::BorderSize::None;
0513 
0514     QCOMPARE(quickTileChangedSpy.count(), 1);
0515     QTEST(window->quickTileMode(), "expectedMode");
0516     QVERIFY(surfaceConfigureRequestedSpy.wait());
0517     QTRY_COMPARE(surfaceConfigureRequestedSpy.count(), hasBorders ? 4 : 3);
0518     QCOMPARE(false, toplevelConfigureRequestedSpy.last().first().toSize().isEmpty());
0519 }
0520 
0521 struct XcbConnectionDeleter
0522 {
0523     void operator()(xcb_connection_t *pointer)
0524     {
0525         xcb_disconnect(pointer);
0526     }
0527 };
0528 
0529 void QuickTilingTest::testX11QuickTiling_data()
0530 {
0531     QTest::addColumn<QuickTileMode>("mode");
0532     QTest::addColumn<QRectF>("expectedGeometry");
0533     QTest::addColumn<int>("screenId");
0534     QTest::addColumn<QuickTileMode>("modeAfterToggle");
0535 
0536 #define FLAG(name) QuickTileMode(QuickTileFlag::name)
0537 
0538     QTest::newRow("left") << FLAG(Left) << QRectF(0, 0, 640, 1024) << 0 << QuickTileMode();
0539     QTest::newRow("top") << FLAG(Top) << QRectF(0, 0, 1280, 512) << 0 << QuickTileMode();
0540     QTest::newRow("right") << FLAG(Right) << QRectF(640, 0, 640, 1024) << 1 << FLAG(Left);
0541     QTest::newRow("bottom") << FLAG(Bottom) << QRectF(0, 512, 1280, 512) << 0 << QuickTileMode();
0542 
0543     QTest::newRow("top left") << (FLAG(Left) | FLAG(Top)) << QRectF(0, 0, 640, 512) << 0 << QuickTileMode();
0544     QTest::newRow("top right") << (FLAG(Right) | FLAG(Top)) << QRectF(640, 0, 640, 512) << 1 << (FLAG(Left) | FLAG(Top));
0545     QTest::newRow("bottom left") << (FLAG(Left) | FLAG(Bottom)) << QRectF(0, 512, 640, 512) << 0 << QuickTileMode();
0546     QTest::newRow("bottom right") << (FLAG(Right) | FLAG(Bottom)) << QRectF(640, 512, 640, 512) << 1 << (FLAG(Left) | FLAG(Bottom));
0547 
0548     QTest::newRow("maximize") << FLAG(Maximize) << QRectF(0, 0, 1280, 1024) << 0 << QuickTileMode();
0549 
0550 #undef FLAG
0551 }
0552 void QuickTilingTest::testX11QuickTiling()
0553 {
0554     std::unique_ptr<xcb_connection_t, XcbConnectionDeleter> c(xcb_connect(nullptr, nullptr));
0555     QVERIFY(!xcb_connection_has_error(c.get()));
0556     const QRect windowGeometry(0, 0, 100, 200);
0557     xcb_window_t windowId = xcb_generate_id(c.get());
0558     xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(),
0559                       windowGeometry.x(),
0560                       windowGeometry.y(),
0561                       windowGeometry.width(),
0562                       windowGeometry.height(),
0563                       0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
0564     xcb_size_hints_t hints;
0565     memset(&hints, 0, sizeof(hints));
0566     xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y());
0567     xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height());
0568     xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
0569     xcb_map_window(c.get(), windowId);
0570     xcb_flush(c.get());
0571 
0572     // we should get a window for it
0573     QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
0574     QVERIFY(windowCreatedSpy.wait());
0575     X11Window *window = windowCreatedSpy.first().first().value<X11Window *>();
0576     QVERIFY(window);
0577     QCOMPARE(window->window(), windowId);
0578 
0579     // now quick tile
0580     QSignalSpy quickTileChangedSpy(window, &Window::quickTileModeChanged);
0581     const QRectF origGeo = window->frameGeometry();
0582     QFETCH(QuickTileMode, mode);
0583     window->setQuickTileMode(mode, true);
0584     QCOMPARE(window->quickTileMode(), mode);
0585     QTEST(window->frameGeometry(), "expectedGeometry");
0586     QCOMPARE(window->geometryRestore(), origGeo);
0587     QEXPECT_FAIL("maximize", "For maximize we get two changed signals", Continue);
0588     QCOMPARE(quickTileChangedSpy.count(), 1);
0589 
0590     // quick tile to same edge again should also act like send to screen
0591     // if screen is on the same edge
0592     const auto outputs = workspace()->outputs();
0593     QCOMPARE(window->output(), outputs[0]);
0594     window->setQuickTileMode(mode, true);
0595     QFETCH(int, screenId);
0596     QCOMPARE(window->output(), outputs[screenId]);
0597     QTEST(window->quickTileMode(), "modeAfterToggle");
0598     QCOMPARE(window->geometryRestore(), origGeo);
0599 
0600     // and destroy the window again
0601     xcb_unmap_window(c.get(), windowId);
0602     xcb_destroy_window(c.get(), windowId);
0603     xcb_flush(c.get());
0604     c.reset();
0605 
0606     QSignalSpy windowClosedSpy(window, &X11Window::windowClosed);
0607     QVERIFY(windowClosedSpy.wait());
0608 }
0609 
0610 void QuickTilingTest::testX11QuickTilingAfterVertMaximize_data()
0611 {
0612     QTest::addColumn<QuickTileMode>("mode");
0613     QTest::addColumn<QRectF>("expectedGeometry");
0614 
0615 #define FLAG(name) QuickTileMode(QuickTileFlag::name)
0616 
0617     QTest::newRow("left") << FLAG(Left) << QRectF(0, 0, 640, 1024);
0618     QTest::newRow("top") << FLAG(Top) << QRectF(0, 0, 1280, 512);
0619     QTest::newRow("right") << FLAG(Right) << QRectF(640, 0, 640, 1024);
0620     QTest::newRow("bottom") << FLAG(Bottom) << QRectF(0, 512, 1280, 512);
0621 
0622     QTest::newRow("top left") << (FLAG(Left) | FLAG(Top)) << QRectF(0, 0, 640, 512);
0623     QTest::newRow("top right") << (FLAG(Right) | FLAG(Top)) << QRectF(640, 0, 640, 512);
0624     QTest::newRow("bottom left") << (FLAG(Left) | FLAG(Bottom)) << QRectF(0, 512, 640, 512);
0625     QTest::newRow("bottom right") << (FLAG(Right) | FLAG(Bottom)) << QRectF(640, 512, 640, 512);
0626 
0627     QTest::newRow("maximize") << FLAG(Maximize) << QRectF(0, 0, 1280, 1024);
0628 
0629 #undef FLAG
0630 }
0631 
0632 void QuickTilingTest::testX11QuickTilingAfterVertMaximize()
0633 {
0634     std::unique_ptr<xcb_connection_t, XcbConnectionDeleter> c(xcb_connect(nullptr, nullptr));
0635     QVERIFY(!xcb_connection_has_error(c.get()));
0636     const QRect windowGeometry(0, 0, 100, 200);
0637     xcb_window_t windowId = xcb_generate_id(c.get());
0638     xcb_create_window(c.get(), XCB_COPY_FROM_PARENT, windowId, rootWindow(),
0639                       windowGeometry.x(),
0640                       windowGeometry.y(),
0641                       windowGeometry.width(),
0642                       windowGeometry.height(),
0643                       0, XCB_WINDOW_CLASS_INPUT_OUTPUT, XCB_COPY_FROM_PARENT, 0, nullptr);
0644     xcb_size_hints_t hints;
0645     memset(&hints, 0, sizeof(hints));
0646     xcb_icccm_size_hints_set_position(&hints, 1, windowGeometry.x(), windowGeometry.y());
0647     xcb_icccm_size_hints_set_size(&hints, 1, windowGeometry.width(), windowGeometry.height());
0648     xcb_icccm_set_wm_normal_hints(c.get(), windowId, &hints);
0649     xcb_map_window(c.get(), windowId);
0650     xcb_flush(c.get());
0651 
0652     // we should get a window for it
0653     QSignalSpy windowCreatedSpy(workspace(), &Workspace::windowAdded);
0654     QVERIFY(windowCreatedSpy.wait());
0655     X11Window *window = windowCreatedSpy.first().first().value<X11Window *>();
0656     QVERIFY(window);
0657     QCOMPARE(window->window(), windowId);
0658 
0659     const QRectF origGeo = window->frameGeometry();
0660     QCOMPARE(window->maximizeMode(), MaximizeRestore);
0661     // vertically maximize the window
0662     window->maximize(window->maximizeMode() ^ MaximizeVertical);
0663     QCOMPARE(window->frameGeometry().width(), origGeo.width());
0664     QCOMPARE(window->height(), window->output()->geometry().height());
0665     QCOMPARE(window->geometryRestore(), origGeo);
0666 
0667     // now quick tile
0668     QSignalSpy quickTileChangedSpy(window, &Window::quickTileModeChanged);
0669     QFETCH(QuickTileMode, mode);
0670     window->setQuickTileMode(mode, true);
0671     QCOMPARE(window->quickTileMode(), mode);
0672     QTEST(window->frameGeometry(), "expectedGeometry");
0673     QEXPECT_FAIL("", "We get two changed events", Continue);
0674     QCOMPARE(quickTileChangedSpy.count(), 1);
0675 
0676     // and destroy the window again
0677     xcb_unmap_window(c.get(), windowId);
0678     xcb_destroy_window(c.get(), windowId);
0679     xcb_flush(c.get());
0680     c.reset();
0681 
0682     QSignalSpy windowClosedSpy(window, &X11Window::windowClosed);
0683     QVERIFY(windowClosedSpy.wait());
0684 }
0685 
0686 void QuickTilingTest::testShortcut_data()
0687 {
0688     QTest::addColumn<QStringList>("shortcutList");
0689     QTest::addColumn<QuickTileMode>("expectedMode");
0690     QTest::addColumn<QRect>("expectedGeometry");
0691 
0692 #define FLAG(name) QuickTileMode(QuickTileFlag::name)
0693     QTest::newRow("top") << QStringList{QStringLiteral("Window Quick Tile Top")} << FLAG(Top) << QRect(0, 0, 1280, 512);
0694     QTest::newRow("bottom") << QStringList{QStringLiteral("Window Quick Tile Bottom")} << FLAG(Bottom) << QRect(0, 512, 1280, 512);
0695     QTest::newRow("top right") << QStringList{QStringLiteral("Window Quick Tile Top Right")} << (FLAG(Top) | FLAG(Right)) << QRect(640, 0, 640, 512);
0696     QTest::newRow("top left") << QStringList{QStringLiteral("Window Quick Tile Top Left")} << (FLAG(Top) | FLAG(Left)) << QRect(0, 0, 640, 512);
0697     QTest::newRow("bottom right") << QStringList{QStringLiteral("Window Quick Tile Bottom Right")} << (FLAG(Bottom) | FLAG(Right)) << QRect(640, 512, 640, 512);
0698     QTest::newRow("bottom left") << QStringList{QStringLiteral("Window Quick Tile Bottom Left")} << (FLAG(Bottom) | FLAG(Left)) << QRect(0, 512, 640, 512);
0699     QTest::newRow("left") << QStringList{QStringLiteral("Window Quick Tile Left")} << FLAG(Left) << QRect(0, 0, 640, 1024);
0700     QTest::newRow("right") << QStringList{QStringLiteral("Window Quick Tile Right")} << FLAG(Right) << QRect(640, 0, 640, 1024);
0701 
0702     // Test combined actions for corner tiling
0703     QTest::newRow("top left combined") << QStringList{QStringLiteral("Window Quick Tile Left"), QStringLiteral("Window Quick Tile Top")} << (FLAG(Top) | FLAG(Left)) << QRect(0, 0, 640, 512);
0704     QTest::newRow("top right combined") << QStringList{QStringLiteral("Window Quick Tile Right"), QStringLiteral("Window Quick Tile Top")} << (FLAG(Top) | FLAG(Right)) << QRect(640, 0, 640, 512);
0705     QTest::newRow("bottom left combined") << QStringList{QStringLiteral("Window Quick Tile Left"), QStringLiteral("Window Quick Tile Bottom")} << (FLAG(Bottom) | FLAG(Left)) << QRect(0, 512, 640, 512);
0706     QTest::newRow("bottom right combined") << QStringList{QStringLiteral("Window Quick Tile Right"), QStringLiteral("Window Quick Tile Bottom")} << (FLAG(Bottom) | FLAG(Right)) << QRect(640, 512, 640, 512);
0707 #undef FLAG
0708 }
0709 
0710 void QuickTilingTest::testShortcut()
0711 {
0712     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0713     QVERIFY(surface != nullptr);
0714     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0715     QVERIFY(shellSurface != nullptr);
0716 
0717     // Map the window.
0718     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0719     QVERIFY(window);
0720     QCOMPARE(workspace()->activeWindow(), window);
0721     QCOMPARE(window->frameGeometry(), QRect(0, 0, 100, 50));
0722     QCOMPARE(window->quickTileMode(), QuickTileMode(QuickTileFlag::None));
0723 
0724     // We have to receive a configure event when the window becomes active.
0725     QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
0726     QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
0727     QVERIFY(surfaceConfigureRequestedSpy.wait());
0728     QCOMPARE(surfaceConfigureRequestedSpy.count(), 1);
0729 
0730     QFETCH(QStringList, shortcutList);
0731     QFETCH(QRect, expectedGeometry);
0732 
0733     const int numberOfQuickTileActions = shortcutList.count();
0734 
0735     if (numberOfQuickTileActions > 1) {
0736         QTest::qWait(1001);
0737     }
0738 
0739     for (QString shortcut : shortcutList) {
0740         // invoke global shortcut through dbus
0741         auto msg = QDBusMessage::createMethodCall(
0742             QStringLiteral("org.kde.kglobalaccel"),
0743             QStringLiteral("/component/kwin"),
0744             QStringLiteral("org.kde.kglobalaccel.Component"),
0745             QStringLiteral("invokeShortcut"));
0746         msg.setArguments(QList<QVariant>{shortcut});
0747         QDBusConnection::sessionBus().asyncCall(msg);
0748     }
0749 
0750     QSignalSpy quickTileChangedSpy(window, &Window::quickTileModeChanged);
0751     QTRY_COMPARE(quickTileChangedSpy.count(), numberOfQuickTileActions);
0752     // at this point the geometry did not yet change
0753     QCOMPARE(window->frameGeometry(), QRect(0, 0, 100, 50));
0754     // but quick tile mode already changed
0755     QTEST(window->quickTileMode(), "expectedMode");
0756 
0757     // but we got requested a new geometry
0758     QVERIFY(surfaceConfigureRequestedSpy.wait());
0759     QCOMPARE(surfaceConfigureRequestedSpy.count(), 2);
0760     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), expectedGeometry.size());
0761 
0762     // attach a new image
0763     QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
0764     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
0765     Test::render(surface.get(), expectedGeometry.size(), Qt::red);
0766 
0767     QVERIFY(frameGeometryChangedSpy.wait());
0768     QEXPECT_FAIL("maximize", "Geometry changed called twice for maximize", Continue);
0769     QCOMPARE(frameGeometryChangedSpy.count(), 1);
0770     QCOMPARE(window->frameGeometry(), expectedGeometry);
0771 }
0772 
0773 void QuickTilingTest::testScript_data()
0774 {
0775     QTest::addColumn<QString>("action");
0776     QTest::addColumn<QuickTileMode>("expectedMode");
0777     QTest::addColumn<QRect>("expectedGeometry");
0778 
0779 #define FLAG(name) QuickTileMode(QuickTileFlag::name)
0780     QTest::newRow("top") << QStringLiteral("Top") << FLAG(Top) << QRect(0, 0, 1280, 512);
0781     QTest::newRow("bottom") << QStringLiteral("Bottom") << FLAG(Bottom) << QRect(0, 512, 1280, 512);
0782     QTest::newRow("top right") << QStringLiteral("TopRight") << (FLAG(Top) | FLAG(Right)) << QRect(640, 0, 640, 512);
0783     QTest::newRow("top left") << QStringLiteral("TopLeft") << (FLAG(Top) | FLAG(Left)) << QRect(0, 0, 640, 512);
0784     QTest::newRow("bottom right") << QStringLiteral("BottomRight") << (FLAG(Bottom) | FLAG(Right)) << QRect(640, 512, 640, 512);
0785     QTest::newRow("bottom left") << QStringLiteral("BottomLeft") << (FLAG(Bottom) | FLAG(Left)) << QRect(0, 512, 640, 512);
0786     QTest::newRow("left") << QStringLiteral("Left") << FLAG(Left) << QRect(0, 0, 640, 1024);
0787     QTest::newRow("right") << QStringLiteral("Right") << FLAG(Right) << QRect(640, 0, 640, 1024);
0788 #undef FLAG
0789 }
0790 
0791 void QuickTilingTest::testScript()
0792 {
0793     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
0794     QVERIFY(surface != nullptr);
0795     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
0796     QVERIFY(shellSurface != nullptr);
0797 
0798     // Map the window.
0799     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
0800     QVERIFY(window);
0801     QCOMPARE(workspace()->activeWindow(), window);
0802     QCOMPARE(window->frameGeometry(), QRect(0, 0, 100, 50));
0803     QCOMPARE(window->quickTileMode(), QuickTileMode(QuickTileFlag::None));
0804 
0805     // We have to receive a configure event upon the window becoming active.
0806     QSignalSpy toplevelConfigureRequestedSpy(shellSurface.get(), &Test::XdgToplevel::configureRequested);
0807     QSignalSpy surfaceConfigureRequestedSpy(shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
0808     QVERIFY(surfaceConfigureRequestedSpy.wait());
0809     QCOMPARE(surfaceConfigureRequestedSpy.count(), 1);
0810 
0811     QSignalSpy quickTileChangedSpy(window, &Window::quickTileModeChanged);
0812     QSignalSpy frameGeometryChangedSpy(window, &Window::frameGeometryChanged);
0813 
0814     QVERIFY(Scripting::self());
0815     QTemporaryFile tmpFile;
0816     QVERIFY(tmpFile.open());
0817     QTextStream out(&tmpFile);
0818 
0819     QFETCH(QString, action);
0820     out << "workspace.slotWindowQuickTile" << action << "()";
0821     out.flush();
0822 
0823     QFETCH(QuickTileMode, expectedMode);
0824     QFETCH(QRect, expectedGeometry);
0825 
0826     const int id = Scripting::self()->loadScript(tmpFile.fileName());
0827     QVERIFY(id != -1);
0828     QVERIFY(Scripting::self()->isScriptLoaded(tmpFile.fileName()));
0829     auto s = Scripting::self()->findScript(tmpFile.fileName());
0830     QVERIFY(s);
0831     QSignalSpy runningChangedSpy(s, &AbstractScript::runningChanged);
0832     s->run();
0833 
0834     QVERIFY(quickTileChangedSpy.wait());
0835     QCOMPARE(quickTileChangedSpy.count(), 1);
0836 
0837     QCOMPARE(runningChangedSpy.count(), 1);
0838     QCOMPARE(runningChangedSpy.first().first().toBool(), true);
0839 
0840     // at this point the geometry did not yet change
0841     QCOMPARE(window->frameGeometry(), QRect(0, 0, 100, 50));
0842     // but quick tile mode already changed
0843     QCOMPARE(window->quickTileMode(), expectedMode);
0844 
0845     // but we got requested a new geometry
0846     QVERIFY(surfaceConfigureRequestedSpy.wait());
0847     QCOMPARE(surfaceConfigureRequestedSpy.count(), 2);
0848     QCOMPARE(toplevelConfigureRequestedSpy.last().at(0).toSize(), expectedGeometry.size());
0849 
0850     // attach a new image
0851     shellSurface->xdgSurface()->ack_configure(surfaceConfigureRequestedSpy.last().at(0).value<quint32>());
0852     Test::render(surface.get(), expectedGeometry.size(), Qt::red);
0853 
0854     QVERIFY(frameGeometryChangedSpy.wait());
0855     QEXPECT_FAIL("maximize", "Geometry changed called twice for maximize", Continue);
0856     QCOMPARE(frameGeometryChangedSpy.count(), 1);
0857     QCOMPARE(window->frameGeometry(), expectedGeometry);
0858 }
0859 
0860 }
0861 
0862 WAYLANDTEST_MAIN(KWin::QuickTilingTest)
0863 #include "quick_tiling_test.moc"