File indexing completed on 2025-03-23 13:48:04
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"