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