Warning, file /plasma/kwin/autotests/integration/xdgshellwindow_rules_test.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

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