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

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2017 Martin Flöser <mgraesslin@kde.org>
0006     SPDX-FileCopyrightText: 2019 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
0007     SPDX-FileCopyrightText: 2022 Ismael Asensio <isma.af@gmail.com>
0008 
0009     SPDX-License-Identifier: GPL-2.0-or-later
0010 */
0011 
0012 #include "kwin_wayland_test.h"
0013 
0014 #include "core/output.h"
0015 #include "core/outputconfiguration.h"
0016 #include "cursor.h"
0017 #include "rules.h"
0018 #include "virtualdesktops.h"
0019 #include "wayland_server.h"
0020 #include "window.h"
0021 #include "workspace.h"
0022 
0023 #include <KWayland/Client/surface.h>
0024 
0025 #include <linux/input.h>
0026 
0027 using namespace KWin;
0028 
0029 static const QString s_socketName = QStringLiteral("wayland_test_kwin_xdgshellwindow_rules-0");
0030 
0031 class TestXdgShellWindowRules : public QObject
0032 {
0033     Q_OBJECT
0034 
0035     enum ClientFlag {
0036         None = 0,
0037         ClientShouldBeInactive = 1 << 0, // Window should be inactive. Used on Minimize tests
0038         ServerSideDecoration = 1 << 1, // Create window with server side decoration. Used on noBorder tests
0039         ReturnAfterSurfaceConfiguration = 1 << 2, // Do not create the window now, but return after surface configuration.
0040     };
0041     Q_DECLARE_FLAGS(ClientFlags, ClientFlag)
0042 
0043 private Q_SLOTS:
0044     void initTestCase();
0045     void init();
0046     void cleanup();
0047 
0048     void testPositionDontAffect();
0049     void testPositionApply();
0050     void testPositionRemember();
0051     void testPositionForce();
0052     void testPositionApplyNow();
0053     void testPositionForceTemporarily();
0054 
0055     void testSizeDontAffect();
0056     void testSizeApply();
0057     void testSizeRemember();
0058     void testSizeForce();
0059     void testSizeApplyNow();
0060     void testSizeForceTemporarily();
0061 
0062     void testMaximizeDontAffect();
0063     void testMaximizeApply();
0064     void testMaximizeRemember();
0065     void testMaximizeForce();
0066     void testMaximizeApplyNow();
0067     void testMaximizeForceTemporarily();
0068 
0069     void testDesktopsDontAffect();
0070     void testDesktopsApply();
0071     void testDesktopsRemember();
0072     void testDesktopsForce();
0073     void testDesktopsApplyNow();
0074     void testDesktopsForceTemporarily();
0075 
0076     void testMinimizeDontAffect();
0077     void testMinimizeApply();
0078     void testMinimizeRemember();
0079     void testMinimizeForce();
0080     void testMinimizeApplyNow();
0081     void testMinimizeForceTemporarily();
0082 
0083     void testSkipTaskbarDontAffect();
0084     void testSkipTaskbarApply();
0085     void testSkipTaskbarRemember();
0086     void testSkipTaskbarForce();
0087     void testSkipTaskbarApplyNow();
0088     void testSkipTaskbarForceTemporarily();
0089 
0090     void testSkipPagerDontAffect();
0091     void testSkipPagerApply();
0092     void testSkipPagerRemember();
0093     void testSkipPagerForce();
0094     void testSkipPagerApplyNow();
0095     void testSkipPagerForceTemporarily();
0096 
0097     void testSkipSwitcherDontAffect();
0098     void testSkipSwitcherApply();
0099     void testSkipSwitcherRemember();
0100     void testSkipSwitcherForce();
0101     void testSkipSwitcherApplyNow();
0102     void testSkipSwitcherForceTemporarily();
0103 
0104     void testKeepAboveDontAffect();
0105     void testKeepAboveApply();
0106     void testKeepAboveRemember();
0107     void testKeepAboveForce();
0108     void testKeepAboveApplyNow();
0109     void testKeepAboveForceTemporarily();
0110 
0111     void testKeepBelowDontAffect();
0112     void testKeepBelowApply();
0113     void testKeepBelowRemember();
0114     void testKeepBelowForce();
0115     void testKeepBelowApplyNow();
0116     void testKeepBelowForceTemporarily();
0117 
0118     void testShortcutDontAffect();
0119     void testShortcutApply();
0120     void testShortcutRemember();
0121     void testShortcutForce();
0122     void testShortcutApplyNow();
0123     void testShortcutForceTemporarily();
0124 
0125     void testDesktopFileDontAffect();
0126     void testDesktopFileApply();
0127     void testDesktopFileRemember();
0128     void testDesktopFileForce();
0129     void testDesktopFileApplyNow();
0130     void testDesktopFileForceTemporarily();
0131 
0132     void testActiveOpacityDontAffect();
0133     void testActiveOpacityForce();
0134     void testActiveOpacityForceTemporarily();
0135 
0136     void testInactiveOpacityDontAffect();
0137     void testInactiveOpacityForce();
0138     void testInactiveOpacityForceTemporarily();
0139 
0140     void testNoBorderDontAffect();
0141     void testNoBorderApply();
0142     void testNoBorderRemember();
0143     void testNoBorderForce();
0144     void testNoBorderApplyNow();
0145     void testNoBorderForceTemporarily();
0146 
0147     void testScreenDontAffect();
0148     void testScreenApply();
0149     void testScreenRemember();
0150     void testScreenForce();
0151     void testScreenApplyNow();
0152     void testScreenForceTemporarily();
0153 
0154     void testLayerDontAffect();
0155     void testLayerForce();
0156     void testLayerForceTemporarily();
0157 
0158     void testCloseableDontAffect();
0159     void testCloseableForce();
0160     void testCloseableForceTemporarily();
0161 
0162     void testMatchAfterNameChange();
0163 
0164 private:
0165     void createTestWindow(ClientFlags flags = None);
0166     void mapClientToSurface(QSize clientSize, ClientFlags flags = None);
0167     void destroyTestWindow();
0168 
0169     template<typename T>
0170     void setWindowRule(const QString &property, const T &value, int policy);
0171 
0172 private:
0173     KSharedConfig::Ptr m_config;
0174 
0175     Window *m_window;
0176     std::unique_ptr<KWayland::Client::Surface> m_surface;
0177     std::unique_ptr<Test::XdgToplevel> m_shellSurface;
0178 
0179     std::unique_ptr<QSignalSpy> m_toplevelConfigureRequestedSpy;
0180     std::unique_ptr<QSignalSpy> m_surfaceConfigureRequestedSpy;
0181 };
0182 
0183 void TestXdgShellWindowRules::initTestCase()
0184 {
0185     qRegisterMetaType<KWin::Window *>();
0186 
0187     QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
0188     QVERIFY(waylandServer()->init(s_socketName));
0189     Test::setOutputConfig({
0190         QRect(0, 0, 1280, 1024),
0191         QRect(1280, 0, 1280, 1024),
0192     });
0193 
0194     kwinApp()->start();
0195     QVERIFY(applicationStartedSpy.wait());
0196     const auto outputs = workspace()->outputs();
0197     QCOMPARE(outputs.count(), 2);
0198     QCOMPARE(outputs[0]->geometry(), QRect(0, 0, 1280, 1024));
0199     QCOMPARE(outputs[1]->geometry(), QRect(1280, 0, 1280, 1024));
0200 
0201     m_config = KSharedConfig::openConfig(QStringLiteral("kwinrulesrc"), KConfig::SimpleConfig);
0202     workspace()->rulebook()->setConfig(m_config);
0203 }
0204 
0205 void TestXdgShellWindowRules::init()
0206 {
0207     VirtualDesktopManager::self()->setCurrent(VirtualDesktopManager::self()->desktops().first());
0208     QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::XdgDecorationV1));
0209 
0210     workspace()->setActiveOutput(QPoint(640, 512));
0211 }
0212 
0213 void TestXdgShellWindowRules::cleanup()
0214 {
0215     if (m_shellSurface) {
0216         destroyTestWindow();
0217     }
0218 
0219     Test::destroyWaylandConnection();
0220 
0221     // Wipe the window rule config clean.
0222     for (const QString &group : m_config->groupList()) {
0223         m_config->deleteGroup(group);
0224     }
0225     workspace()->slotReconfigure();
0226 
0227     // Restore virtual desktops to the initial state.
0228     VirtualDesktopManager::self()->setCount(1);
0229     QCOMPARE(VirtualDesktopManager::self()->count(), 1u);
0230 }
0231 
0232 void TestXdgShellWindowRules::createTestWindow(ClientFlags flags)
0233 {
0234     // Apply flags for special windows and rules
0235     const bool createClient = !(flags & ReturnAfterSurfaceConfiguration);
0236     const auto decorationMode = (flags & ServerSideDecoration) ? Test::XdgToplevelDecorationV1::mode_server_side
0237                                                                : Test::XdgToplevelDecorationV1::mode_client_side;
0238     // Create an xdg surface.
0239     m_surface = Test::createSurface();
0240     m_shellSurface.reset(Test::createXdgToplevelSurface(m_surface.get(), Test::CreationSetup::CreateOnly, m_surface.get()));
0241     Test::XdgToplevelDecorationV1 *decoration = Test::createXdgToplevelDecorationV1(m_shellSurface.get(), m_shellSurface.get());
0242 
0243     // Add signal watchers
0244     m_toplevelConfigureRequestedSpy = std::make_unique<QSignalSpy>(m_shellSurface.get(), &Test::XdgToplevel::configureRequested);
0245     m_surfaceConfigureRequestedSpy = std::make_unique<QSignalSpy>(m_shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
0246 
0247     m_shellSurface->set_app_id(QStringLiteral("org.kde.foo"));
0248     decoration->set_mode(decorationMode);
0249 
0250     // Wait for the initial configure event
0251     m_surface->commit(KWayland::Client::Surface::CommitFlag::None);
0252     QVERIFY(m_surfaceConfigureRequestedSpy->wait());
0253 
0254     if (createClient) {
0255         mapClientToSurface(QSize(100, 50), flags);
0256     }
0257 }
0258 
0259 void TestXdgShellWindowRules::mapClientToSurface(QSize clientSize, ClientFlags flags)
0260 {
0261     const bool clientShouldBeActive = !(flags & ClientShouldBeInactive);
0262 
0263     QVERIFY(m_surface != nullptr);
0264     QVERIFY(m_shellSurface != nullptr);
0265     QVERIFY(m_surfaceConfigureRequestedSpy != nullptr);
0266 
0267     // Draw content of the surface.
0268     m_shellSurface->xdgSurface()->ack_configure(m_surfaceConfigureRequestedSpy->last().at(0).value<quint32>());
0269 
0270     // Create the window
0271     m_window = Test::renderAndWaitForShown(m_surface.get(), clientSize, Qt::blue);
0272     QVERIFY(m_window);
0273     QCOMPARE(m_window->isActive(), clientShouldBeActive);
0274 }
0275 
0276 void TestXdgShellWindowRules::destroyTestWindow()
0277 {
0278     m_surfaceConfigureRequestedSpy.reset();
0279     m_toplevelConfigureRequestedSpy.reset();
0280     m_shellSurface.reset();
0281     m_surface.reset();
0282     QVERIFY(Test::waitForWindowClosed(m_window));
0283 }
0284 
0285 template<typename T>
0286 void TestXdgShellWindowRules::setWindowRule(const QString &property, const T &value, int policy)
0287 {
0288     // Initialize RuleBook with the test rule.
0289     m_config->group(QStringLiteral("General")).writeEntry("count", 1);
0290     KConfigGroup group = m_config->group(QStringLiteral("1"));
0291 
0292     group.writeEntry(property, value);
0293     group.writeEntry(QStringLiteral("%1rule").arg(property), policy);
0294 
0295     group.writeEntry("wmclass", "org.kde.foo");
0296     group.writeEntry("wmclasscomplete", false);
0297     group.writeEntry("wmclassmatch", int(Rules::ExactMatch));
0298     group.sync();
0299 
0300     workspace()->slotReconfigure();
0301 }
0302 
0303 void TestXdgShellWindowRules::testPositionDontAffect()
0304 {
0305     setWindowRule("position", QPoint(42, 42), int(Rules::DontAffect));
0306 
0307     createTestWindow();
0308 
0309     // The position of the window should not be affected by the rule. The default
0310     // placement policy will put the window in the top-left corner of the screen.
0311     QVERIFY(m_window->isMovable());
0312     QVERIFY(m_window->isMovableAcrossScreens());
0313     QCOMPARE(m_window->pos(), QPoint(0, 0));
0314 
0315     destroyTestWindow();
0316 }
0317 
0318 void TestXdgShellWindowRules::testPositionApply()
0319 {
0320     setWindowRule("position", QPoint(42, 42), int(Rules::Apply));
0321 
0322     createTestWindow();
0323 
0324     // The window should be moved to the position specified by the rule.
0325     QVERIFY(m_window->isMovable());
0326     QVERIFY(m_window->isMovableAcrossScreens());
0327     QCOMPARE(m_window->pos(), QPoint(42, 42));
0328 
0329     // One should still be able to move the window around.
0330     QSignalSpy interactiveMoveResizeStartedSpy(m_window, &Window::interactiveMoveResizeStarted);
0331     QSignalSpy interactiveMoveResizeSteppedSpy(m_window, &Window::interactiveMoveResizeStepped);
0332     QSignalSpy interactiveMoveResizeFinishedSpy(m_window, &Window::interactiveMoveResizeFinished);
0333 
0334     QCOMPARE(workspace()->moveResizeWindow(), nullptr);
0335     QVERIFY(!m_window->isInteractiveMove());
0336     QVERIFY(!m_window->isInteractiveResize());
0337     workspace()->slotWindowMove();
0338     QCOMPARE(workspace()->moveResizeWindow(), m_window);
0339     QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1);
0340     QVERIFY(m_window->isInteractiveMove());
0341     QVERIFY(!m_window->isInteractiveResize());
0342 
0343     const QPointF cursorPos = KWin::Cursors::self()->mouse()->pos();
0344     m_window->keyPressEvent(Qt::Key_Right);
0345     m_window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos());
0346     QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 0));
0347     QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
0348     QCOMPARE(m_window->pos(), QPoint(50, 42));
0349 
0350     m_window->keyPressEvent(Qt::Key_Enter);
0351     QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 1);
0352     QCOMPARE(workspace()->moveResizeWindow(), nullptr);
0353     QVERIFY(!m_window->isInteractiveMove());
0354     QVERIFY(!m_window->isInteractiveResize());
0355     QCOMPARE(m_window->pos(), QPoint(50, 42));
0356 
0357     // The rule should be applied again if the window appears after it's been closed.
0358     destroyTestWindow();
0359     createTestWindow();
0360 
0361     QVERIFY(m_window->isMovable());
0362     QVERIFY(m_window->isMovableAcrossScreens());
0363     QCOMPARE(m_window->pos(), QPoint(42, 42));
0364 
0365     destroyTestWindow();
0366 }
0367 
0368 void TestXdgShellWindowRules::testPositionRemember()
0369 {
0370     setWindowRule("position", QPoint(42, 42), int(Rules::Remember));
0371     createTestWindow();
0372 
0373     // The window should be moved to the position specified by the rule.
0374     QVERIFY(m_window->isMovable());
0375     QVERIFY(m_window->isMovableAcrossScreens());
0376     QCOMPARE(m_window->pos(), QPoint(42, 42));
0377 
0378     // One should still be able to move the window around.
0379     QSignalSpy interactiveMoveResizeStartedSpy(m_window, &Window::interactiveMoveResizeStarted);
0380     QSignalSpy interactiveMoveResizeSteppedSpy(m_window, &Window::interactiveMoveResizeStepped);
0381     QSignalSpy interactiveMoveResizeFinishedSpy(m_window, &Window::interactiveMoveResizeFinished);
0382 
0383     QCOMPARE(workspace()->moveResizeWindow(), nullptr);
0384     QVERIFY(!m_window->isInteractiveMove());
0385     QVERIFY(!m_window->isInteractiveResize());
0386     workspace()->slotWindowMove();
0387     QCOMPARE(workspace()->moveResizeWindow(), m_window);
0388     QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1);
0389     QVERIFY(m_window->isInteractiveMove());
0390     QVERIFY(!m_window->isInteractiveResize());
0391 
0392     const QPointF cursorPos = KWin::Cursors::self()->mouse()->pos();
0393     m_window->keyPressEvent(Qt::Key_Right);
0394     m_window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos());
0395     QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 0));
0396     QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
0397     QCOMPARE(m_window->pos(), QPoint(50, 42));
0398 
0399     m_window->keyPressEvent(Qt::Key_Enter);
0400     QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 1);
0401     QCOMPARE(workspace()->moveResizeWindow(), nullptr);
0402     QVERIFY(!m_window->isInteractiveMove());
0403     QVERIFY(!m_window->isInteractiveResize());
0404     QCOMPARE(m_window->pos(), QPoint(50, 42));
0405 
0406     // The window should be placed at the last know position if we reopen it.
0407     destroyTestWindow();
0408     createTestWindow();
0409 
0410     QVERIFY(m_window->isMovable());
0411     QVERIFY(m_window->isMovableAcrossScreens());
0412     QCOMPARE(m_window->pos(), QPoint(50, 42));
0413 
0414     destroyTestWindow();
0415 }
0416 
0417 void TestXdgShellWindowRules::testPositionForce()
0418 {
0419     setWindowRule("position", QPoint(42, 42), int(Rules::Force));
0420 
0421     createTestWindow();
0422 
0423     // The window should be moved to the position specified by the rule.
0424     QVERIFY(!m_window->isMovable());
0425     QVERIFY(!m_window->isMovableAcrossScreens());
0426     QCOMPARE(m_window->pos(), QPoint(42, 42));
0427 
0428     // User should not be able to move the window.
0429     QSignalSpy interactiveMoveResizeStartedSpy(m_window, &Window::interactiveMoveResizeStarted);
0430     QCOMPARE(workspace()->moveResizeWindow(), nullptr);
0431     QVERIFY(!m_window->isInteractiveMove());
0432     QVERIFY(!m_window->isInteractiveResize());
0433     workspace()->slotWindowMove();
0434     QCOMPARE(workspace()->moveResizeWindow(), nullptr);
0435     QCOMPARE(interactiveMoveResizeStartedSpy.count(), 0);
0436     QVERIFY(!m_window->isInteractiveMove());
0437     QVERIFY(!m_window->isInteractiveResize());
0438 
0439     // The position should still be forced if we reopen the window.
0440     destroyTestWindow();
0441     createTestWindow();
0442 
0443     QVERIFY(!m_window->isMovable());
0444     QVERIFY(!m_window->isMovableAcrossScreens());
0445     QCOMPARE(m_window->pos(), QPoint(42, 42));
0446 
0447     destroyTestWindow();
0448 }
0449 
0450 void TestXdgShellWindowRules::testPositionApplyNow()
0451 {
0452     createTestWindow();
0453 
0454     // The position of the window isn't set by any rule, thus the default placement
0455     // policy will try to put the window in the top-left corner of the screen.
0456     QVERIFY(m_window->isMovable());
0457     QVERIFY(m_window->isMovableAcrossScreens());
0458     QCOMPARE(m_window->pos(), QPoint(0, 0));
0459 
0460     QSignalSpy frameGeometryChangedSpy(m_window, &Window::frameGeometryChanged);
0461 
0462     setWindowRule("position", QPoint(42, 42), int(Rules::ApplyNow));
0463 
0464     // The window should be moved to the position specified by the rule.
0465     QCOMPARE(frameGeometryChangedSpy.count(), 1);
0466     QCOMPARE(m_window->pos(), QPoint(42, 42));
0467 
0468     // We still have to be able to move the window around.
0469     QVERIFY(m_window->isMovable());
0470     QVERIFY(m_window->isMovableAcrossScreens());
0471     QSignalSpy interactiveMoveResizeStartedSpy(m_window, &Window::interactiveMoveResizeStarted);
0472     QSignalSpy interactiveMoveResizeSteppedSpy(m_window, &Window::interactiveMoveResizeStepped);
0473     QSignalSpy interactiveMoveResizeFinishedSpy(m_window, &Window::interactiveMoveResizeFinished);
0474 
0475     QCOMPARE(workspace()->moveResizeWindow(), nullptr);
0476     QVERIFY(!m_window->isInteractiveMove());
0477     QVERIFY(!m_window->isInteractiveResize());
0478     workspace()->slotWindowMove();
0479     QCOMPARE(workspace()->moveResizeWindow(), m_window);
0480     QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1);
0481     QVERIFY(m_window->isInteractiveMove());
0482     QVERIFY(!m_window->isInteractiveResize());
0483 
0484     const QPointF cursorPos = KWin::Cursors::self()->mouse()->pos();
0485     m_window->keyPressEvent(Qt::Key_Right);
0486     m_window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos());
0487     QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 0));
0488     QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
0489     QCOMPARE(m_window->pos(), QPoint(50, 42));
0490 
0491     m_window->keyPressEvent(Qt::Key_Enter);
0492     QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 1);
0493     QCOMPARE(workspace()->moveResizeWindow(), nullptr);
0494     QVERIFY(!m_window->isInteractiveMove());
0495     QVERIFY(!m_window->isInteractiveResize());
0496     QCOMPARE(m_window->pos(), QPoint(50, 42));
0497 
0498     // The rule should not be applied again.
0499     m_window->evaluateWindowRules();
0500     QCOMPARE(m_window->pos(), QPoint(50, 42));
0501 
0502     destroyTestWindow();
0503 }
0504 
0505 void TestXdgShellWindowRules::testPositionForceTemporarily()
0506 {
0507     setWindowRule("position", QPoint(42, 42), int(Rules::ForceTemporarily));
0508 
0509     createTestWindow();
0510 
0511     // The window should be moved to the position specified by the rule.
0512     QVERIFY(!m_window->isMovable());
0513     QVERIFY(!m_window->isMovableAcrossScreens());
0514     QCOMPARE(m_window->pos(), QPoint(42, 42));
0515 
0516     // User should not be able to move the window.
0517     QSignalSpy interactiveMoveResizeStartedSpy(m_window, &Window::interactiveMoveResizeStarted);
0518     QCOMPARE(workspace()->moveResizeWindow(), nullptr);
0519     QVERIFY(!m_window->isInteractiveMove());
0520     QVERIFY(!m_window->isInteractiveResize());
0521     workspace()->slotWindowMove();
0522     QCOMPARE(workspace()->moveResizeWindow(), nullptr);
0523     QCOMPARE(interactiveMoveResizeStartedSpy.count(), 0);
0524     QVERIFY(!m_window->isInteractiveMove());
0525     QVERIFY(!m_window->isInteractiveResize());
0526 
0527     // The rule should be discarded if we close the window.
0528     destroyTestWindow();
0529     createTestWindow();
0530 
0531     QVERIFY(m_window->isMovable());
0532     QVERIFY(m_window->isMovableAcrossScreens());
0533     QCOMPARE(m_window->pos(), QPoint(0, 0));
0534 
0535     destroyTestWindow();
0536 }
0537 
0538 void TestXdgShellWindowRules::testSizeDontAffect()
0539 {
0540     setWindowRule("size", QSize(480, 640), int(Rules::DontAffect));
0541 
0542     createTestWindow(ReturnAfterSurfaceConfiguration);
0543 
0544     // The window size shouldn't be enforced by the rule.
0545     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
0546     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
0547     QCOMPARE(m_toplevelConfigureRequestedSpy->last().first().toSize(), QSize(0, 0));
0548 
0549     // Map the window.
0550     mapClientToSurface(QSize(100, 50));
0551     QVERIFY(m_window->isResizable());
0552     QCOMPARE(m_window->size(), QSize(100, 50));
0553 
0554     // We should receive a configure event when the window becomes active.
0555     QVERIFY(m_surfaceConfigureRequestedSpy->wait());
0556     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
0557     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
0558 
0559     destroyTestWindow();
0560 }
0561 
0562 void TestXdgShellWindowRules::testSizeApply()
0563 {
0564     setWindowRule("size", QSize(480, 640), int(Rules::Apply));
0565 
0566     createTestWindow(ReturnAfterSurfaceConfiguration);
0567 
0568     // The initial configure event should contain size hint set by the rule.
0569     Test::XdgToplevel::States states;
0570     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
0571     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
0572     QCOMPARE(m_toplevelConfigureRequestedSpy->last().at(0).toSize(), QSize(480, 640));
0573     states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
0574     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Activated));
0575     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Resizing));
0576 
0577     // Map the window.
0578     mapClientToSurface(QSize(480, 640));
0579     QVERIFY(m_window->isResizable());
0580     QCOMPARE(m_window->size(), QSize(480, 640));
0581 
0582     // We should receive a configure event when the window becomes active.
0583     QVERIFY(m_surfaceConfigureRequestedSpy->wait());
0584     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
0585     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
0586     states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
0587     QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
0588     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Resizing));
0589 
0590     // One still should be able to resize the window.
0591     QSignalSpy frameGeometryChangedSpy(m_window, &Window::frameGeometryChanged);
0592     QSignalSpy interactiveMoveResizeStartedSpy(m_window, &Window::interactiveMoveResizeStarted);
0593     QSignalSpy interactiveMoveResizeSteppedSpy(m_window, &Window::interactiveMoveResizeStepped);
0594     QSignalSpy interactiveMoveResizeFinishedSpy(m_window, &Window::interactiveMoveResizeFinished);
0595 
0596     QCOMPARE(workspace()->moveResizeWindow(), nullptr);
0597     QVERIFY(!m_window->isInteractiveMove());
0598     QVERIFY(!m_window->isInteractiveResize());
0599     workspace()->slotWindowResize();
0600     QCOMPARE(workspace()->moveResizeWindow(), m_window);
0601     QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1);
0602     QVERIFY(!m_window->isInteractiveMove());
0603     QVERIFY(m_window->isInteractiveResize());
0604     QVERIFY(m_surfaceConfigureRequestedSpy->wait());
0605     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 3);
0606     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 3);
0607     states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
0608     QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
0609     QVERIFY(states.testFlag(Test::XdgToplevel::State::Resizing));
0610     m_shellSurface->xdgSurface()->ack_configure(m_surfaceConfigureRequestedSpy->last().at(0).value<quint32>());
0611 
0612     const QPointF cursorPos = KWin::Cursors::self()->mouse()->pos();
0613     m_window->keyPressEvent(Qt::Key_Right);
0614     m_window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos());
0615     QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 0));
0616     QVERIFY(m_surfaceConfigureRequestedSpy->wait());
0617     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 4);
0618     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 4);
0619     states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
0620     QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
0621     QVERIFY(states.testFlag(Test::XdgToplevel::State::Resizing));
0622     QCOMPARE(m_toplevelConfigureRequestedSpy->last().at(0).toSize(), QSize(488, 640));
0623     QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
0624     m_shellSurface->xdgSurface()->ack_configure(m_surfaceConfigureRequestedSpy->last().at(0).value<quint32>());
0625     Test::render(m_surface.get(), QSize(488, 640), Qt::blue);
0626     QVERIFY(frameGeometryChangedSpy.wait());
0627     QCOMPARE(m_window->size(), QSize(488, 640));
0628     QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
0629 
0630     m_window->keyPressEvent(Qt::Key_Enter);
0631     QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 1);
0632     QCOMPARE(workspace()->moveResizeWindow(), nullptr);
0633     QVERIFY(!m_window->isInteractiveMove());
0634     QVERIFY(!m_window->isInteractiveResize());
0635 
0636     QVERIFY(m_surfaceConfigureRequestedSpy->wait());
0637     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 5);
0638     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 5);
0639 
0640     // The rule should be applied again if the window appears after it's been closed.
0641     destroyTestWindow();
0642     createTestWindow(ReturnAfterSurfaceConfiguration);
0643 
0644     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
0645     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
0646     QCOMPARE(m_toplevelConfigureRequestedSpy->last().first().toSize(), QSize(480, 640));
0647 
0648     mapClientToSurface(QSize(480, 640));
0649     QVERIFY(m_window->isResizable());
0650     QCOMPARE(m_window->size(), QSize(480, 640));
0651 
0652     QVERIFY(m_surfaceConfigureRequestedSpy->wait());
0653     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
0654     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
0655 
0656     destroyTestWindow();
0657 }
0658 
0659 void TestXdgShellWindowRules::testSizeRemember()
0660 {
0661     setWindowRule("size", QSize(480, 640), int(Rules::Remember));
0662 
0663     createTestWindow(ReturnAfterSurfaceConfiguration);
0664 
0665     // The initial configure event should contain size hint set by the rule.
0666     Test::XdgToplevel::States states;
0667     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
0668     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
0669     QCOMPARE(m_toplevelConfigureRequestedSpy->last().first().toSize(), QSize(480, 640));
0670     states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
0671     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Activated));
0672     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Resizing));
0673 
0674     // Map the window.
0675     mapClientToSurface(QSize(480, 640));
0676     QVERIFY(m_window->isResizable());
0677     QCOMPARE(m_window->size(), QSize(480, 640));
0678 
0679     // We should receive a configure event when the window becomes active.
0680     QVERIFY(m_surfaceConfigureRequestedSpy->wait());
0681     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
0682     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
0683     states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
0684     QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
0685     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Resizing));
0686 
0687     // One should still be able to resize the window.
0688     QSignalSpy frameGeometryChangedSpy(m_window, &Window::frameGeometryChanged);
0689     QSignalSpy interactiveMoveResizeStartedSpy(m_window, &Window::interactiveMoveResizeStarted);
0690     QSignalSpy interactiveMoveResizeSteppedSpy(m_window, &Window::interactiveMoveResizeStepped);
0691     QSignalSpy interactiveMoveResizeFinishedSpy(m_window, &Window::interactiveMoveResizeFinished);
0692 
0693     QCOMPARE(workspace()->moveResizeWindow(), nullptr);
0694     QVERIFY(!m_window->isInteractiveMove());
0695     QVERIFY(!m_window->isInteractiveResize());
0696     workspace()->slotWindowResize();
0697     QCOMPARE(workspace()->moveResizeWindow(), m_window);
0698     QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1);
0699     QVERIFY(!m_window->isInteractiveMove());
0700     QVERIFY(m_window->isInteractiveResize());
0701     QVERIFY(m_surfaceConfigureRequestedSpy->wait());
0702     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 3);
0703     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 3);
0704     states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
0705     QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
0706     QVERIFY(states.testFlag(Test::XdgToplevel::State::Resizing));
0707     m_shellSurface->xdgSurface()->ack_configure(m_surfaceConfigureRequestedSpy->last().at(0).value<quint32>());
0708 
0709     const QPointF cursorPos = KWin::Cursors::self()->mouse()->pos();
0710     m_window->keyPressEvent(Qt::Key_Right);
0711     m_window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos());
0712     QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 0));
0713     QVERIFY(m_surfaceConfigureRequestedSpy->wait());
0714     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 4);
0715     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 4);
0716     states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
0717     QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
0718     QVERIFY(states.testFlag(Test::XdgToplevel::State::Resizing));
0719     QCOMPARE(m_toplevelConfigureRequestedSpy->last().at(0).toSize(), QSize(488, 640));
0720     QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
0721     m_shellSurface->xdgSurface()->ack_configure(m_surfaceConfigureRequestedSpy->last().at(0).value<quint32>());
0722     Test::render(m_surface.get(), QSize(488, 640), Qt::blue);
0723     QVERIFY(frameGeometryChangedSpy.wait());
0724     QCOMPARE(m_window->size(), QSize(488, 640));
0725     QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
0726 
0727     m_window->keyPressEvent(Qt::Key_Enter);
0728     QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 1);
0729     QCOMPARE(workspace()->moveResizeWindow(), nullptr);
0730     QVERIFY(!m_window->isInteractiveMove());
0731     QVERIFY(!m_window->isInteractiveResize());
0732 
0733     QVERIFY(m_surfaceConfigureRequestedSpy->wait());
0734     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 5);
0735     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 5);
0736 
0737     // If the window appears again, it should have the last known size.
0738     destroyTestWindow();
0739     createTestWindow(ReturnAfterSurfaceConfiguration);
0740 
0741     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
0742     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
0743     QCOMPARE(m_toplevelConfigureRequestedSpy->last().first().toSize(), QSize(488, 640));
0744 
0745     mapClientToSurface(QSize(488, 640));
0746     QVERIFY(m_window->isResizable());
0747     QCOMPARE(m_window->size(), QSize(488, 640));
0748 
0749     QVERIFY(m_surfaceConfigureRequestedSpy->wait());
0750     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
0751     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
0752 
0753     destroyTestWindow();
0754 }
0755 
0756 void TestXdgShellWindowRules::testSizeForce()
0757 {
0758     setWindowRule("size", QSize(480, 640), int(Rules::Force));
0759 
0760     createTestWindow(ReturnAfterSurfaceConfiguration);
0761 
0762     // The initial configure event should contain size hint set by the rule.
0763     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
0764     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
0765     QCOMPARE(m_toplevelConfigureRequestedSpy->last().first().toSize(), QSize(480, 640));
0766 
0767     // Map the window.
0768     mapClientToSurface(QSize(480, 640));
0769     QVERIFY(!m_window->isResizable());
0770     QCOMPARE(m_window->size(), QSize(480, 640));
0771 
0772     // We should receive a configure event when the window becomes active.
0773     QVERIFY(m_surfaceConfigureRequestedSpy->wait());
0774     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
0775     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
0776 
0777     // Any attempt to resize the window should not succeed.
0778     QSignalSpy interactiveMoveResizeStartedSpy(m_window, &Window::interactiveMoveResizeStarted);
0779     QCOMPARE(workspace()->moveResizeWindow(), nullptr);
0780     QVERIFY(!m_window->isInteractiveMove());
0781     QVERIFY(!m_window->isInteractiveResize());
0782     workspace()->slotWindowResize();
0783     QCOMPARE(workspace()->moveResizeWindow(), nullptr);
0784     QCOMPARE(interactiveMoveResizeStartedSpy.count(), 0);
0785     QVERIFY(!m_window->isInteractiveMove());
0786     QVERIFY(!m_window->isInteractiveResize());
0787     QVERIFY(!m_surfaceConfigureRequestedSpy->wait(100));
0788 
0789     // If the window appears again, the size should still be forced.
0790     destroyTestWindow();
0791     createTestWindow(ReturnAfterSurfaceConfiguration);
0792 
0793     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
0794     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
0795     QCOMPARE(m_toplevelConfigureRequestedSpy->last().first().toSize(), QSize(480, 640));
0796 
0797     mapClientToSurface(QSize(480, 640));
0798     QVERIFY(!m_window->isResizable());
0799     QCOMPARE(m_window->size(), QSize(480, 640));
0800 
0801     QVERIFY(m_surfaceConfigureRequestedSpy->wait());
0802     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
0803     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
0804 
0805     destroyTestWindow();
0806 }
0807 
0808 void TestXdgShellWindowRules::testSizeApplyNow()
0809 {
0810     createTestWindow(ReturnAfterSurfaceConfiguration);
0811 
0812     // The expected surface dimensions should be set by the rule.
0813     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
0814     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
0815     QCOMPARE(m_toplevelConfigureRequestedSpy->last().first().toSize(), QSize(0, 0));
0816 
0817     // Map the window.
0818     mapClientToSurface(QSize(100, 50));
0819     QVERIFY(m_window->isResizable());
0820     QCOMPARE(m_window->size(), QSize(100, 50));
0821 
0822     // We should receive a configure event when the window becomes active.
0823     QVERIFY(m_surfaceConfigureRequestedSpy->wait());
0824     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
0825     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
0826 
0827     setWindowRule("size", QSize(480, 640), int(Rules::ApplyNow));
0828 
0829     // The compositor should send a configure event with a new size.
0830     QVERIFY(m_surfaceConfigureRequestedSpy->wait());
0831     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 3);
0832     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 3);
0833     QCOMPARE(m_toplevelConfigureRequestedSpy->last().first().toSize(), QSize(480, 640));
0834 
0835     // Draw the surface with the new size.
0836     QSignalSpy frameGeometryChangedSpy(m_window, &Window::frameGeometryChanged);
0837     m_shellSurface->xdgSurface()->ack_configure(m_surfaceConfigureRequestedSpy->last().at(0).value<quint32>());
0838     Test::render(m_surface.get(), QSize(480, 640), Qt::blue);
0839     QVERIFY(frameGeometryChangedSpy.wait());
0840     QCOMPARE(m_window->size(), QSize(480, 640));
0841     QVERIFY(!m_surfaceConfigureRequestedSpy->wait(100));
0842 
0843     // The rule should not be applied again.
0844     m_window->evaluateWindowRules();
0845     QVERIFY(!m_surfaceConfigureRequestedSpy->wait(100));
0846 
0847     destroyTestWindow();
0848 }
0849 
0850 void TestXdgShellWindowRules::testSizeForceTemporarily()
0851 {
0852     setWindowRule("size", QSize(480, 640), int(Rules::ForceTemporarily));
0853 
0854     createTestWindow(ReturnAfterSurfaceConfiguration);
0855 
0856     // The initial configure event should contain size hint set by the rule.
0857     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
0858     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
0859     QCOMPARE(m_toplevelConfigureRequestedSpy->last().first().toSize(), QSize(480, 640));
0860 
0861     // Map the window.
0862     mapClientToSurface(QSize(480, 640));
0863     QVERIFY(!m_window->isResizable());
0864     QCOMPARE(m_window->size(), QSize(480, 640));
0865 
0866     // We should receive a configure event when the window becomes active.
0867     QVERIFY(m_surfaceConfigureRequestedSpy->wait());
0868     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
0869     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
0870 
0871     // Any attempt to resize the window should not succeed.
0872     QSignalSpy interactiveMoveResizeStartedSpy(m_window, &Window::interactiveMoveResizeStarted);
0873     QCOMPARE(workspace()->moveResizeWindow(), nullptr);
0874     QVERIFY(!m_window->isInteractiveMove());
0875     QVERIFY(!m_window->isInteractiveResize());
0876     workspace()->slotWindowResize();
0877     QCOMPARE(workspace()->moveResizeWindow(), nullptr);
0878     QCOMPARE(interactiveMoveResizeStartedSpy.count(), 0);
0879     QVERIFY(!m_window->isInteractiveMove());
0880     QVERIFY(!m_window->isInteractiveResize());
0881     QVERIFY(!m_surfaceConfigureRequestedSpy->wait(100));
0882 
0883     // The rule should be discarded when the window is closed.
0884     destroyTestWindow();
0885     createTestWindow(ReturnAfterSurfaceConfiguration);
0886 
0887     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
0888     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
0889     QCOMPARE(m_toplevelConfigureRequestedSpy->last().first().toSize(), QSize(0, 0));
0890 
0891     mapClientToSurface(QSize(100, 50));
0892     QVERIFY(m_window->isResizable());
0893     QCOMPARE(m_window->size(), QSize(100, 50));
0894 
0895     QVERIFY(m_surfaceConfigureRequestedSpy->wait());
0896     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
0897     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
0898 
0899     destroyTestWindow();
0900 }
0901 
0902 void TestXdgShellWindowRules::testMaximizeDontAffect()
0903 {
0904     setWindowRule("maximizehoriz", true, int(Rules::DontAffect));
0905     setWindowRule("maximizevert", true, int(Rules::DontAffect));
0906 
0907     createTestWindow(ReturnAfterSurfaceConfiguration);
0908 
0909     // Wait for the initial configure event.
0910     Test::XdgToplevel::States states;
0911     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
0912     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
0913     QCOMPARE(m_toplevelConfigureRequestedSpy->last().at(0).toSize(), QSize(0, 0));
0914     states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
0915     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Activated));
0916     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
0917 
0918     // Map the window.
0919     mapClientToSurface(QSize(100, 50));
0920 
0921     QVERIFY(m_window->isMaximizable());
0922     QCOMPARE(m_window->maximizeMode(), MaximizeMode::MaximizeRestore);
0923     QCOMPARE(m_window->requestedMaximizeMode(), MaximizeMode::MaximizeRestore);
0924     QCOMPARE(m_window->size(), QSize(100, 50));
0925 
0926     // We should receive a configure event when the window becomes active.
0927     QVERIFY(m_surfaceConfigureRequestedSpy->wait());
0928     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
0929     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
0930     states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
0931     QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
0932     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
0933 
0934     destroyTestWindow();
0935 }
0936 
0937 void TestXdgShellWindowRules::testMaximizeApply()
0938 {
0939     setWindowRule("maximizehoriz", true, int(Rules::Apply));
0940     setWindowRule("maximizevert", true, int(Rules::Apply));
0941 
0942     createTestWindow(ReturnAfterSurfaceConfiguration);
0943 
0944     // Wait for the initial configure event.
0945     Test::XdgToplevel::States states;
0946     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
0947     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
0948     QCOMPARE(m_toplevelConfigureRequestedSpy->last().at(0).toSize(), QSize(1280, 1024));
0949     states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
0950     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Activated));
0951     QVERIFY(states.testFlag(Test::XdgToplevel::State::Maximized));
0952 
0953     // Map the window.
0954     mapClientToSurface(QSize(1280, 1024));
0955 
0956     QVERIFY(m_window->isMaximizable());
0957     QCOMPARE(m_window->maximizeMode(), MaximizeMode::MaximizeFull);
0958     QCOMPARE(m_window->requestedMaximizeMode(), MaximizeMode::MaximizeFull);
0959     QCOMPARE(m_window->size(), QSize(1280, 1024));
0960 
0961     // We should receive a configure event when the window becomes active.
0962     QVERIFY(m_surfaceConfigureRequestedSpy->wait());
0963     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
0964     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
0965     states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
0966     QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
0967     QVERIFY(states.testFlag(Test::XdgToplevel::State::Maximized));
0968 
0969     // One should still be able to change the maximized state of the window.
0970     workspace()->slotWindowMaximize();
0971     QVERIFY(m_surfaceConfigureRequestedSpy->wait());
0972     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 3);
0973     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 3);
0974     QCOMPARE(m_toplevelConfigureRequestedSpy->last().at(0).toSize(), QSize(0, 0));
0975     states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
0976     QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
0977     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
0978 
0979     QSignalSpy frameGeometryChangedSpy(m_window, &Window::frameGeometryChanged);
0980     m_shellSurface->xdgSurface()->ack_configure(m_surfaceConfigureRequestedSpy->last().at(0).value<quint32>());
0981     Test::render(m_surface.get(), QSize(100, 50), Qt::blue);
0982     QVERIFY(frameGeometryChangedSpy.wait());
0983     QCOMPARE(m_window->size(), QSize(100, 50));
0984     QCOMPARE(m_window->maximizeMode(), MaximizeMode::MaximizeRestore);
0985     QCOMPARE(m_window->requestedMaximizeMode(), MaximizeMode::MaximizeRestore);
0986 
0987     // If we create the window again, it should be initially maximized.
0988     destroyTestWindow();
0989     createTestWindow(ReturnAfterSurfaceConfiguration);
0990 
0991     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
0992     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
0993     QCOMPARE(m_toplevelConfigureRequestedSpy->last().at(0).toSize(), QSize(1280, 1024));
0994     states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
0995     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Activated));
0996     QVERIFY(states.testFlag(Test::XdgToplevel::State::Maximized));
0997 
0998     mapClientToSurface(QSize(1280, 1024));
0999     QVERIFY(m_window->isMaximizable());
1000     QCOMPARE(m_window->maximizeMode(), MaximizeMode::MaximizeFull);
1001     QCOMPARE(m_window->requestedMaximizeMode(), MaximizeMode::MaximizeFull);
1002     QCOMPARE(m_window->size(), QSize(1280, 1024));
1003 
1004     QVERIFY(m_surfaceConfigureRequestedSpy->wait());
1005     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
1006     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
1007     states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
1008     QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
1009     QVERIFY(states.testFlag(Test::XdgToplevel::State::Maximized));
1010 
1011     destroyTestWindow();
1012 }
1013 
1014 void TestXdgShellWindowRules::testMaximizeRemember()
1015 {
1016     setWindowRule("maximizehoriz", true, int(Rules::Remember));
1017     setWindowRule("maximizevert", true, int(Rules::Remember));
1018 
1019     createTestWindow(ReturnAfterSurfaceConfiguration);
1020 
1021     // Wait for the initial configure event.
1022     Test::XdgToplevel::States states;
1023     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
1024     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
1025     QCOMPARE(m_toplevelConfigureRequestedSpy->last().at(0).toSize(), QSize(1280, 1024));
1026     states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
1027     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Activated));
1028     QVERIFY(states.testFlag(Test::XdgToplevel::State::Maximized));
1029 
1030     // Map the window.
1031     mapClientToSurface(QSize(1280, 1024));
1032 
1033     QVERIFY(m_window->isMaximizable());
1034     QCOMPARE(m_window->maximizeMode(), MaximizeMode::MaximizeFull);
1035     QCOMPARE(m_window->requestedMaximizeMode(), MaximizeMode::MaximizeFull);
1036     QCOMPARE(m_window->size(), QSize(1280, 1024));
1037 
1038     // We should receive a configure event when the window becomes active.
1039     QVERIFY(m_surfaceConfigureRequestedSpy->wait());
1040     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
1041     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
1042     states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
1043     QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
1044     QVERIFY(states.testFlag(Test::XdgToplevel::State::Maximized));
1045 
1046     // One should still be able to change the maximized state of the window.
1047     workspace()->slotWindowMaximize();
1048     QVERIFY(m_surfaceConfigureRequestedSpy->wait());
1049     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 3);
1050     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 3);
1051     QCOMPARE(m_toplevelConfigureRequestedSpy->last().at(0).toSize(), QSize(0, 0));
1052     states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
1053     QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
1054     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
1055 
1056     QSignalSpy frameGeometryChangedSpy(m_window, &Window::frameGeometryChanged);
1057     m_shellSurface->xdgSurface()->ack_configure(m_surfaceConfigureRequestedSpy->last().at(0).value<quint32>());
1058     Test::render(m_surface.get(), QSize(100, 50), Qt::blue);
1059     QVERIFY(frameGeometryChangedSpy.wait());
1060     QCOMPARE(m_window->size(), QSize(100, 50));
1061     QCOMPARE(m_window->maximizeMode(), MaximizeMode::MaximizeRestore);
1062     QCOMPARE(m_window->requestedMaximizeMode(), MaximizeMode::MaximizeRestore);
1063 
1064     // If we create the window again, it should not be maximized (because last time it wasn't).
1065     destroyTestWindow();
1066     createTestWindow(ReturnAfterSurfaceConfiguration);
1067 
1068     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
1069     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
1070     QCOMPARE(m_toplevelConfigureRequestedSpy->last().at(0).toSize(), QSize(0, 0));
1071     states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
1072     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Activated));
1073     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
1074 
1075     mapClientToSurface(QSize(100, 50));
1076 
1077     QVERIFY(m_window->isMaximizable());
1078     QCOMPARE(m_window->maximizeMode(), MaximizeMode::MaximizeRestore);
1079     QCOMPARE(m_window->requestedMaximizeMode(), MaximizeMode::MaximizeRestore);
1080     QCOMPARE(m_window->size(), QSize(100, 50));
1081 
1082     QVERIFY(m_surfaceConfigureRequestedSpy->wait());
1083     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
1084     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
1085     states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
1086     QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
1087     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
1088 
1089     destroyTestWindow();
1090 }
1091 
1092 void TestXdgShellWindowRules::testMaximizeForce()
1093 {
1094     setWindowRule("maximizehoriz", true, int(Rules::Force));
1095     setWindowRule("maximizevert", true, int(Rules::Force));
1096 
1097     createTestWindow(ReturnAfterSurfaceConfiguration);
1098 
1099     // Wait for the initial configure event.
1100     Test::XdgToplevel::States states;
1101     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
1102     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
1103     QCOMPARE(m_toplevelConfigureRequestedSpy->last().at(0).toSize(), QSize(1280, 1024));
1104     states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
1105     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Activated));
1106     QVERIFY(states.testFlag(Test::XdgToplevel::State::Maximized));
1107 
1108     // Map the window.
1109     mapClientToSurface(QSize(1280, 1024));
1110 
1111     QVERIFY(!m_window->isMaximizable());
1112     QCOMPARE(m_window->maximizeMode(), MaximizeMode::MaximizeFull);
1113     QCOMPARE(m_window->requestedMaximizeMode(), MaximizeMode::MaximizeFull);
1114     QCOMPARE(m_window->size(), QSize(1280, 1024));
1115 
1116     // We should receive a configure event when the window becomes active.
1117     QVERIFY(m_surfaceConfigureRequestedSpy->wait());
1118     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
1119     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
1120     states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
1121     QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
1122     QVERIFY(states.testFlag(Test::XdgToplevel::State::Maximized));
1123 
1124     // Any attempt to change the maximized state should not succeed.
1125     const QRectF oldGeometry = m_window->frameGeometry();
1126     workspace()->slotWindowMaximize();
1127     QVERIFY(!m_surfaceConfigureRequestedSpy->wait(100));
1128     QCOMPARE(m_window->maximizeMode(), MaximizeMode::MaximizeFull);
1129     QCOMPARE(m_window->requestedMaximizeMode(), MaximizeMode::MaximizeFull);
1130     QCOMPARE(m_window->frameGeometry(), oldGeometry);
1131 
1132     // If we create the window again, the maximized state should still be forced.
1133     destroyTestWindow();
1134     createTestWindow(ReturnAfterSurfaceConfiguration);
1135 
1136     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
1137     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
1138     QCOMPARE(m_toplevelConfigureRequestedSpy->last().at(0).toSize(), QSize(1280, 1024));
1139     states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
1140     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Activated));
1141     QVERIFY(states.testFlag(Test::XdgToplevel::State::Maximized));
1142 
1143     mapClientToSurface(QSize(1280, 1024));
1144 
1145     QVERIFY(!m_window->isMaximizable());
1146     QCOMPARE(m_window->maximizeMode(), MaximizeMode::MaximizeFull);
1147     QCOMPARE(m_window->requestedMaximizeMode(), MaximizeMode::MaximizeFull);
1148     QCOMPARE(m_window->size(), QSize(1280, 1024));
1149 
1150     QVERIFY(m_surfaceConfigureRequestedSpy->wait());
1151     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
1152     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
1153     states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
1154     QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
1155     QVERIFY(states.testFlag(Test::XdgToplevel::State::Maximized));
1156 
1157     destroyTestWindow();
1158 }
1159 
1160 void TestXdgShellWindowRules::testMaximizeApplyNow()
1161 {
1162     createTestWindow(ReturnAfterSurfaceConfiguration);
1163 
1164     // Wait for the initial configure event.
1165     Test::XdgToplevel::States states;
1166     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
1167     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
1168     QCOMPARE(m_toplevelConfigureRequestedSpy->last().at(0).toSize(), QSize(0, 0));
1169     states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
1170     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Activated));
1171     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
1172 
1173     // Map the window.
1174     mapClientToSurface(QSize(100, 50));
1175 
1176     QVERIFY(m_window->isMaximizable());
1177     QCOMPARE(m_window->maximizeMode(), MaximizeMode::MaximizeRestore);
1178     QCOMPARE(m_window->requestedMaximizeMode(), MaximizeMode::MaximizeRestore);
1179     QCOMPARE(m_window->size(), QSize(100, 50));
1180 
1181     // We should receive a configure event when the window becomes active.
1182     QVERIFY(m_surfaceConfigureRequestedSpy->wait());
1183     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
1184     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
1185     states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
1186     QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
1187     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
1188 
1189     setWindowRule("maximizehoriz", true, int(Rules::ApplyNow));
1190     setWindowRule("maximizevert", true, int(Rules::ApplyNow));
1191 
1192     // We should receive a configure event with a new surface size.
1193     QVERIFY(m_surfaceConfigureRequestedSpy->wait());
1194     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 3);
1195     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 3);
1196     QCOMPARE(m_toplevelConfigureRequestedSpy->last().at(0).toSize(), QSize(1280, 1024));
1197     states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
1198     QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
1199     QVERIFY(states.testFlag(Test::XdgToplevel::State::Maximized));
1200 
1201     // Draw contents of the maximized client.
1202     QSignalSpy frameGeometryChangedSpy(m_window, &Window::frameGeometryChanged);
1203     m_shellSurface->xdgSurface()->ack_configure(m_surfaceConfigureRequestedSpy->last().at(0).value<quint32>());
1204     Test::render(m_surface.get(), QSize(1280, 1024), Qt::blue);
1205     QVERIFY(frameGeometryChangedSpy.wait());
1206     QCOMPARE(m_window->size(), QSize(1280, 1024));
1207     QCOMPARE(m_window->maximizeMode(), MaximizeMode::MaximizeFull);
1208     QCOMPARE(m_window->requestedMaximizeMode(), MaximizeMode::MaximizeFull);
1209 
1210     // The window still has to be maximizeable.
1211     QVERIFY(m_window->isMaximizable());
1212 
1213     // Restore the window.
1214     workspace()->slotWindowMaximize();
1215     QVERIFY(m_surfaceConfigureRequestedSpy->wait());
1216     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 4);
1217     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 4);
1218     QCOMPARE(m_toplevelConfigureRequestedSpy->last().at(0).toSize(), QSize(100, 50));
1219     states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
1220     QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
1221     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
1222 
1223     m_shellSurface->xdgSurface()->ack_configure(m_surfaceConfigureRequestedSpy->last().at(0).value<quint32>());
1224     Test::render(m_surface.get(), QSize(100, 50), Qt::blue);
1225     QVERIFY(frameGeometryChangedSpy.wait());
1226     QCOMPARE(m_window->size(), QSize(100, 50));
1227     QCOMPARE(m_window->maximizeMode(), MaximizeMode::MaximizeRestore);
1228     QCOMPARE(m_window->requestedMaximizeMode(), MaximizeMode::MaximizeRestore);
1229 
1230     // The rule should be discarded after it's been applied.
1231     const QRectF oldGeometry = m_window->frameGeometry();
1232     m_window->evaluateWindowRules();
1233     QVERIFY(!m_surfaceConfigureRequestedSpy->wait(100));
1234     QCOMPARE(m_window->maximizeMode(), MaximizeMode::MaximizeRestore);
1235     QCOMPARE(m_window->requestedMaximizeMode(), MaximizeMode::MaximizeRestore);
1236     QCOMPARE(m_window->frameGeometry(), oldGeometry);
1237 
1238     destroyTestWindow();
1239 }
1240 
1241 void TestXdgShellWindowRules::testMaximizeForceTemporarily()
1242 {
1243     setWindowRule("maximizehoriz", true, int(Rules::ForceTemporarily));
1244     setWindowRule("maximizevert", true, int(Rules::ForceTemporarily));
1245 
1246     createTestWindow(ReturnAfterSurfaceConfiguration);
1247 
1248     // Wait for the initial configure event.
1249     Test::XdgToplevel::States states;
1250     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
1251     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
1252     QCOMPARE(m_toplevelConfigureRequestedSpy->last().at(0).toSize(), QSize(1280, 1024));
1253     states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
1254     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Activated));
1255     QVERIFY(states.testFlag(Test::XdgToplevel::State::Maximized));
1256 
1257     // Map the window.
1258     mapClientToSurface(QSize(1280, 1024));
1259 
1260     QVERIFY(!m_window->isMaximizable());
1261     QCOMPARE(m_window->maximizeMode(), MaximizeMode::MaximizeFull);
1262     QCOMPARE(m_window->requestedMaximizeMode(), MaximizeMode::MaximizeFull);
1263     QCOMPARE(m_window->size(), QSize(1280, 1024));
1264 
1265     // We should receive a configure event when the window becomes active.
1266     QVERIFY(m_surfaceConfigureRequestedSpy->wait());
1267     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
1268     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
1269     states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
1270     QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
1271     QVERIFY(states.testFlag(Test::XdgToplevel::State::Maximized));
1272 
1273     // Any attempt to change the maximized state should not succeed.
1274     const QRectF oldGeometry = m_window->frameGeometry();
1275     workspace()->slotWindowMaximize();
1276     QVERIFY(!m_surfaceConfigureRequestedSpy->wait(100));
1277     QCOMPARE(m_window->maximizeMode(), MaximizeMode::MaximizeFull);
1278     QCOMPARE(m_window->requestedMaximizeMode(), MaximizeMode::MaximizeFull);
1279     QCOMPARE(m_window->frameGeometry(), oldGeometry);
1280 
1281     // The rule should be discarded if we close the window.
1282     destroyTestWindow();
1283     createTestWindow(ReturnAfterSurfaceConfiguration);
1284 
1285     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
1286     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
1287     QCOMPARE(m_toplevelConfigureRequestedSpy->last().at(0).toSize(), QSize(0, 0));
1288     states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
1289     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Activated));
1290     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
1291 
1292     mapClientToSurface(QSize(100, 50));
1293 
1294     QVERIFY(m_window->isMaximizable());
1295     QCOMPARE(m_window->maximizeMode(), MaximizeMode::MaximizeRestore);
1296     QCOMPARE(m_window->requestedMaximizeMode(), MaximizeMode::MaximizeRestore);
1297     QCOMPARE(m_window->size(), QSize(100, 50));
1298 
1299     QVERIFY(m_surfaceConfigureRequestedSpy->wait());
1300     QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
1301     QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
1302     states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
1303     QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
1304     QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
1305 
1306     destroyTestWindow();
1307 }
1308 
1309 void TestXdgShellWindowRules::testDesktopsDontAffect()
1310 {
1311     // We need at least two virtual desktop for this test.
1312     VirtualDesktopManager::self()->setCount(2);
1313     QCOMPARE(VirtualDesktopManager::self()->count(), 2u);
1314     VirtualDesktop *vd1 = VirtualDesktopManager::self()->desktops().at(0);
1315     VirtualDesktop *vd2 = VirtualDesktopManager::self()->desktops().at(1);
1316 
1317     VirtualDesktopManager::self()->setCurrent(vd1);
1318     QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd1);
1319 
1320     setWindowRule("desktops", QStringList{vd2->id()}, int(Rules::DontAffect));
1321 
1322     createTestWindow();
1323 
1324     // The window should appear on the current virtual desktop.
1325     QCOMPARE(m_window->desktops(), {vd1});
1326     QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd1);
1327 
1328     destroyTestWindow();
1329 }
1330 
1331 void TestXdgShellWindowRules::testDesktopsApply()
1332 {
1333     // We need at least two virtual desktop for this test.
1334     VirtualDesktopManager::self()->setCount(2);
1335     QCOMPARE(VirtualDesktopManager::self()->count(), 2u);
1336     VirtualDesktop *vd1 = VirtualDesktopManager::self()->desktops().at(0);
1337     VirtualDesktop *vd2 = VirtualDesktopManager::self()->desktops().at(1);
1338 
1339     VirtualDesktopManager::self()->setCurrent(vd1);
1340     QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd1);
1341 
1342     setWindowRule("desktops", QStringList{vd2->id()}, int(Rules::Apply));
1343 
1344     createTestWindow();
1345 
1346     // The window should appear on the second virtual desktop.
1347     QCOMPARE(m_window->desktops(), {vd2});
1348     QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd2);
1349 
1350     // We still should be able to move the window between desktops.
1351     m_window->setDesktops({vd1});
1352     QCOMPARE(m_window->desktops(), {vd1});
1353     QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd2);
1354 
1355     // If we re-open the window, it should appear on the second virtual desktop again.
1356     destroyTestWindow();
1357     VirtualDesktopManager::self()->setCurrent(vd1);
1358     QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd1);
1359     createTestWindow();
1360 
1361     QCOMPARE(m_window->desktops(), {vd2});
1362     QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd2);
1363 
1364     destroyTestWindow();
1365 }
1366 
1367 void TestXdgShellWindowRules::testDesktopsRemember()
1368 {
1369     // We need at least two virtual desktop for this test.
1370     VirtualDesktopManager::self()->setCount(2);
1371     QCOMPARE(VirtualDesktopManager::self()->count(), 2u);
1372     VirtualDesktop *vd1 = VirtualDesktopManager::self()->desktops().at(0);
1373     VirtualDesktop *vd2 = VirtualDesktopManager::self()->desktops().at(1);
1374 
1375     VirtualDesktopManager::self()->setCurrent(vd1);
1376     QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd1);
1377 
1378     setWindowRule("desktops", QStringList{vd2->id()}, int(Rules::Remember));
1379 
1380     createTestWindow();
1381 
1382     QCOMPARE(m_window->desktops(), {vd2});
1383     QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd2);
1384 
1385     // Move the window to the first virtual desktop.
1386     m_window->setDesktops({vd1});
1387     QCOMPARE(m_window->desktops(), {vd1});
1388     QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd2);
1389 
1390     // If we create the window again, it should appear on the first virtual desktop.
1391     destroyTestWindow();
1392     createTestWindow();
1393 
1394     QCOMPARE(m_window->desktops(), {vd1});
1395     QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd1);
1396 
1397     destroyTestWindow();
1398 }
1399 
1400 void TestXdgShellWindowRules::testDesktopsForce()
1401 {
1402     // We need at least two virtual desktop for this test.
1403     VirtualDesktopManager::self()->setCount(2);
1404     QCOMPARE(VirtualDesktopManager::self()->count(), 2u);
1405     VirtualDesktop *vd1 = VirtualDesktopManager::self()->desktops().at(0);
1406     VirtualDesktop *vd2 = VirtualDesktopManager::self()->desktops().at(1);
1407 
1408     VirtualDesktopManager::self()->setCurrent(vd1);
1409     QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd1);
1410 
1411     setWindowRule("desktops", QStringList{vd2->id()}, int(Rules::Force));
1412 
1413     createTestWindow();
1414 
1415     // The window should appear on the second virtual desktop.
1416     QCOMPARE(m_window->desktops(), {vd2});
1417     QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd2);
1418 
1419     // Any attempt to move the window to another virtual desktop should fail.
1420     m_window->setDesktops({vd1});
1421     QCOMPARE(m_window->desktops(), {vd2});
1422     QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd2);
1423 
1424     // If we re-open the window, it should appear on the second virtual desktop again.
1425     destroyTestWindow();
1426     VirtualDesktopManager::self()->setCurrent(vd1);
1427     QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd1);
1428     createTestWindow();
1429 
1430     QCOMPARE(m_window->desktops(), {vd2});
1431     QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd2);
1432 
1433     destroyTestWindow();
1434 }
1435 
1436 void TestXdgShellWindowRules::testDesktopsApplyNow()
1437 {
1438     // We need at least two virtual desktop for this test.
1439     VirtualDesktopManager::self()->setCount(2);
1440     QCOMPARE(VirtualDesktopManager::self()->count(), 2u);
1441     VirtualDesktop *vd1 = VirtualDesktopManager::self()->desktops().at(0);
1442     VirtualDesktop *vd2 = VirtualDesktopManager::self()->desktops().at(1);
1443 
1444     VirtualDesktopManager::self()->setCurrent(vd1);
1445     QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd1);
1446 
1447     createTestWindow();
1448 
1449     QCOMPARE(m_window->desktops(), {vd1});
1450     QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd1);
1451 
1452     setWindowRule("desktops", QStringList{vd2->id()}, int(Rules::ApplyNow));
1453 
1454     // The window should have been moved to the second virtual desktop.
1455     QCOMPARE(m_window->desktops(), {vd2});
1456     QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd1);
1457 
1458     // One should still be able to move the window between desktops.
1459     m_window->setDesktops({vd1});
1460     QCOMPARE(m_window->desktops(), {vd1});
1461     QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd1);
1462 
1463     // The rule should not be applied again.
1464     m_window->evaluateWindowRules();
1465     QCOMPARE(m_window->desktops(), {vd1});
1466     QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd1);
1467 
1468     destroyTestWindow();
1469 }
1470 
1471 void TestXdgShellWindowRules::testDesktopsForceTemporarily()
1472 {
1473     // We need at least two virtual desktop for this test.
1474     VirtualDesktopManager::self()->setCount(2);
1475     QCOMPARE(VirtualDesktopManager::self()->count(), 2u);
1476     VirtualDesktop *vd1 = VirtualDesktopManager::self()->desktops().at(0);
1477     VirtualDesktop *vd2 = VirtualDesktopManager::self()->desktops().at(1);
1478 
1479     VirtualDesktopManager::self()->setCurrent(vd1);
1480     QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd1);
1481 
1482     setWindowRule("desktops", QStringList{vd2->id()}, int(Rules::ForceTemporarily));
1483 
1484     createTestWindow();
1485 
1486     // The window should appear on the second virtual desktop.
1487     QCOMPARE(m_window->desktops(), {vd2});
1488     QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd2);
1489 
1490     // Any attempt to move the window to another virtual desktop should fail.
1491     m_window->setDesktops({vd1});
1492     QCOMPARE(m_window->desktops(), {vd2});
1493     QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd2);
1494 
1495     // The rule should be discarded when the window is withdrawn.
1496     destroyTestWindow();
1497     VirtualDesktopManager::self()->setCurrent(vd1);
1498     QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd1);
1499     createTestWindow();
1500 
1501     QCOMPARE(m_window->desktops(), {vd1});
1502     QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd1);
1503 
1504     // One should be able to move the window between desktops.
1505     m_window->setDesktops({vd2});
1506     QCOMPARE(m_window->desktops(), {vd2});
1507     QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd1);
1508 
1509     m_window->setDesktops({vd1});
1510     QCOMPARE(m_window->desktops(), {vd1});
1511     QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd1);
1512 
1513     destroyTestWindow();
1514 }
1515 
1516 void TestXdgShellWindowRules::testMinimizeDontAffect()
1517 {
1518     setWindowRule("minimize", true, int(Rules::DontAffect));
1519 
1520     createTestWindow();
1521     QVERIFY(m_window->isMinimizable());
1522 
1523     // The window should not be minimized.
1524     QVERIFY(!m_window->isMinimized());
1525 
1526     destroyTestWindow();
1527 }
1528 
1529 void TestXdgShellWindowRules::testMinimizeApply()
1530 {
1531     setWindowRule("minimize", true, int(Rules::Apply));
1532 
1533     createTestWindow(ClientShouldBeInactive);
1534     QVERIFY(m_window->isMinimizable());
1535 
1536     // The window should be minimized.
1537     QVERIFY(m_window->isMinimized());
1538 
1539     // We should still be able to unminimize the window.
1540     m_window->setMinimized(false);
1541     QVERIFY(!m_window->isMinimized());
1542 
1543     // If we re-open the window, it should be minimized back again.
1544     destroyTestWindow();
1545     createTestWindow(ClientShouldBeInactive);
1546     QVERIFY(m_window->isMinimizable());
1547     QVERIFY(m_window->isMinimized());
1548 
1549     destroyTestWindow();
1550 }
1551 
1552 void TestXdgShellWindowRules::testMinimizeRemember()
1553 {
1554     setWindowRule("minimize", false, int(Rules::Remember));
1555 
1556     createTestWindow();
1557     QVERIFY(m_window->isMinimizable());
1558     QVERIFY(!m_window->isMinimized());
1559 
1560     // Minimize the window.
1561     m_window->setMinimized(true);
1562     QVERIFY(m_window->isMinimized());
1563 
1564     // If we open the window again, it should be minimized.
1565     destroyTestWindow();
1566     createTestWindow(ClientShouldBeInactive);
1567     QVERIFY(m_window->isMinimizable());
1568     QVERIFY(m_window->isMinimized());
1569 
1570     destroyTestWindow();
1571 }
1572 
1573 void TestXdgShellWindowRules::testMinimizeForce()
1574 {
1575     setWindowRule("minimize", false, int(Rules::Force));
1576 
1577     createTestWindow();
1578     QVERIFY(!m_window->isMinimizable());
1579     QVERIFY(!m_window->isMinimized());
1580 
1581     // Any attempt to minimize the window should fail.
1582     m_window->setMinimized(true);
1583     QVERIFY(!m_window->isMinimized());
1584 
1585     // If we re-open the window, the minimized state should still be forced.
1586     destroyTestWindow();
1587     createTestWindow();
1588     QVERIFY(!m_window->isMinimizable());
1589     QVERIFY(!m_window->isMinimized());
1590     m_window->setMinimized(true);
1591     QVERIFY(!m_window->isMinimized());
1592 
1593     destroyTestWindow();
1594 }
1595 
1596 void TestXdgShellWindowRules::testMinimizeApplyNow()
1597 {
1598     createTestWindow();
1599     QVERIFY(m_window->isMinimizable());
1600     QVERIFY(!m_window->isMinimized());
1601 
1602     setWindowRule("minimize", true, int(Rules::ApplyNow));
1603 
1604     // The window should be minimized now.
1605     QVERIFY(m_window->isMinimizable());
1606     QVERIFY(m_window->isMinimized());
1607 
1608     // One is still able to unminimize the window.
1609     m_window->setMinimized(false);
1610     QVERIFY(!m_window->isMinimized());
1611 
1612     // The rule should not be applied again.
1613     m_window->evaluateWindowRules();
1614     QVERIFY(m_window->isMinimizable());
1615     QVERIFY(!m_window->isMinimized());
1616 
1617     destroyTestWindow();
1618 }
1619 
1620 void TestXdgShellWindowRules::testMinimizeForceTemporarily()
1621 {
1622     setWindowRule("minimize", false, int(Rules::ForceTemporarily));
1623 
1624     createTestWindow();
1625     QVERIFY(!m_window->isMinimizable());
1626     QVERIFY(!m_window->isMinimized());
1627 
1628     // Any attempt to minimize the window should fail until the window is closed.
1629     m_window->setMinimized(true);
1630     QVERIFY(!m_window->isMinimized());
1631 
1632     // The rule should be discarded when the window is closed.
1633     destroyTestWindow();
1634     createTestWindow();
1635     QVERIFY(m_window->isMinimizable());
1636     QVERIFY(!m_window->isMinimized());
1637     m_window->setMinimized(true);
1638     QVERIFY(m_window->isMinimized());
1639 
1640     destroyTestWindow();
1641 }
1642 
1643 void TestXdgShellWindowRules::testSkipTaskbarDontAffect()
1644 {
1645     setWindowRule("skiptaskbar", true, int(Rules::DontAffect));
1646 
1647     createTestWindow();
1648 
1649     // The window should not be affected by the rule.
1650     QVERIFY(!m_window->skipTaskbar());
1651 
1652     destroyTestWindow();
1653 }
1654 
1655 void TestXdgShellWindowRules::testSkipTaskbarApply()
1656 {
1657     setWindowRule("skiptaskbar", true, int(Rules::Apply));
1658 
1659     createTestWindow();
1660 
1661     // The window should not be included on a taskbar.
1662     QVERIFY(m_window->skipTaskbar());
1663 
1664     // Though one can change that.
1665     m_window->setOriginalSkipTaskbar(false);
1666     QVERIFY(!m_window->skipTaskbar());
1667 
1668     // Reopen the window, the rule should be applied again.
1669     destroyTestWindow();
1670     createTestWindow();
1671     QVERIFY(m_window->skipTaskbar());
1672 
1673     destroyTestWindow();
1674 }
1675 
1676 void TestXdgShellWindowRules::testSkipTaskbarRemember()
1677 {
1678     setWindowRule("skiptaskbar", true, int(Rules::Remember));
1679 
1680     createTestWindow();
1681 
1682     // The window should not be included on a taskbar.
1683     QVERIFY(m_window->skipTaskbar());
1684 
1685     // Change the skip-taskbar state.
1686     m_window->setOriginalSkipTaskbar(false);
1687     QVERIFY(!m_window->skipTaskbar());
1688 
1689     // Reopen the window.
1690     destroyTestWindow();
1691     createTestWindow();
1692 
1693     // The window should be included on a taskbar.
1694     QVERIFY(!m_window->skipTaskbar());
1695 
1696     destroyTestWindow();
1697 }
1698 
1699 void TestXdgShellWindowRules::testSkipTaskbarForce()
1700 {
1701     setWindowRule("skiptaskbar", true, int(Rules::Force));
1702 
1703     createTestWindow();
1704 
1705     // The window should not be included on a taskbar.
1706     QVERIFY(m_window->skipTaskbar());
1707 
1708     // Any attempt to change the skip-taskbar state should not succeed.
1709     m_window->setOriginalSkipTaskbar(false);
1710     QVERIFY(m_window->skipTaskbar());
1711 
1712     // Reopen the window.
1713     destroyTestWindow();
1714     createTestWindow();
1715 
1716     // The skip-taskbar state should be still forced.
1717     QVERIFY(m_window->skipTaskbar());
1718 
1719     destroyTestWindow();
1720 }
1721 
1722 void TestXdgShellWindowRules::testSkipTaskbarApplyNow()
1723 {
1724     createTestWindow();
1725     QVERIFY(!m_window->skipTaskbar());
1726 
1727     setWindowRule("skiptaskbar", true, int(Rules::ApplyNow));
1728 
1729     // The window should not be on a taskbar now.
1730     QVERIFY(m_window->skipTaskbar());
1731 
1732     // Also, one change the skip-taskbar state.
1733     m_window->setOriginalSkipTaskbar(false);
1734     QVERIFY(!m_window->skipTaskbar());
1735 
1736     // The rule should not be applied again.
1737     m_window->evaluateWindowRules();
1738     QVERIFY(!m_window->skipTaskbar());
1739 
1740     destroyTestWindow();
1741 }
1742 
1743 void TestXdgShellWindowRules::testSkipTaskbarForceTemporarily()
1744 {
1745     setWindowRule("skiptaskbar", true, int(Rules::ForceTemporarily));
1746 
1747     createTestWindow();
1748 
1749     // The window should not be included on a taskbar.
1750     QVERIFY(m_window->skipTaskbar());
1751 
1752     // Any attempt to change the skip-taskbar state should not succeed.
1753     m_window->setOriginalSkipTaskbar(false);
1754     QVERIFY(m_window->skipTaskbar());
1755 
1756     // The rule should be discarded when the window is closed.
1757     destroyTestWindow();
1758     createTestWindow();
1759     QVERIFY(!m_window->skipTaskbar());
1760 
1761     // The skip-taskbar state is no longer forced.
1762     m_window->setOriginalSkipTaskbar(true);
1763     QVERIFY(m_window->skipTaskbar());
1764 
1765     destroyTestWindow();
1766 }
1767 
1768 void TestXdgShellWindowRules::testSkipPagerDontAffect()
1769 {
1770     setWindowRule("skippager", true, int(Rules::DontAffect));
1771 
1772     createTestWindow();
1773 
1774     // The window should not be affected by the rule.
1775     QVERIFY(!m_window->skipPager());
1776 
1777     destroyTestWindow();
1778 }
1779 
1780 void TestXdgShellWindowRules::testSkipPagerApply()
1781 {
1782     setWindowRule("skippager", true, int(Rules::Apply));
1783 
1784     createTestWindow();
1785 
1786     // The window should not be included on a pager.
1787     QVERIFY(m_window->skipPager());
1788 
1789     // Though one can change that.
1790     m_window->setSkipPager(false);
1791     QVERIFY(!m_window->skipPager());
1792 
1793     // Reopen the window, the rule should be applied again.
1794     destroyTestWindow();
1795     createTestWindow();
1796     QVERIFY(m_window->skipPager());
1797 
1798     destroyTestWindow();
1799 }
1800 
1801 void TestXdgShellWindowRules::testSkipPagerRemember()
1802 {
1803     setWindowRule("skippager", true, int(Rules::Remember));
1804 
1805     createTestWindow();
1806 
1807     // The window should not be included on a pager.
1808     QVERIFY(m_window->skipPager());
1809 
1810     // Change the skip-pager state.
1811     m_window->setSkipPager(false);
1812     QVERIFY(!m_window->skipPager());
1813 
1814     // Reopen the window.
1815     destroyTestWindow();
1816     createTestWindow();
1817 
1818     // The window should be included on a pager.
1819     QVERIFY(!m_window->skipPager());
1820 
1821     destroyTestWindow();
1822 }
1823 
1824 void TestXdgShellWindowRules::testSkipPagerForce()
1825 {
1826     setWindowRule("skippager", true, int(Rules::Force));
1827 
1828     createTestWindow();
1829 
1830     // The window should not be included on a pager.
1831     QVERIFY(m_window->skipPager());
1832 
1833     // Any attempt to change the skip-pager state should not succeed.
1834     m_window->setSkipPager(false);
1835     QVERIFY(m_window->skipPager());
1836 
1837     // Reopen the window.
1838     destroyTestWindow();
1839     createTestWindow();
1840 
1841     // The skip-pager state should be still forced.
1842     QVERIFY(m_window->skipPager());
1843 
1844     destroyTestWindow();
1845 }
1846 
1847 void TestXdgShellWindowRules::testSkipPagerApplyNow()
1848 {
1849     createTestWindow();
1850     QVERIFY(!m_window->skipPager());
1851 
1852     setWindowRule("skippager", true, int(Rules::ApplyNow));
1853 
1854     // The window should not be on a pager now.
1855     QVERIFY(m_window->skipPager());
1856 
1857     // Also, one change the skip-pager state.
1858     m_window->setSkipPager(false);
1859     QVERIFY(!m_window->skipPager());
1860 
1861     // The rule should not be applied again.
1862     m_window->evaluateWindowRules();
1863     QVERIFY(!m_window->skipPager());
1864 
1865     destroyTestWindow();
1866 }
1867 
1868 void TestXdgShellWindowRules::testSkipPagerForceTemporarily()
1869 {
1870     setWindowRule("skippager", true, int(Rules::ForceTemporarily));
1871 
1872     createTestWindow();
1873 
1874     // The window should not be included on a pager.
1875     QVERIFY(m_window->skipPager());
1876 
1877     // Any attempt to change the skip-pager state should not succeed.
1878     m_window->setSkipPager(false);
1879     QVERIFY(m_window->skipPager());
1880 
1881     // The rule should be discarded when the window is closed.
1882     destroyTestWindow();
1883     createTestWindow();
1884     QVERIFY(!m_window->skipPager());
1885 
1886     // The skip-pager state is no longer forced.
1887     m_window->setSkipPager(true);
1888     QVERIFY(m_window->skipPager());
1889 
1890     destroyTestWindow();
1891 }
1892 
1893 void TestXdgShellWindowRules::testSkipSwitcherDontAffect()
1894 {
1895     setWindowRule("skipswitcher", true, int(Rules::DontAffect));
1896 
1897     createTestWindow();
1898 
1899     // The window should not be affected by the rule.
1900     QVERIFY(!m_window->skipSwitcher());
1901 
1902     destroyTestWindow();
1903 }
1904 
1905 void TestXdgShellWindowRules::testSkipSwitcherApply()
1906 {
1907     setWindowRule("skipswitcher", true, int(Rules::Apply));
1908 
1909     createTestWindow();
1910 
1911     // The window should be excluded from window switching effects.
1912     QVERIFY(m_window->skipSwitcher());
1913 
1914     // Though one can change that.
1915     m_window->setSkipSwitcher(false);
1916     QVERIFY(!m_window->skipSwitcher());
1917 
1918     // Reopen the window, the rule should be applied again.
1919     destroyTestWindow();
1920     createTestWindow();
1921     QVERIFY(m_window->skipSwitcher());
1922 
1923     destroyTestWindow();
1924 }
1925 
1926 void TestXdgShellWindowRules::testSkipSwitcherRemember()
1927 {
1928     setWindowRule("skipswitcher", true, int(Rules::Remember));
1929 
1930     createTestWindow();
1931 
1932     // The window should be excluded from window switching effects.
1933     QVERIFY(m_window->skipSwitcher());
1934 
1935     // Change the skip-switcher state.
1936     m_window->setSkipSwitcher(false);
1937     QVERIFY(!m_window->skipSwitcher());
1938 
1939     // Reopen the window.
1940     destroyTestWindow();
1941     createTestWindow();
1942 
1943     // The window should be included in window switching effects.
1944     QVERIFY(!m_window->skipSwitcher());
1945 
1946     destroyTestWindow();
1947 }
1948 
1949 void TestXdgShellWindowRules::testSkipSwitcherForce()
1950 {
1951     setWindowRule("skipswitcher", true, int(Rules::Force));
1952 
1953     createTestWindow();
1954 
1955     // The window should be excluded from window switching effects.
1956     QVERIFY(m_window->skipSwitcher());
1957 
1958     // Any attempt to change the skip-switcher state should not succeed.
1959     m_window->setSkipSwitcher(false);
1960     QVERIFY(m_window->skipSwitcher());
1961 
1962     // Reopen the window.
1963     destroyTestWindow();
1964     createTestWindow();
1965 
1966     // The skip-switcher state should be still forced.
1967     QVERIFY(m_window->skipSwitcher());
1968 
1969     destroyTestWindow();
1970 }
1971 
1972 void TestXdgShellWindowRules::testSkipSwitcherApplyNow()
1973 {
1974     createTestWindow();
1975     QVERIFY(!m_window->skipSwitcher());
1976 
1977     setWindowRule("skipswitcher", true, int(Rules::ApplyNow));
1978 
1979     // The window should be excluded from window switching effects now.
1980     QVERIFY(m_window->skipSwitcher());
1981 
1982     // Also, one change the skip-switcher state.
1983     m_window->setSkipSwitcher(false);
1984     QVERIFY(!m_window->skipSwitcher());
1985 
1986     // The rule should not be applied again.
1987     m_window->evaluateWindowRules();
1988     QVERIFY(!m_window->skipSwitcher());
1989 
1990     destroyTestWindow();
1991 }
1992 
1993 void TestXdgShellWindowRules::testSkipSwitcherForceTemporarily()
1994 {
1995     setWindowRule("skipswitcher", true, int(Rules::ForceTemporarily));
1996 
1997     createTestWindow();
1998 
1999     // The window should be excluded from window switching effects.
2000     QVERIFY(m_window->skipSwitcher());
2001 
2002     // Any attempt to change the skip-switcher state should not succeed.
2003     m_window->setSkipSwitcher(false);
2004     QVERIFY(m_window->skipSwitcher());
2005 
2006     // The rule should be discarded when the window is closed.
2007     destroyTestWindow();
2008     createTestWindow();
2009     QVERIFY(!m_window->skipSwitcher());
2010 
2011     // The skip-switcher state is no longer forced.
2012     m_window->setSkipSwitcher(true);
2013     QVERIFY(m_window->skipSwitcher());
2014 
2015     destroyTestWindow();
2016 }
2017 
2018 void TestXdgShellWindowRules::testKeepAboveDontAffect()
2019 {
2020     setWindowRule("above", true, int(Rules::DontAffect));
2021 
2022     createTestWindow();
2023 
2024     // The keep-above state of the window should not be affected by the rule.
2025     QVERIFY(!m_window->keepAbove());
2026 
2027     destroyTestWindow();
2028 }
2029 
2030 void TestXdgShellWindowRules::testKeepAboveApply()
2031 {
2032     setWindowRule("above", true, int(Rules::Apply));
2033 
2034     createTestWindow();
2035 
2036     // Initially, the window should be kept above.
2037     QVERIFY(m_window->keepAbove());
2038 
2039     // One should also be able to alter the keep-above state.
2040     m_window->setKeepAbove(false);
2041     QVERIFY(!m_window->keepAbove());
2042 
2043     // If one re-opens the window, it should be kept above back again.
2044     destroyTestWindow();
2045     createTestWindow();
2046     QVERIFY(m_window->keepAbove());
2047 
2048     destroyTestWindow();
2049 }
2050 
2051 void TestXdgShellWindowRules::testKeepAboveRemember()
2052 {
2053     setWindowRule("above", true, int(Rules::Remember));
2054 
2055     createTestWindow();
2056 
2057     // Initially, the window should be kept above.
2058     QVERIFY(m_window->keepAbove());
2059 
2060     // Unset the keep-above state.
2061     m_window->setKeepAbove(false);
2062     QVERIFY(!m_window->keepAbove());
2063     destroyTestWindow();
2064 
2065     // Re-open the window, it should not be kept above.
2066     createTestWindow();
2067     QVERIFY(!m_window->keepAbove());
2068 
2069     destroyTestWindow();
2070 }
2071 
2072 void TestXdgShellWindowRules::testKeepAboveForce()
2073 {
2074     setWindowRule("above", true, int(Rules::Force));
2075 
2076     createTestWindow();
2077 
2078     // Initially, the window should be kept above.
2079     QVERIFY(m_window->keepAbove());
2080 
2081     // Any attemt to unset the keep-above should not succeed.
2082     m_window->setKeepAbove(false);
2083     QVERIFY(m_window->keepAbove());
2084 
2085     // If we re-open the window, it should still be kept above.
2086     destroyTestWindow();
2087     createTestWindow();
2088     QVERIFY(m_window->keepAbove());
2089 
2090     destroyTestWindow();
2091 }
2092 
2093 void TestXdgShellWindowRules::testKeepAboveApplyNow()
2094 {
2095     createTestWindow();
2096     QVERIFY(!m_window->keepAbove());
2097 
2098     setWindowRule("above", true, int(Rules::ApplyNow));
2099 
2100     // The window should now be kept above other windows.
2101     QVERIFY(m_window->keepAbove());
2102 
2103     // One is still able to change the keep-above state of the window.
2104     m_window->setKeepAbove(false);
2105     QVERIFY(!m_window->keepAbove());
2106 
2107     // The rule should not be applied again.
2108     m_window->evaluateWindowRules();
2109     QVERIFY(!m_window->keepAbove());
2110 
2111     destroyTestWindow();
2112 }
2113 
2114 void TestXdgShellWindowRules::testKeepAboveForceTemporarily()
2115 {
2116     setWindowRule("above", true, int(Rules::ForceTemporarily));
2117 
2118     createTestWindow();
2119 
2120     // Initially, the window should be kept above.
2121     QVERIFY(m_window->keepAbove());
2122 
2123     // Any attempt to alter the keep-above state should not succeed.
2124     m_window->setKeepAbove(false);
2125     QVERIFY(m_window->keepAbove());
2126 
2127     // The rule should be discarded when the window is closed.
2128     destroyTestWindow();
2129     createTestWindow();
2130     QVERIFY(!m_window->keepAbove());
2131 
2132     // The keep-above state is no longer forced.
2133     m_window->setKeepAbove(true);
2134     QVERIFY(m_window->keepAbove());
2135     m_window->setKeepAbove(false);
2136     QVERIFY(!m_window->keepAbove());
2137 
2138     destroyTestWindow();
2139 }
2140 
2141 void TestXdgShellWindowRules::testKeepBelowDontAffect()
2142 {
2143     setWindowRule("below", true, int(Rules::DontAffect));
2144 
2145     createTestWindow();
2146 
2147     // The keep-below state of the window should not be affected by the rule.
2148     QVERIFY(!m_window->keepBelow());
2149 
2150     destroyTestWindow();
2151 }
2152 
2153 void TestXdgShellWindowRules::testKeepBelowApply()
2154 {
2155     setWindowRule("below", true, int(Rules::Apply));
2156 
2157     createTestWindow();
2158 
2159     // Initially, the window should be kept below.
2160     QVERIFY(m_window->keepBelow());
2161 
2162     // One should also be able to alter the keep-below state.
2163     m_window->setKeepBelow(false);
2164     QVERIFY(!m_window->keepBelow());
2165 
2166     // If one re-opens the window, it should be kept above back again.
2167     destroyTestWindow();
2168     createTestWindow();
2169     QVERIFY(m_window->keepBelow());
2170 
2171     destroyTestWindow();
2172 }
2173 
2174 void TestXdgShellWindowRules::testKeepBelowRemember()
2175 {
2176     setWindowRule("below", true, int(Rules::Remember));
2177 
2178     createTestWindow();
2179 
2180     // Initially, the window should be kept below.
2181     QVERIFY(m_window->keepBelow());
2182 
2183     // Unset the keep-below state.
2184     m_window->setKeepBelow(false);
2185     QVERIFY(!m_window->keepBelow());
2186     destroyTestWindow();
2187 
2188     // Re-open the window, it should not be kept below.
2189     createTestWindow();
2190     QVERIFY(!m_window->keepBelow());
2191 
2192     destroyTestWindow();
2193 }
2194 
2195 void TestXdgShellWindowRules::testKeepBelowForce()
2196 {
2197     setWindowRule("below", true, int(Rules::Force));
2198 
2199     createTestWindow();
2200 
2201     // Initially, the window should be kept below.
2202     QVERIFY(m_window->keepBelow());
2203 
2204     // Any attemt to unset the keep-below should not succeed.
2205     m_window->setKeepBelow(false);
2206     QVERIFY(m_window->keepBelow());
2207 
2208     // If we re-open the window, it should still be kept below.
2209     destroyTestWindow();
2210     createTestWindow();
2211     QVERIFY(m_window->keepBelow());
2212 
2213     destroyTestWindow();
2214 }
2215 
2216 void TestXdgShellWindowRules::testKeepBelowApplyNow()
2217 {
2218     createTestWindow();
2219     QVERIFY(!m_window->keepBelow());
2220 
2221     setWindowRule("below", true, int(Rules::ApplyNow));
2222 
2223     // The window should now be kept below other windows.
2224     QVERIFY(m_window->keepBelow());
2225 
2226     // One is still able to change the keep-below state of the window.
2227     m_window->setKeepBelow(false);
2228     QVERIFY(!m_window->keepBelow());
2229 
2230     // The rule should not be applied again.
2231     m_window->evaluateWindowRules();
2232     QVERIFY(!m_window->keepBelow());
2233 
2234     destroyTestWindow();
2235 }
2236 
2237 void TestXdgShellWindowRules::testKeepBelowForceTemporarily()
2238 {
2239     setWindowRule("below", true, int(Rules::ForceTemporarily));
2240 
2241     createTestWindow();
2242 
2243     // Initially, the window should be kept below.
2244     QVERIFY(m_window->keepBelow());
2245 
2246     // Any attempt to alter the keep-below state should not succeed.
2247     m_window->setKeepBelow(false);
2248     QVERIFY(m_window->keepBelow());
2249 
2250     // The rule should be discarded when the window is closed.
2251     destroyTestWindow();
2252     createTestWindow();
2253     QVERIFY(!m_window->keepBelow());
2254 
2255     // The keep-below state is no longer forced.
2256     m_window->setKeepBelow(true);
2257     QVERIFY(m_window->keepBelow());
2258     m_window->setKeepBelow(false);
2259     QVERIFY(!m_window->keepBelow());
2260 
2261     destroyTestWindow();
2262 }
2263 
2264 void TestXdgShellWindowRules::testShortcutDontAffect()
2265 {
2266 #if !KWIN_BUILD_GLOBALSHORTCUTS
2267     QSKIP("Can't test shortcuts without shortcuts");
2268     return;
2269 #endif
2270 
2271     setWindowRule("shortcut", "Ctrl+Alt+1", int(Rules::DontAffect));
2272 
2273     createTestWindow();
2274     QCOMPARE(m_window->shortcut(), QKeySequence());
2275     m_window->setMinimized(true);
2276     QVERIFY(m_window->isMinimized());
2277 
2278     // If we press the window shortcut, nothing should happen.
2279     QSignalSpy minimizedChangedSpy(m_window, &Window::minimizedChanged);
2280     quint32 timestamp = 1;
2281     Test::keyboardKeyPressed(KEY_LEFTCTRL, timestamp++);
2282     Test::keyboardKeyPressed(KEY_LEFTALT, timestamp++);
2283     Test::keyboardKeyPressed(KEY_1, timestamp++);
2284     Test::keyboardKeyReleased(KEY_1, timestamp++);
2285     Test::keyboardKeyReleased(KEY_LEFTALT, timestamp++);
2286     Test::keyboardKeyReleased(KEY_LEFTCTRL, timestamp++);
2287     QVERIFY(!minimizedChangedSpy.wait(100));
2288     QVERIFY(m_window->isMinimized());
2289 
2290     destroyTestWindow();
2291 }
2292 
2293 void TestXdgShellWindowRules::testShortcutApply()
2294 {
2295 #if !KWIN_BUILD_GLOBALSHORTCUTS
2296     QSKIP("Can't test shortcuts without shortcuts");
2297     return;
2298 #endif
2299     setWindowRule("shortcut", "Ctrl+Alt+1", int(Rules::Apply));
2300 
2301     createTestWindow();
2302 
2303     // If we press the window shortcut, the window should be brought back to user.
2304     QSignalSpy minimizedChangedSpy(m_window, &Window::minimizedChanged);
2305     quint32 timestamp = 1;
2306     QCOMPARE(m_window->shortcut(), (QKeySequence{Qt::CTRL | Qt::ALT | Qt::Key_1}));
2307     m_window->setMinimized(true);
2308     QVERIFY(m_window->isMinimized());
2309     Test::keyboardKeyPressed(KEY_LEFTCTRL, timestamp++);
2310     Test::keyboardKeyPressed(KEY_LEFTALT, timestamp++);
2311     Test::keyboardKeyPressed(KEY_1, timestamp++);
2312     Test::keyboardKeyReleased(KEY_1, timestamp++);
2313     Test::keyboardKeyReleased(KEY_LEFTALT, timestamp++);
2314     Test::keyboardKeyReleased(KEY_LEFTCTRL, timestamp++);
2315     QVERIFY(minimizedChangedSpy.wait());
2316     QVERIFY(!m_window->isMinimized());
2317 
2318     // One can also change the shortcut.
2319     m_window->setShortcut(QStringLiteral("Ctrl+Alt+2"));
2320     QCOMPARE(m_window->shortcut(), (QKeySequence{Qt::CTRL | Qt::ALT | Qt::Key_2}));
2321     m_window->setMinimized(true);
2322     QVERIFY(m_window->isMinimized());
2323     Test::keyboardKeyPressed(KEY_LEFTCTRL, timestamp++);
2324     Test::keyboardKeyPressed(KEY_LEFTALT, timestamp++);
2325     Test::keyboardKeyPressed(KEY_2, timestamp++);
2326     Test::keyboardKeyReleased(KEY_2, timestamp++);
2327     Test::keyboardKeyReleased(KEY_LEFTALT, timestamp++);
2328     Test::keyboardKeyReleased(KEY_LEFTCTRL, timestamp++);
2329     QVERIFY(minimizedChangedSpy.wait());
2330     QVERIFY(!m_window->isMinimized());
2331 
2332     // The old shortcut should do nothing.
2333     m_window->setMinimized(true);
2334     QVERIFY(m_window->isMinimized());
2335     Test::keyboardKeyPressed(KEY_LEFTCTRL, timestamp++);
2336     Test::keyboardKeyPressed(KEY_LEFTALT, timestamp++);
2337     Test::keyboardKeyPressed(KEY_1, timestamp++);
2338     Test::keyboardKeyReleased(KEY_1, timestamp++);
2339     Test::keyboardKeyReleased(KEY_LEFTALT, timestamp++);
2340     Test::keyboardKeyReleased(KEY_LEFTCTRL, timestamp++);
2341     QVERIFY(!minimizedChangedSpy.wait(100));
2342     QVERIFY(m_window->isMinimized());
2343 
2344     // Reopen the window.
2345     destroyTestWindow();
2346     createTestWindow();
2347 
2348     // The window shortcut should be set back to Ctrl+Alt+1.
2349     QCOMPARE(m_window->shortcut(), (QKeySequence{Qt::CTRL | Qt::ALT | Qt::Key_1}));
2350 
2351     destroyTestWindow();
2352 }
2353 
2354 void TestXdgShellWindowRules::testShortcutRemember()
2355 {
2356     QSKIP("KWin core doesn't try to save the last used window shortcut");
2357 
2358     setWindowRule("shortcut", "Ctrl+Alt+1", int(Rules::Remember));
2359 
2360     createTestWindow();
2361 
2362     // If we press the window shortcut, the window should be brought back to user.
2363     QSignalSpy minimizedChangedSpy(m_window, &Window::minimizedChanged);
2364     quint32 timestamp = 1;
2365     QCOMPARE(m_window->shortcut(), (QKeySequence{Qt::CTRL | Qt::ALT | Qt::Key_1}));
2366     m_window->setMinimized(true);
2367     QVERIFY(m_window->isMinimized());
2368     Test::keyboardKeyPressed(KEY_LEFTCTRL, timestamp++);
2369     Test::keyboardKeyPressed(KEY_LEFTALT, timestamp++);
2370     Test::keyboardKeyPressed(KEY_1, timestamp++);
2371     Test::keyboardKeyReleased(KEY_1, timestamp++);
2372     Test::keyboardKeyReleased(KEY_LEFTALT, timestamp++);
2373     Test::keyboardKeyReleased(KEY_LEFTCTRL, timestamp++);
2374     QVERIFY(minimizedChangedSpy.wait());
2375     QVERIFY(!m_window->isMinimized());
2376 
2377     // Change the window shortcut to Ctrl+Alt+2.
2378     m_window->setShortcut(QStringLiteral("Ctrl+Alt+2"));
2379     QCOMPARE(m_window->shortcut(), (QKeySequence{Qt::CTRL | Qt::ALT | Qt::Key_2}));
2380     m_window->setMinimized(true);
2381     QVERIFY(m_window->isMinimized());
2382     Test::keyboardKeyPressed(KEY_LEFTCTRL, timestamp++);
2383     Test::keyboardKeyPressed(KEY_LEFTALT, timestamp++);
2384     Test::keyboardKeyPressed(KEY_2, timestamp++);
2385     Test::keyboardKeyReleased(KEY_2, timestamp++);
2386     Test::keyboardKeyReleased(KEY_LEFTALT, timestamp++);
2387     Test::keyboardKeyReleased(KEY_LEFTCTRL, timestamp++);
2388     QVERIFY(minimizedChangedSpy.wait());
2389     QVERIFY(!m_window->isMinimized());
2390 
2391     // Reopen the window.
2392     destroyTestWindow();
2393     createTestWindow();
2394 
2395     // The window shortcut should be set to the last known value.
2396     QCOMPARE(m_window->shortcut(), (QKeySequence{Qt::CTRL | Qt::ALT | Qt::Key_2}));
2397 
2398     destroyTestWindow();
2399 }
2400 
2401 void TestXdgShellWindowRules::testShortcutForce()
2402 {
2403     QSKIP("KWin core can't release forced window shortcuts");
2404 
2405     setWindowRule("shortcut", "Ctrl+Alt+1", int(Rules::Force));
2406 
2407     createTestWindow();
2408 
2409     // If we press the window shortcut, the window should be brought back to user.
2410     QSignalSpy minimizedChangedSpy(m_window, &Window::minimizedChanged);
2411     quint32 timestamp = 1;
2412     QCOMPARE(m_window->shortcut(), (QKeySequence{Qt::CTRL | Qt::ALT | Qt::Key_1}));
2413     m_window->setMinimized(true);
2414     QVERIFY(m_window->isMinimized());
2415     Test::keyboardKeyPressed(KEY_LEFTCTRL, timestamp++);
2416     Test::keyboardKeyPressed(KEY_LEFTALT, timestamp++);
2417     Test::keyboardKeyPressed(KEY_1, timestamp++);
2418     Test::keyboardKeyReleased(KEY_1, timestamp++);
2419     Test::keyboardKeyReleased(KEY_LEFTALT, timestamp++);
2420     Test::keyboardKeyReleased(KEY_LEFTCTRL, timestamp++);
2421     QVERIFY(minimizedChangedSpy.wait());
2422     QVERIFY(!m_window->isMinimized());
2423 
2424     // Any attempt to change the window shortcut should not succeed.
2425     m_window->setShortcut(QStringLiteral("Ctrl+Alt+2"));
2426     QCOMPARE(m_window->shortcut(), (QKeySequence{Qt::CTRL | Qt::ALT | Qt::Key_1}));
2427     m_window->setMinimized(true);
2428     QVERIFY(m_window->isMinimized());
2429     Test::keyboardKeyPressed(KEY_LEFTCTRL, timestamp++);
2430     Test::keyboardKeyPressed(KEY_LEFTALT, timestamp++);
2431     Test::keyboardKeyPressed(KEY_2, timestamp++);
2432     Test::keyboardKeyReleased(KEY_2, timestamp++);
2433     Test::keyboardKeyReleased(KEY_LEFTALT, timestamp++);
2434     Test::keyboardKeyReleased(KEY_LEFTCTRL, timestamp++);
2435     QVERIFY(!minimizedChangedSpy.wait(100));
2436     QVERIFY(m_window->isMinimized());
2437 
2438     // Reopen the window.
2439     destroyTestWindow();
2440     createTestWindow();
2441 
2442     // The window shortcut should still be forced.
2443     QCOMPARE(m_window->shortcut(), (QKeySequence{Qt::CTRL | Qt::ALT | Qt::Key_1}));
2444 
2445     destroyTestWindow();
2446 }
2447 
2448 void TestXdgShellWindowRules::testShortcutApplyNow()
2449 {
2450 #if !KWIN_BUILD_GLOBALSHORTCUTS
2451     QSKIP("Can't test shortcuts without shortcuts");
2452     return;
2453 #endif
2454 
2455     createTestWindow();
2456     QVERIFY(m_window->shortcut().isEmpty());
2457 
2458     setWindowRule("shortcut", "Ctrl+Alt+1", int(Rules::ApplyNow));
2459 
2460     // The window should now have a window shortcut assigned.
2461     QCOMPARE(m_window->shortcut(), (QKeySequence{Qt::CTRL | Qt::ALT | Qt::Key_1}));
2462     QSignalSpy minimizedChangedSpy(m_window, &Window::minimizedChanged);
2463     quint32 timestamp = 1;
2464     m_window->setMinimized(true);
2465     QVERIFY(m_window->isMinimized());
2466     Test::keyboardKeyPressed(KEY_LEFTCTRL, timestamp++);
2467     Test::keyboardKeyPressed(KEY_LEFTALT, timestamp++);
2468     Test::keyboardKeyPressed(KEY_1, timestamp++);
2469     Test::keyboardKeyReleased(KEY_1, timestamp++);
2470     Test::keyboardKeyReleased(KEY_LEFTALT, timestamp++);
2471     Test::keyboardKeyReleased(KEY_LEFTCTRL, timestamp++);
2472     QVERIFY(minimizedChangedSpy.wait());
2473     QVERIFY(!m_window->isMinimized());
2474 
2475     // Assign a different shortcut.
2476     m_window->setShortcut(QStringLiteral("Ctrl+Alt+2"));
2477     QCOMPARE(m_window->shortcut(), (QKeySequence{Qt::CTRL | Qt::ALT | Qt::Key_2}));
2478     m_window->setMinimized(true);
2479     QVERIFY(m_window->isMinimized());
2480     Test::keyboardKeyPressed(KEY_LEFTCTRL, timestamp++);
2481     Test::keyboardKeyPressed(KEY_LEFTALT, timestamp++);
2482     Test::keyboardKeyPressed(KEY_2, timestamp++);
2483     Test::keyboardKeyReleased(KEY_2, timestamp++);
2484     Test::keyboardKeyReleased(KEY_LEFTALT, timestamp++);
2485     Test::keyboardKeyReleased(KEY_LEFTCTRL, timestamp++);
2486     QVERIFY(minimizedChangedSpy.wait());
2487     QVERIFY(!m_window->isMinimized());
2488 
2489     // The rule should not be applied again.
2490     m_window->evaluateWindowRules();
2491     QCOMPARE(m_window->shortcut(), (QKeySequence{Qt::CTRL | Qt::ALT | Qt::Key_2}));
2492 
2493     destroyTestWindow();
2494 }
2495 
2496 void TestXdgShellWindowRules::testShortcutForceTemporarily()
2497 {
2498     QSKIP("KWin core can't release forced window shortcuts");
2499 
2500     setWindowRule("shortcut", "Ctrl+Alt+1", int(Rules::ForceTemporarily));
2501 
2502     createTestWindow();
2503 
2504     // If we press the window shortcut, the window should be brought back to user.
2505     QSignalSpy minimizedChangedSpy(m_window, &Window::minimizedChanged);
2506     quint32 timestamp = 1;
2507     QCOMPARE(m_window->shortcut(), (QKeySequence{Qt::CTRL | Qt::ALT | Qt::Key_1}));
2508     m_window->setMinimized(true);
2509     QVERIFY(m_window->isMinimized());
2510     Test::keyboardKeyPressed(KEY_LEFTCTRL, timestamp++);
2511     Test::keyboardKeyPressed(KEY_LEFTALT, timestamp++);
2512     Test::keyboardKeyPressed(KEY_1, timestamp++);
2513     Test::keyboardKeyReleased(KEY_1, timestamp++);
2514     Test::keyboardKeyReleased(KEY_LEFTALT, timestamp++);
2515     Test::keyboardKeyReleased(KEY_LEFTCTRL, timestamp++);
2516     QVERIFY(minimizedChangedSpy.wait());
2517     QVERIFY(!m_window->isMinimized());
2518 
2519     // Any attempt to change the window shortcut should not succeed.
2520     m_window->setShortcut(QStringLiteral("Ctrl+Alt+2"));
2521     QCOMPARE(m_window->shortcut(), (QKeySequence{Qt::CTRL | Qt::ALT | Qt::Key_1}));
2522     m_window->setMinimized(true);
2523     QVERIFY(m_window->isMinimized());
2524     Test::keyboardKeyPressed(KEY_LEFTCTRL, timestamp++);
2525     Test::keyboardKeyPressed(KEY_LEFTALT, timestamp++);
2526     Test::keyboardKeyPressed(KEY_2, timestamp++);
2527     Test::keyboardKeyReleased(KEY_2, timestamp++);
2528     Test::keyboardKeyReleased(KEY_LEFTALT, timestamp++);
2529     Test::keyboardKeyReleased(KEY_LEFTCTRL, timestamp++);
2530     QVERIFY(!minimizedChangedSpy.wait(100));
2531     QVERIFY(m_window->isMinimized());
2532 
2533     // The rule should be discarded when the window is closed.
2534     destroyTestWindow();
2535     createTestWindow();
2536     QVERIFY(m_window->shortcut().isEmpty());
2537 
2538     destroyTestWindow();
2539 }
2540 
2541 void TestXdgShellWindowRules::testDesktopFileDontAffect()
2542 {
2543     // Currently, the desktop file name is derived from the app id. If the app id is
2544     // changed, then the old rules will be lost. Either setDesktopFileName should
2545     // be exposed or the desktop file name rule should be removed for wayland windows.
2546     QSKIP("Needs changes in KWin core to pass");
2547 }
2548 
2549 void TestXdgShellWindowRules::testDesktopFileApply()
2550 {
2551     // Currently, the desktop file name is derived from the app id. If the app id is
2552     // changed, then the old rules will be lost. Either setDesktopFileName should
2553     // be exposed or the desktop file name rule should be removed for wayland windows.
2554     QSKIP("Needs changes in KWin core to pass");
2555 }
2556 
2557 void TestXdgShellWindowRules::testDesktopFileRemember()
2558 {
2559     // Currently, the desktop file name is derived from the app id. If the app id is
2560     // changed, then the old rules will be lost. Either setDesktopFileName should
2561     // be exposed or the desktop file name rule should be removed for wayland windows.
2562     QSKIP("Needs changes in KWin core to pass");
2563 }
2564 
2565 void TestXdgShellWindowRules::testDesktopFileForce()
2566 {
2567     // Currently, the desktop file name is derived from the app id. If the app id is
2568     // changed, then the old rules will be lost. Either setDesktopFileName should
2569     // be exposed or the desktop file name rule should be removed for wayland windows.
2570     QSKIP("Needs changes in KWin core to pass");
2571 }
2572 
2573 void TestXdgShellWindowRules::testDesktopFileApplyNow()
2574 {
2575     // Currently, the desktop file name is derived from the app id. If the app id is
2576     // changed, then the old rules will be lost. Either setDesktopFileName should
2577     // be exposed or the desktop file name rule should be removed for wayland windows.
2578     QSKIP("Needs changes in KWin core to pass");
2579 }
2580 
2581 void TestXdgShellWindowRules::testDesktopFileForceTemporarily()
2582 {
2583     // Currently, the desktop file name is derived from the app id. If the app id is
2584     // changed, then the old rules will be lost. Either setDesktopFileName should
2585     // be exposed or the desktop file name rule should be removed for wayland windows.
2586     QSKIP("Needs changes in KWin core to pass");
2587 }
2588 
2589 void TestXdgShellWindowRules::testActiveOpacityDontAffect()
2590 {
2591     setWindowRule("opacityactive", 90, int(Rules::DontAffect));
2592 
2593     createTestWindow();
2594     QVERIFY(m_window->isActive());
2595 
2596     // The opacity should not be affected by the rule.
2597     QCOMPARE(m_window->opacity(), 1.0);
2598 
2599     destroyTestWindow();
2600 }
2601 
2602 void TestXdgShellWindowRules::testActiveOpacityForce()
2603 {
2604     setWindowRule("opacityactive", 90, int(Rules::Force));
2605 
2606     createTestWindow();
2607     QVERIFY(m_window->isActive());
2608     QCOMPARE(m_window->opacity(), 0.9);
2609 
2610     destroyTestWindow();
2611 }
2612 
2613 void TestXdgShellWindowRules::testActiveOpacityForceTemporarily()
2614 {
2615     setWindowRule("opacityactive", 90, int(Rules::ForceTemporarily));
2616 
2617     createTestWindow();
2618     QVERIFY(m_window->isActive());
2619     QCOMPARE(m_window->opacity(), 0.9);
2620 
2621     // The rule should be discarded when the window is closed.
2622     destroyTestWindow();
2623     createTestWindow();
2624     QVERIFY(m_window->isActive());
2625     QCOMPARE(m_window->opacity(), 1.0);
2626 
2627     destroyTestWindow();
2628 }
2629 
2630 void TestXdgShellWindowRules::testInactiveOpacityDontAffect()
2631 {
2632     setWindowRule("opacityinactive", 80, int(Rules::DontAffect));
2633 
2634     createTestWindow();
2635     QVERIFY(m_window->isActive());
2636 
2637     // Make the window inactive.
2638     workspace()->setActiveWindow(nullptr);
2639     QVERIFY(!m_window->isActive());
2640 
2641     // The opacity of the window should not be affected by the rule.
2642     QCOMPARE(m_window->opacity(), 1.0);
2643 
2644     destroyTestWindow();
2645 }
2646 
2647 void TestXdgShellWindowRules::testInactiveOpacityForce()
2648 {
2649     setWindowRule("opacityinactive", 80, int(Rules::Force));
2650 
2651     createTestWindow();
2652     QVERIFY(m_window->isActive());
2653     QCOMPARE(m_window->opacity(), 1.0);
2654 
2655     // Make the window inactive.
2656     workspace()->setActiveWindow(nullptr);
2657     QVERIFY(!m_window->isActive());
2658 
2659     // The opacity should be forced by the rule.
2660     QCOMPARE(m_window->opacity(), 0.8);
2661 
2662     destroyTestWindow();
2663 }
2664 
2665 void TestXdgShellWindowRules::testInactiveOpacityForceTemporarily()
2666 {
2667     setWindowRule("opacityinactive", 80, int(Rules::ForceTemporarily));
2668 
2669     createTestWindow();
2670     QVERIFY(m_window->isActive());
2671     QCOMPARE(m_window->opacity(), 1.0);
2672 
2673     // Make the window inactive.
2674     workspace()->setActiveWindow(nullptr);
2675     QVERIFY(!m_window->isActive());
2676 
2677     // The opacity should be forced by the rule.
2678     QCOMPARE(m_window->opacity(), 0.8);
2679 
2680     // The rule should be discarded when the window is closed.
2681     destroyTestWindow();
2682     createTestWindow();
2683 
2684     QVERIFY(m_window->isActive());
2685     QCOMPARE(m_window->opacity(), 1.0);
2686     workspace()->setActiveWindow(nullptr);
2687     QVERIFY(!m_window->isActive());
2688     QCOMPARE(m_window->opacity(), 1.0);
2689 
2690     destroyTestWindow();
2691 }
2692 
2693 void TestXdgShellWindowRules::testNoBorderDontAffect()
2694 {
2695     setWindowRule("noborder", true, int(Rules::DontAffect));
2696     createTestWindow(ServerSideDecoration);
2697 
2698     // The window should not be affected by the rule.
2699     QVERIFY(!m_window->noBorder());
2700 
2701     destroyTestWindow();
2702 }
2703 
2704 void TestXdgShellWindowRules::testNoBorderApply()
2705 {
2706     setWindowRule("noborder", true, int(Rules::Apply));
2707     createTestWindow(ServerSideDecoration);
2708 
2709     // Initially, the window should not be decorated.
2710     QVERIFY(m_window->noBorder());
2711     QVERIFY(!m_window->isDecorated());
2712 
2713     // But you should be able to change "no border" property afterwards.
2714     QVERIFY(m_window->userCanSetNoBorder());
2715     m_window->setNoBorder(false);
2716     QVERIFY(!m_window->noBorder());
2717 
2718     // If one re-opens the window, it should have no border again.
2719     destroyTestWindow();
2720     createTestWindow(ServerSideDecoration);
2721     QVERIFY(m_window->noBorder());
2722 
2723     destroyTestWindow();
2724 }
2725 
2726 void TestXdgShellWindowRules::testNoBorderRemember()
2727 {
2728     setWindowRule("noborder", true, int(Rules::Remember));
2729     createTestWindow(ServerSideDecoration);
2730 
2731     // Initially, the window should not be decorated.
2732     QVERIFY(m_window->noBorder());
2733     QVERIFY(!m_window->isDecorated());
2734 
2735     // Unset the "no border" property.
2736     QVERIFY(m_window->userCanSetNoBorder());
2737     m_window->setNoBorder(false);
2738     QVERIFY(!m_window->noBorder());
2739 
2740     // Re-open the window, it should be decorated.
2741     destroyTestWindow();
2742     createTestWindow(ServerSideDecoration);
2743     QVERIFY(m_window->isDecorated());
2744     QVERIFY(!m_window->noBorder());
2745 
2746     destroyTestWindow();
2747 }
2748 
2749 void TestXdgShellWindowRules::testNoBorderForce()
2750 {
2751     setWindowRule("noborder", true, int(Rules::Force));
2752     createTestWindow(ServerSideDecoration);
2753 
2754     // The window should not be decorated.
2755     QVERIFY(m_window->noBorder());
2756     QVERIFY(!m_window->isDecorated());
2757 
2758     // And the user should not be able to change the "no border" property.
2759     m_window->setNoBorder(false);
2760     QVERIFY(m_window->noBorder());
2761 
2762     // Reopen the window.
2763     destroyTestWindow();
2764     createTestWindow(ServerSideDecoration);
2765 
2766     // The "no border" property should be still forced.
2767     QVERIFY(m_window->noBorder());
2768 
2769     destroyTestWindow();
2770 }
2771 
2772 void TestXdgShellWindowRules::testNoBorderApplyNow()
2773 {
2774     createTestWindow(ServerSideDecoration);
2775     QVERIFY(!m_window->noBorder());
2776 
2777     // Initialize RuleBook with the test rule.
2778     setWindowRule("noborder", true, int(Rules::ApplyNow));
2779 
2780     // The "no border" property should be set now.
2781     QVERIFY(m_window->noBorder());
2782 
2783     // One should be still able to change the "no border" property.
2784     m_window->setNoBorder(false);
2785     QVERIFY(!m_window->noBorder());
2786 
2787     // The rule should not be applied again.
2788     m_window->evaluateWindowRules();
2789     QVERIFY(!m_window->noBorder());
2790 
2791     destroyTestWindow();
2792 }
2793 
2794 void TestXdgShellWindowRules::testNoBorderForceTemporarily()
2795 {
2796     setWindowRule("noborder", true, int(Rules::ForceTemporarily));
2797     createTestWindow(ServerSideDecoration);
2798 
2799     // The "no border" property should be set.
2800     QVERIFY(m_window->noBorder());
2801 
2802     // And you should not be able to change it.
2803     m_window->setNoBorder(false);
2804     QVERIFY(m_window->noBorder());
2805 
2806     // The rule should be discarded when the window is closed.
2807     destroyTestWindow();
2808     createTestWindow(ServerSideDecoration);
2809     QVERIFY(!m_window->noBorder());
2810 
2811     // The "no border" property is no longer forced.
2812     m_window->setNoBorder(true);
2813     QVERIFY(m_window->noBorder());
2814     m_window->setNoBorder(false);
2815     QVERIFY(!m_window->noBorder());
2816 
2817     destroyTestWindow();
2818 }
2819 
2820 void TestXdgShellWindowRules::testScreenDontAffect()
2821 {
2822     const QList<KWin::Output *> outputs = workspace()->outputs();
2823 
2824     setWindowRule("screen", int(1), int(Rules::DontAffect));
2825 
2826     createTestWindow();
2827 
2828     // The window should not be affected by the rule.
2829     QCOMPARE(m_window->output()->name(), outputs.at(0)->name());
2830 
2831     // The user can still move the window to another screen.
2832     workspace()->sendWindowToOutput(m_window, outputs.at(1));
2833     QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
2834 
2835     destroyTestWindow();
2836 }
2837 
2838 void TestXdgShellWindowRules::testScreenApply()
2839 {
2840     const QList<KWin::Output *> outputs = workspace()->outputs();
2841 
2842     setWindowRule("screen", int(1), int(Rules::Apply));
2843 
2844     createTestWindow();
2845 
2846     // The window should be in the screen specified by the rule.
2847     QEXPECT_FAIL("", "Applying a screen rule on a new client fails on Wayland", Continue);
2848     QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
2849 
2850     // The user can move the window to another screen.
2851     workspace()->sendWindowToOutput(m_window, outputs.at(0));
2852     QCOMPARE(m_window->output()->name(), outputs.at(0)->name());
2853 
2854     destroyTestWindow();
2855 }
2856 
2857 void TestXdgShellWindowRules::testScreenRemember()
2858 {
2859     const QList<KWin::Output *> outputs = workspace()->outputs();
2860 
2861     setWindowRule("screen", int(1), int(Rules::Remember));
2862 
2863     createTestWindow();
2864 
2865     // Initially, the window should be in the first screen
2866     QCOMPARE(m_window->output()->name(), outputs.at(0)->name());
2867 
2868     // Move the window to the second screen.
2869     workspace()->sendWindowToOutput(m_window, outputs.at(1));
2870     QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
2871 
2872     // Close and reopen the window.
2873     destroyTestWindow();
2874     createTestWindow();
2875 
2876     QEXPECT_FAIL("", "Applying a screen rule on a new client fails on Wayland", Continue);
2877     QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
2878 
2879     destroyTestWindow();
2880 }
2881 
2882 void TestXdgShellWindowRules::testScreenForce()
2883 {
2884     const QList<KWin::Output *> outputs = workspace()->outputs();
2885 
2886     createTestWindow();
2887     QVERIFY(m_window->isActive());
2888 
2889     setWindowRule("screen", int(1), int(Rules::Force));
2890 
2891     // The window should be forced to the screen specified by the rule.
2892     QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
2893 
2894     // User should not be able to move the window to another screen.
2895     workspace()->sendWindowToOutput(m_window, outputs.at(0));
2896     QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
2897 
2898     // Disable the output where the window is on, so the window is moved the other screen
2899     OutputConfiguration config;
2900     auto changeSet = config.changeSet(outputs.at(1));
2901     changeSet->enabled = false;
2902     workspace()->applyOutputConfiguration(config);
2903 
2904     QVERIFY(!outputs.at(1)->isEnabled());
2905     QCOMPARE(m_window->output()->name(), outputs.at(0)->name());
2906 
2907     // Enable the output and check that the window is moved there again
2908     changeSet->enabled = true;
2909     workspace()->applyOutputConfiguration(config);
2910 
2911     QVERIFY(outputs.at(1)->isEnabled());
2912     QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
2913 
2914     // Close and reopen the window.
2915     destroyTestWindow();
2916     createTestWindow();
2917 
2918     QEXPECT_FAIL("", "Applying a screen rule on a new client fails on Wayland", Continue);
2919     QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
2920 
2921     destroyTestWindow();
2922 }
2923 
2924 void TestXdgShellWindowRules::testScreenApplyNow()
2925 {
2926     const QList<KWin::Output *> outputs = workspace()->outputs();
2927 
2928     createTestWindow();
2929 
2930     QCOMPARE(m_window->output()->name(), outputs.at(0)->name());
2931 
2932     // Set the rule so the window should move to the screen specified by the rule.
2933     setWindowRule("screen", int(1), int(Rules::ApplyNow));
2934     QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
2935 
2936     // The user can move the window to another screen.
2937     workspace()->sendWindowToOutput(m_window, outputs.at(0));
2938     QCOMPARE(m_window->output()->name(), outputs.at(0)->name());
2939 
2940     // The rule should not be applied again.
2941     m_window->evaluateWindowRules();
2942     QCOMPARE(m_window->output()->name(), outputs.at(0)->name());
2943 
2944     destroyTestWindow();
2945 }
2946 
2947 void TestXdgShellWindowRules::testScreenForceTemporarily()
2948 {
2949     const QList<KWin::Output *> outputs = workspace()->outputs();
2950 
2951     createTestWindow();
2952 
2953     setWindowRule("screen", int(1), int(Rules::ForceTemporarily));
2954 
2955     // The window should be forced the second screen
2956     QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
2957 
2958     // User is not allowed to move it
2959     workspace()->sendWindowToOutput(m_window, outputs.at(0));
2960     QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
2961 
2962     // Close and reopen the window.
2963     destroyTestWindow();
2964     createTestWindow();
2965 
2966     // The rule should be discarded now
2967     QCOMPARE(m_window->output()->name(), outputs.at(0)->name());
2968 
2969     destroyTestWindow();
2970 }
2971 
2972 void TestXdgShellWindowRules::testMatchAfterNameChange()
2973 {
2974     setWindowRule("above", true, int(Rules::Force));
2975 
2976     std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
2977     std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
2978 
2979     auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
2980     QVERIFY(window);
2981     QVERIFY(window->isActive());
2982     QCOMPARE(window->keepAbove(), false);
2983 
2984     QSignalSpy desktopFileNameSpy(window, &Window::desktopFileNameChanged);
2985 
2986     shellSurface->set_app_id(QStringLiteral("org.kde.foo"));
2987     QVERIFY(desktopFileNameSpy.wait());
2988     QCOMPARE(window->keepAbove(), true);
2989 }
2990 
2991 void TestXdgShellWindowRules::testLayerDontAffect()
2992 {
2993     setWindowRule("layer", QStringLiteral("overlay"), int(Rules::DontAffect));
2994 
2995     createTestWindow();
2996 
2997     // The layer should not be affected by the rule.
2998     QCOMPARE(m_window->layer(), NormalLayer);
2999 
3000     destroyTestWindow();
3001 }
3002 
3003 void TestXdgShellWindowRules::testLayerForce()
3004 {
3005     setWindowRule("layer", QStringLiteral("overlay"), int(Rules::Force));
3006 
3007     createTestWindow();
3008     QCOMPARE(m_window->layer(), OverlayLayer);
3009 
3010     destroyTestWindow();
3011 }
3012 
3013 void TestXdgShellWindowRules::testLayerForceTemporarily()
3014 {
3015     setWindowRule("layer", QStringLiteral("overlay"), int(Rules::ForceTemporarily));
3016 
3017     createTestWindow();
3018     QCOMPARE(m_window->layer(), OverlayLayer);
3019 
3020     // The rule should be discarded when the window is closed.
3021     destroyTestWindow();
3022     createTestWindow();
3023     QCOMPARE(m_window->layer(), NormalLayer);
3024 
3025     destroyTestWindow();
3026 }
3027 
3028 void TestXdgShellWindowRules::testCloseableDontAffect()
3029 {
3030     setWindowRule("closeable", false, int(Rules::DontAffect));
3031 
3032     createTestWindow();
3033 
3034     QVERIFY(m_window->isCloseable());
3035 
3036     destroyTestWindow();
3037 }
3038 
3039 void TestXdgShellWindowRules::testCloseableForce()
3040 {
3041     setWindowRule("closeable", false, int(Rules::Force));
3042 
3043     createTestWindow();
3044     QVERIFY(!m_window->isCloseable());
3045 
3046     destroyTestWindow();
3047 }
3048 
3049 void TestXdgShellWindowRules::testCloseableForceTemporarily()
3050 {
3051     setWindowRule("closeable", false, int(Rules::ForceTemporarily));
3052 
3053     createTestWindow();
3054     QVERIFY(!m_window->isCloseable());
3055 
3056     // The rule should be discarded when the window is closed.
3057     destroyTestWindow();
3058     createTestWindow();
3059     QVERIFY(m_window->isCloseable());
3060 
3061     destroyTestWindow();
3062 }
3063 
3064 WAYLANDTEST_MAIN(TestXdgShellWindowRules)
3065 #include "xdgshellwindow_rules_test.moc"