KWin
Loading...
Searching...
No Matches
xdgshellwindow_rules_test.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2017 Martin Flöser <mgraesslin@kde.org>
6 SPDX-FileCopyrightText: 2019 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
7 SPDX-FileCopyrightText: 2022 Ismael Asensio <isma.af@gmail.com>
8
9 SPDX-License-Identifier: GPL-2.0-or-later
10*/
11
12#include "kwin_wayland_test.h"
13
14#include "core/output.h"
16#include "cursor.h"
17#include "rules.h"
18#include "virtualdesktops.h"
19#include "wayland_server.h"
20#include "window.h"
21#include "workspace.h"
22
23#include <KWayland/Client/surface.h>
24
25#include <linux/input.h>
26
27using namespace KWin;
28
29static const QString s_socketName = QStringLiteral("wayland_test_kwin_xdgshellwindow_rules-0");
30
31class TestXdgShellWindowRules : public QObject
32{
33 Q_OBJECT
34
35 enum ClientFlag {
36 None = 0,
37 ClientShouldBeInactive = 1 << 0, // Window should be inactive. Used on Minimize tests
38 ServerSideDecoration = 1 << 1, // Create window with server side decoration. Used on noBorder tests
39 ReturnAfterSurfaceConfiguration = 1 << 2, // Do not create the window now, but return after surface configuration.
40 };
41 Q_DECLARE_FLAGS(ClientFlags, ClientFlag)
42
43private Q_SLOTS:
44 void initTestCase();
45 void init();
46 void cleanup();
47
48 void testPositionDontAffect();
49 void testPositionApply();
50 void testPositionRemember();
51 void testPositionForce();
52 void testPositionApplyNow();
53 void testPositionForceTemporarily();
54
55 void testSizeDontAffect();
56 void testSizeApply();
57 void testSizeRemember();
58 void testSizeForce();
59 void testSizeApplyNow();
60 void testSizeForceTemporarily();
61
62 void testMaximizeDontAffect();
63 void testMaximizeApply();
64 void testMaximizeRemember();
65 void testMaximizeForce();
66 void testMaximizeApplyNow();
67 void testMaximizeForceTemporarily();
68
69 void testDesktopsDontAffect();
70 void testDesktopsApply();
71 void testDesktopsRemember();
72 void testDesktopsForce();
73 void testDesktopsApplyNow();
74 void testDesktopsForceTemporarily();
75
76 void testMinimizeDontAffect();
77 void testMinimizeApply();
78 void testMinimizeRemember();
79 void testMinimizeForce();
80 void testMinimizeApplyNow();
81 void testMinimizeForceTemporarily();
82
83 void testSkipTaskbarDontAffect();
84 void testSkipTaskbarApply();
85 void testSkipTaskbarRemember();
86 void testSkipTaskbarForce();
87 void testSkipTaskbarApplyNow();
88 void testSkipTaskbarForceTemporarily();
89
90 void testSkipPagerDontAffect();
91 void testSkipPagerApply();
92 void testSkipPagerRemember();
93 void testSkipPagerForce();
94 void testSkipPagerApplyNow();
95 void testSkipPagerForceTemporarily();
96
97 void testSkipSwitcherDontAffect();
98 void testSkipSwitcherApply();
99 void testSkipSwitcherRemember();
100 void testSkipSwitcherForce();
101 void testSkipSwitcherApplyNow();
102 void testSkipSwitcherForceTemporarily();
103
104 void testKeepAboveDontAffect();
105 void testKeepAboveApply();
106 void testKeepAboveRemember();
107 void testKeepAboveForce();
108 void testKeepAboveApplyNow();
109 void testKeepAboveForceTemporarily();
110
111 void testKeepBelowDontAffect();
112 void testKeepBelowApply();
113 void testKeepBelowRemember();
114 void testKeepBelowForce();
115 void testKeepBelowApplyNow();
116 void testKeepBelowForceTemporarily();
117
118 void testShortcutDontAffect();
119 void testShortcutApply();
120 void testShortcutRemember();
121 void testShortcutForce();
122 void testShortcutApplyNow();
123 void testShortcutForceTemporarily();
124
125 void testDesktopFileDontAffect();
126 void testDesktopFileApply();
127 void testDesktopFileRemember();
128 void testDesktopFileForce();
129 void testDesktopFileApplyNow();
130 void testDesktopFileForceTemporarily();
131
132 void testActiveOpacityDontAffect();
133 void testActiveOpacityForce();
134 void testActiveOpacityForceTemporarily();
135
136 void testInactiveOpacityDontAffect();
137 void testInactiveOpacityForce();
138 void testInactiveOpacityForceTemporarily();
139
140 void testNoBorderDontAffect();
141 void testNoBorderApply();
142 void testNoBorderRemember();
143 void testNoBorderForce();
144 void testNoBorderApplyNow();
145 void testNoBorderForceTemporarily();
146
147 void testScreenDontAffect();
148 void testScreenApply();
149 void testScreenRemember();
150 void testScreenForce();
151 void testScreenApplyNow();
152 void testScreenForceTemporarily();
153
154 void testLayerDontAffect();
155 void testLayerForce();
156 void testLayerForceTemporarily();
157
158 void testCloseableDontAffect();
159 void testCloseableForce();
160 void testCloseableForceTemporarily();
161
162 void testMatchAfterNameChange();
163
164private:
165 void createTestWindow(ClientFlags flags = None);
166 void mapClientToSurface(QSize clientSize, ClientFlags flags = None);
167 void destroyTestWindow();
168
169 template<typename T>
170 void setWindowRule(const QString &property, const T &value, int policy);
171
172private:
173 KSharedConfig::Ptr m_config;
174
175 Window *m_window;
176 std::unique_ptr<KWayland::Client::Surface> m_surface;
177 std::unique_ptr<Test::XdgToplevel> m_shellSurface;
178
179 std::unique_ptr<QSignalSpy> m_toplevelConfigureRequestedSpy;
180 std::unique_ptr<QSignalSpy> m_surfaceConfigureRequestedSpy;
181};
182
183void TestXdgShellWindowRules::initTestCase()
184{
185 qRegisterMetaType<KWin::Window *>();
186
187 QSignalSpy applicationStartedSpy(kwinApp(), &Application::started);
188 QVERIFY(waylandServer()->init(s_socketName));
190 QRect(0, 0, 1280, 1024),
191 QRect(1280, 0, 1280, 1024),
192 });
193
194 kwinApp()->start();
195 QVERIFY(applicationStartedSpy.wait());
196 const auto outputs = workspace()->outputs();
197 QCOMPARE(outputs.count(), 2);
198 QCOMPARE(outputs[0]->geometry(), QRect(0, 0, 1280, 1024));
199 QCOMPARE(outputs[1]->geometry(), QRect(1280, 0, 1280, 1024));
200
201 m_config = KSharedConfig::openConfig(QStringLiteral("kwinrulesrc"), KConfig::SimpleConfig);
202 workspace()->rulebook()->setConfig(m_config);
203}
204
205void TestXdgShellWindowRules::init()
206{
207 VirtualDesktopManager::self()->setCurrent(VirtualDesktopManager::self()->desktops().first());
208 QVERIFY(Test::setupWaylandConnection(Test::AdditionalWaylandInterface::XdgDecorationV1));
209
210 workspace()->setActiveOutput(QPoint(640, 512));
211}
212
213void TestXdgShellWindowRules::cleanup()
214{
215 if (m_shellSurface) {
216 destroyTestWindow();
217 }
218
220
221 // Wipe the window rule config clean.
222 for (const QString &group : m_config->groupList()) {
223 m_config->deleteGroup(group);
224 }
226
227 // Restore virtual desktops to the initial state.
228 VirtualDesktopManager::self()->setCount(1);
229 QCOMPARE(VirtualDesktopManager::self()->count(), 1u);
230}
231
232void TestXdgShellWindowRules::createTestWindow(ClientFlags flags)
233{
234 // Apply flags for special windows and rules
235 const bool createClient = !(flags & ReturnAfterSurfaceConfiguration);
236 const auto decorationMode = (flags & ServerSideDecoration) ? Test::XdgToplevelDecorationV1::mode_server_side
237 : Test::XdgToplevelDecorationV1::mode_client_side;
238 // Create an xdg surface.
239 m_surface = Test::createSurface();
240 m_shellSurface.reset(Test::createXdgToplevelSurface(m_surface.get(), Test::CreationSetup::CreateOnly, m_surface.get()));
241 Test::XdgToplevelDecorationV1 *decoration = Test::createXdgToplevelDecorationV1(m_shellSurface.get(), m_shellSurface.get());
242
243 // Add signal watchers
244 m_toplevelConfigureRequestedSpy = std::make_unique<QSignalSpy>(m_shellSurface.get(), &Test::XdgToplevel::configureRequested);
245 m_surfaceConfigureRequestedSpy = std::make_unique<QSignalSpy>(m_shellSurface->xdgSurface(), &Test::XdgSurface::configureRequested);
246
247 m_shellSurface->set_app_id(QStringLiteral("org.kde.foo"));
248 decoration->set_mode(decorationMode);
249
250 // Wait for the initial configure event
251 m_surface->commit(KWayland::Client::Surface::CommitFlag::None);
252 QVERIFY(m_surfaceConfigureRequestedSpy->wait());
253
254 if (createClient) {
255 mapClientToSurface(QSize(100, 50), flags);
256 }
257}
258
259void TestXdgShellWindowRules::mapClientToSurface(QSize clientSize, ClientFlags flags)
260{
261 const bool clientShouldBeActive = !(flags & ClientShouldBeInactive);
262
263 QVERIFY(m_surface != nullptr);
264 QVERIFY(m_shellSurface != nullptr);
265 QVERIFY(m_surfaceConfigureRequestedSpy != nullptr);
266
267 // Draw content of the surface.
268 m_shellSurface->xdgSurface()->ack_configure(m_surfaceConfigureRequestedSpy->last().at(0).value<quint32>());
269
270 // Create the window
271 m_window = Test::renderAndWaitForShown(m_surface.get(), clientSize, Qt::blue);
272 QVERIFY(m_window);
273 QCOMPARE(m_window->isActive(), clientShouldBeActive);
274}
275
276void TestXdgShellWindowRules::destroyTestWindow()
277{
278 m_surfaceConfigureRequestedSpy.reset();
279 m_toplevelConfigureRequestedSpy.reset();
280 m_shellSurface.reset();
281 m_surface.reset();
282 QVERIFY(Test::waitForWindowClosed(m_window));
283}
284
285template<typename T>
286void TestXdgShellWindowRules::setWindowRule(const QString &property, const T &value, int policy)
287{
288 // Initialize RuleBook with the test rule.
289 m_config->group(QStringLiteral("General")).writeEntry("count", 1);
290 KConfigGroup group = m_config->group(QStringLiteral("1"));
291
292 group.writeEntry(property, value);
293 group.writeEntry(QStringLiteral("%1rule").arg(property), policy);
294
295 group.writeEntry("wmclass", "org.kde.foo");
296 group.writeEntry("wmclasscomplete", false);
297 group.writeEntry("wmclassmatch", int(Rules::ExactMatch));
298 group.sync();
299
301}
302
303void TestXdgShellWindowRules::testPositionDontAffect()
304{
305 setWindowRule("position", QPoint(42, 42), int(Rules::DontAffect));
306
307 createTestWindow();
308
309 // The position of the window should not be affected by the rule. The default
310 // placement policy will put the window in the top-left corner of the screen.
311 QVERIFY(m_window->isMovable());
312 QVERIFY(m_window->isMovableAcrossScreens());
313 QCOMPARE(m_window->pos(), QPoint(0, 0));
314
315 destroyTestWindow();
316}
317
318void TestXdgShellWindowRules::testPositionApply()
319{
320 setWindowRule("position", QPoint(42, 42), int(Rules::Apply));
321
322 createTestWindow();
323
324 // The window should be moved to the position specified by the rule.
325 QVERIFY(m_window->isMovable());
326 QVERIFY(m_window->isMovableAcrossScreens());
327 QCOMPARE(m_window->pos(), QPoint(42, 42));
328
329 // One should still be able to move the window around.
330 QSignalSpy interactiveMoveResizeStartedSpy(m_window, &Window::interactiveMoveResizeStarted);
331 QSignalSpy interactiveMoveResizeSteppedSpy(m_window, &Window::interactiveMoveResizeStepped);
332 QSignalSpy interactiveMoveResizeFinishedSpy(m_window, &Window::interactiveMoveResizeFinished);
333
334 QCOMPARE(workspace()->moveResizeWindow(), nullptr);
335 QVERIFY(!m_window->isInteractiveMove());
336 QVERIFY(!m_window->isInteractiveResize());
338 QCOMPARE(workspace()->moveResizeWindow(), m_window);
339 QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1);
340 QVERIFY(m_window->isInteractiveMove());
341 QVERIFY(!m_window->isInteractiveResize());
342
343 const QPointF cursorPos = KWin::Cursors::self()->mouse()->pos();
344 m_window->keyPressEvent(Qt::Key_Right);
345 m_window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos());
346 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 0));
347 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
348 QCOMPARE(m_window->pos(), QPoint(50, 42));
349
350 m_window->keyPressEvent(Qt::Key_Enter);
351 QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 1);
352 QCOMPARE(workspace()->moveResizeWindow(), nullptr);
353 QVERIFY(!m_window->isInteractiveMove());
354 QVERIFY(!m_window->isInteractiveResize());
355 QCOMPARE(m_window->pos(), QPoint(50, 42));
356
357 // The rule should be applied again if the window appears after it's been closed.
358 destroyTestWindow();
359 createTestWindow();
360
361 QVERIFY(m_window->isMovable());
362 QVERIFY(m_window->isMovableAcrossScreens());
363 QCOMPARE(m_window->pos(), QPoint(42, 42));
364
365 destroyTestWindow();
366}
367
368void TestXdgShellWindowRules::testPositionRemember()
369{
370 setWindowRule("position", QPoint(42, 42), int(Rules::Remember));
371 createTestWindow();
372
373 // The window should be moved to the position specified by the rule.
374 QVERIFY(m_window->isMovable());
375 QVERIFY(m_window->isMovableAcrossScreens());
376 QCOMPARE(m_window->pos(), QPoint(42, 42));
377
378 // One should still be able to move the window around.
379 QSignalSpy interactiveMoveResizeStartedSpy(m_window, &Window::interactiveMoveResizeStarted);
380 QSignalSpy interactiveMoveResizeSteppedSpy(m_window, &Window::interactiveMoveResizeStepped);
381 QSignalSpy interactiveMoveResizeFinishedSpy(m_window, &Window::interactiveMoveResizeFinished);
382
383 QCOMPARE(workspace()->moveResizeWindow(), nullptr);
384 QVERIFY(!m_window->isInteractiveMove());
385 QVERIFY(!m_window->isInteractiveResize());
387 QCOMPARE(workspace()->moveResizeWindow(), m_window);
388 QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1);
389 QVERIFY(m_window->isInteractiveMove());
390 QVERIFY(!m_window->isInteractiveResize());
391
392 const QPointF cursorPos = KWin::Cursors::self()->mouse()->pos();
393 m_window->keyPressEvent(Qt::Key_Right);
394 m_window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos());
395 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 0));
396 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
397 QCOMPARE(m_window->pos(), QPoint(50, 42));
398
399 m_window->keyPressEvent(Qt::Key_Enter);
400 QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 1);
401 QCOMPARE(workspace()->moveResizeWindow(), nullptr);
402 QVERIFY(!m_window->isInteractiveMove());
403 QVERIFY(!m_window->isInteractiveResize());
404 QCOMPARE(m_window->pos(), QPoint(50, 42));
405
406 // The window should be placed at the last know position if we reopen it.
407 destroyTestWindow();
408 createTestWindow();
409
410 QVERIFY(m_window->isMovable());
411 QVERIFY(m_window->isMovableAcrossScreens());
412 QCOMPARE(m_window->pos(), QPoint(50, 42));
413
414 destroyTestWindow();
415}
416
417void TestXdgShellWindowRules::testPositionForce()
418{
419 setWindowRule("position", QPoint(42, 42), int(Rules::Force));
420
421 createTestWindow();
422
423 // The window should be moved to the position specified by the rule.
424 QVERIFY(!m_window->isMovable());
425 QVERIFY(!m_window->isMovableAcrossScreens());
426 QCOMPARE(m_window->pos(), QPoint(42, 42));
427
428 // User should not be able to move the window.
429 QSignalSpy interactiveMoveResizeStartedSpy(m_window, &Window::interactiveMoveResizeStarted);
430 QCOMPARE(workspace()->moveResizeWindow(), nullptr);
431 QVERIFY(!m_window->isInteractiveMove());
432 QVERIFY(!m_window->isInteractiveResize());
434 QCOMPARE(workspace()->moveResizeWindow(), nullptr);
435 QCOMPARE(interactiveMoveResizeStartedSpy.count(), 0);
436 QVERIFY(!m_window->isInteractiveMove());
437 QVERIFY(!m_window->isInteractiveResize());
438
439 // The position should still be forced if we reopen the window.
440 destroyTestWindow();
441 createTestWindow();
442
443 QVERIFY(!m_window->isMovable());
444 QVERIFY(!m_window->isMovableAcrossScreens());
445 QCOMPARE(m_window->pos(), QPoint(42, 42));
446
447 destroyTestWindow();
448}
449
450void TestXdgShellWindowRules::testPositionApplyNow()
451{
452 createTestWindow();
453
454 // The position of the window isn't set by any rule, thus the default placement
455 // policy will try to put the window in the top-left corner of the screen.
456 QVERIFY(m_window->isMovable());
457 QVERIFY(m_window->isMovableAcrossScreens());
458 QCOMPARE(m_window->pos(), QPoint(0, 0));
459
460 QSignalSpy frameGeometryChangedSpy(m_window, &Window::frameGeometryChanged);
461
462 setWindowRule("position", QPoint(42, 42), int(Rules::ApplyNow));
463
464 // The window should be moved to the position specified by the rule.
465 QCOMPARE(frameGeometryChangedSpy.count(), 1);
466 QCOMPARE(m_window->pos(), QPoint(42, 42));
467
468 // We still have to be able to move the window around.
469 QVERIFY(m_window->isMovable());
470 QVERIFY(m_window->isMovableAcrossScreens());
471 QSignalSpy interactiveMoveResizeStartedSpy(m_window, &Window::interactiveMoveResizeStarted);
472 QSignalSpy interactiveMoveResizeSteppedSpy(m_window, &Window::interactiveMoveResizeStepped);
473 QSignalSpy interactiveMoveResizeFinishedSpy(m_window, &Window::interactiveMoveResizeFinished);
474
475 QCOMPARE(workspace()->moveResizeWindow(), nullptr);
476 QVERIFY(!m_window->isInteractiveMove());
477 QVERIFY(!m_window->isInteractiveResize());
479 QCOMPARE(workspace()->moveResizeWindow(), m_window);
480 QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1);
481 QVERIFY(m_window->isInteractiveMove());
482 QVERIFY(!m_window->isInteractiveResize());
483
484 const QPointF cursorPos = KWin::Cursors::self()->mouse()->pos();
485 m_window->keyPressEvent(Qt::Key_Right);
486 m_window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos());
487 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 0));
488 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
489 QCOMPARE(m_window->pos(), QPoint(50, 42));
490
491 m_window->keyPressEvent(Qt::Key_Enter);
492 QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 1);
493 QCOMPARE(workspace()->moveResizeWindow(), nullptr);
494 QVERIFY(!m_window->isInteractiveMove());
495 QVERIFY(!m_window->isInteractiveResize());
496 QCOMPARE(m_window->pos(), QPoint(50, 42));
497
498 // The rule should not be applied again.
499 m_window->evaluateWindowRules();
500 QCOMPARE(m_window->pos(), QPoint(50, 42));
501
502 destroyTestWindow();
503}
504
505void TestXdgShellWindowRules::testPositionForceTemporarily()
506{
507 setWindowRule("position", QPoint(42, 42), int(Rules::ForceTemporarily));
508
509 createTestWindow();
510
511 // The window should be moved to the position specified by the rule.
512 QVERIFY(!m_window->isMovable());
513 QVERIFY(!m_window->isMovableAcrossScreens());
514 QCOMPARE(m_window->pos(), QPoint(42, 42));
515
516 // User should not be able to move the window.
517 QSignalSpy interactiveMoveResizeStartedSpy(m_window, &Window::interactiveMoveResizeStarted);
518 QCOMPARE(workspace()->moveResizeWindow(), nullptr);
519 QVERIFY(!m_window->isInteractiveMove());
520 QVERIFY(!m_window->isInteractiveResize());
522 QCOMPARE(workspace()->moveResizeWindow(), nullptr);
523 QCOMPARE(interactiveMoveResizeStartedSpy.count(), 0);
524 QVERIFY(!m_window->isInteractiveMove());
525 QVERIFY(!m_window->isInteractiveResize());
526
527 // The rule should be discarded if we close the window.
528 destroyTestWindow();
529 createTestWindow();
530
531 QVERIFY(m_window->isMovable());
532 QVERIFY(m_window->isMovableAcrossScreens());
533 QCOMPARE(m_window->pos(), QPoint(0, 0));
534
535 destroyTestWindow();
536}
537
538void TestXdgShellWindowRules::testSizeDontAffect()
539{
540 setWindowRule("size", QSize(480, 640), int(Rules::DontAffect));
541
542 createTestWindow(ReturnAfterSurfaceConfiguration);
543
544 // The window size shouldn't be enforced by the rule.
545 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
546 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
547 QCOMPARE(m_toplevelConfigureRequestedSpy->last().first().toSize(), QSize(0, 0));
548
549 // Map the window.
550 mapClientToSurface(QSize(100, 50));
551 QVERIFY(m_window->isResizable());
552 QCOMPARE(m_window->size(), QSize(100, 50));
553
554 // We should receive a configure event when the window becomes active.
555 QVERIFY(m_surfaceConfigureRequestedSpy->wait());
556 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
557 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
558
559 destroyTestWindow();
560}
561
562void TestXdgShellWindowRules::testSizeApply()
563{
564 setWindowRule("size", QSize(480, 640), int(Rules::Apply));
565
566 createTestWindow(ReturnAfterSurfaceConfiguration);
567
568 // The initial configure event should contain size hint set by the rule.
569 Test::XdgToplevel::States states;
570 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
571 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
572 QCOMPARE(m_toplevelConfigureRequestedSpy->last().at(0).toSize(), QSize(480, 640));
573 states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
574 QVERIFY(!states.testFlag(Test::XdgToplevel::State::Activated));
575 QVERIFY(!states.testFlag(Test::XdgToplevel::State::Resizing));
576
577 // Map the window.
578 mapClientToSurface(QSize(480, 640));
579 QVERIFY(m_window->isResizable());
580 QCOMPARE(m_window->size(), QSize(480, 640));
581
582 // We should receive a configure event when the window becomes active.
583 QVERIFY(m_surfaceConfigureRequestedSpy->wait());
584 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
585 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
586 states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
587 QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
588 QVERIFY(!states.testFlag(Test::XdgToplevel::State::Resizing));
589
590 // One still should be able to resize the window.
591 QSignalSpy frameGeometryChangedSpy(m_window, &Window::frameGeometryChanged);
592 QSignalSpy interactiveMoveResizeStartedSpy(m_window, &Window::interactiveMoveResizeStarted);
593 QSignalSpy interactiveMoveResizeSteppedSpy(m_window, &Window::interactiveMoveResizeStepped);
594 QSignalSpy interactiveMoveResizeFinishedSpy(m_window, &Window::interactiveMoveResizeFinished);
595
596 QCOMPARE(workspace()->moveResizeWindow(), nullptr);
597 QVERIFY(!m_window->isInteractiveMove());
598 QVERIFY(!m_window->isInteractiveResize());
600 QCOMPARE(workspace()->moveResizeWindow(), m_window);
601 QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1);
602 QVERIFY(!m_window->isInteractiveMove());
603 QVERIFY(m_window->isInteractiveResize());
604 QVERIFY(m_surfaceConfigureRequestedSpy->wait());
605 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 3);
606 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 3);
607 states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
608 QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
609 QVERIFY(states.testFlag(Test::XdgToplevel::State::Resizing));
610 m_shellSurface->xdgSurface()->ack_configure(m_surfaceConfigureRequestedSpy->last().at(0).value<quint32>());
611
612 const QPointF cursorPos = KWin::Cursors::self()->mouse()->pos();
613 m_window->keyPressEvent(Qt::Key_Right);
614 m_window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos());
615 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 0));
616 QVERIFY(m_surfaceConfigureRequestedSpy->wait());
617 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 4);
618 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 4);
619 states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
620 QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
621 QVERIFY(states.testFlag(Test::XdgToplevel::State::Resizing));
622 QCOMPARE(m_toplevelConfigureRequestedSpy->last().at(0).toSize(), QSize(488, 640));
623 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
624 m_shellSurface->xdgSurface()->ack_configure(m_surfaceConfigureRequestedSpy->last().at(0).value<quint32>());
625 Test::render(m_surface.get(), QSize(488, 640), Qt::blue);
626 QVERIFY(frameGeometryChangedSpy.wait());
627 QCOMPARE(m_window->size(), QSize(488, 640));
628 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
629
630 m_window->keyPressEvent(Qt::Key_Enter);
631 QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 1);
632 QCOMPARE(workspace()->moveResizeWindow(), nullptr);
633 QVERIFY(!m_window->isInteractiveMove());
634 QVERIFY(!m_window->isInteractiveResize());
635
636 QVERIFY(m_surfaceConfigureRequestedSpy->wait());
637 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 5);
638 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 5);
639
640 // The rule should be applied again if the window appears after it's been closed.
641 destroyTestWindow();
642 createTestWindow(ReturnAfterSurfaceConfiguration);
643
644 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
645 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
646 QCOMPARE(m_toplevelConfigureRequestedSpy->last().first().toSize(), QSize(480, 640));
647
648 mapClientToSurface(QSize(480, 640));
649 QVERIFY(m_window->isResizable());
650 QCOMPARE(m_window->size(), QSize(480, 640));
651
652 QVERIFY(m_surfaceConfigureRequestedSpy->wait());
653 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
654 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
655
656 destroyTestWindow();
657}
658
659void TestXdgShellWindowRules::testSizeRemember()
660{
661 setWindowRule("size", QSize(480, 640), int(Rules::Remember));
662
663 createTestWindow(ReturnAfterSurfaceConfiguration);
664
665 // The initial configure event should contain size hint set by the rule.
666 Test::XdgToplevel::States states;
667 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
668 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
669 QCOMPARE(m_toplevelConfigureRequestedSpy->last().first().toSize(), QSize(480, 640));
670 states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
671 QVERIFY(!states.testFlag(Test::XdgToplevel::State::Activated));
672 QVERIFY(!states.testFlag(Test::XdgToplevel::State::Resizing));
673
674 // Map the window.
675 mapClientToSurface(QSize(480, 640));
676 QVERIFY(m_window->isResizable());
677 QCOMPARE(m_window->size(), QSize(480, 640));
678
679 // We should receive a configure event when the window becomes active.
680 QVERIFY(m_surfaceConfigureRequestedSpy->wait());
681 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
682 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
683 states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
684 QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
685 QVERIFY(!states.testFlag(Test::XdgToplevel::State::Resizing));
686
687 // One should still be able to resize the window.
688 QSignalSpy frameGeometryChangedSpy(m_window, &Window::frameGeometryChanged);
689 QSignalSpy interactiveMoveResizeStartedSpy(m_window, &Window::interactiveMoveResizeStarted);
690 QSignalSpy interactiveMoveResizeSteppedSpy(m_window, &Window::interactiveMoveResizeStepped);
691 QSignalSpy interactiveMoveResizeFinishedSpy(m_window, &Window::interactiveMoveResizeFinished);
692
693 QCOMPARE(workspace()->moveResizeWindow(), nullptr);
694 QVERIFY(!m_window->isInteractiveMove());
695 QVERIFY(!m_window->isInteractiveResize());
697 QCOMPARE(workspace()->moveResizeWindow(), m_window);
698 QCOMPARE(interactiveMoveResizeStartedSpy.count(), 1);
699 QVERIFY(!m_window->isInteractiveMove());
700 QVERIFY(m_window->isInteractiveResize());
701 QVERIFY(m_surfaceConfigureRequestedSpy->wait());
702 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 3);
703 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 3);
704 states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
705 QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
706 QVERIFY(states.testFlag(Test::XdgToplevel::State::Resizing));
707 m_shellSurface->xdgSurface()->ack_configure(m_surfaceConfigureRequestedSpy->last().at(0).value<quint32>());
708
709 const QPointF cursorPos = KWin::Cursors::self()->mouse()->pos();
710 m_window->keyPressEvent(Qt::Key_Right);
711 m_window->updateInteractiveMoveResize(KWin::Cursors::self()->mouse()->pos());
712 QCOMPARE(KWin::Cursors::self()->mouse()->pos(), cursorPos + QPoint(8, 0));
713 QVERIFY(m_surfaceConfigureRequestedSpy->wait());
714 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 4);
715 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 4);
716 states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
717 QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
718 QVERIFY(states.testFlag(Test::XdgToplevel::State::Resizing));
719 QCOMPARE(m_toplevelConfigureRequestedSpy->last().at(0).toSize(), QSize(488, 640));
720 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
721 m_shellSurface->xdgSurface()->ack_configure(m_surfaceConfigureRequestedSpy->last().at(0).value<quint32>());
722 Test::render(m_surface.get(), QSize(488, 640), Qt::blue);
723 QVERIFY(frameGeometryChangedSpy.wait());
724 QCOMPARE(m_window->size(), QSize(488, 640));
725 QCOMPARE(interactiveMoveResizeSteppedSpy.count(), 1);
726
727 m_window->keyPressEvent(Qt::Key_Enter);
728 QCOMPARE(interactiveMoveResizeFinishedSpy.count(), 1);
729 QCOMPARE(workspace()->moveResizeWindow(), nullptr);
730 QVERIFY(!m_window->isInteractiveMove());
731 QVERIFY(!m_window->isInteractiveResize());
732
733 QVERIFY(m_surfaceConfigureRequestedSpy->wait());
734 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 5);
735 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 5);
736
737 // If the window appears again, it should have the last known size.
738 destroyTestWindow();
739 createTestWindow(ReturnAfterSurfaceConfiguration);
740
741 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
742 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
743 QCOMPARE(m_toplevelConfigureRequestedSpy->last().first().toSize(), QSize(488, 640));
744
745 mapClientToSurface(QSize(488, 640));
746 QVERIFY(m_window->isResizable());
747 QCOMPARE(m_window->size(), QSize(488, 640));
748
749 QVERIFY(m_surfaceConfigureRequestedSpy->wait());
750 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
751 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
752
753 destroyTestWindow();
754}
755
756void TestXdgShellWindowRules::testSizeForce()
757{
758 setWindowRule("size", QSize(480, 640), int(Rules::Force));
759
760 createTestWindow(ReturnAfterSurfaceConfiguration);
761
762 // The initial configure event should contain size hint set by the rule.
763 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
764 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
765 QCOMPARE(m_toplevelConfigureRequestedSpy->last().first().toSize(), QSize(480, 640));
766
767 // Map the window.
768 mapClientToSurface(QSize(480, 640));
769 QVERIFY(!m_window->isResizable());
770 QCOMPARE(m_window->size(), QSize(480, 640));
771
772 // We should receive a configure event when the window becomes active.
773 QVERIFY(m_surfaceConfigureRequestedSpy->wait());
774 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
775 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
776
777 // Any attempt to resize the window should not succeed.
778 QSignalSpy interactiveMoveResizeStartedSpy(m_window, &Window::interactiveMoveResizeStarted);
779 QCOMPARE(workspace()->moveResizeWindow(), nullptr);
780 QVERIFY(!m_window->isInteractiveMove());
781 QVERIFY(!m_window->isInteractiveResize());
783 QCOMPARE(workspace()->moveResizeWindow(), nullptr);
784 QCOMPARE(interactiveMoveResizeStartedSpy.count(), 0);
785 QVERIFY(!m_window->isInteractiveMove());
786 QVERIFY(!m_window->isInteractiveResize());
787 QVERIFY(!m_surfaceConfigureRequestedSpy->wait(100));
788
789 // If the window appears again, the size should still be forced.
790 destroyTestWindow();
791 createTestWindow(ReturnAfterSurfaceConfiguration);
792
793 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
794 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
795 QCOMPARE(m_toplevelConfigureRequestedSpy->last().first().toSize(), QSize(480, 640));
796
797 mapClientToSurface(QSize(480, 640));
798 QVERIFY(!m_window->isResizable());
799 QCOMPARE(m_window->size(), QSize(480, 640));
800
801 QVERIFY(m_surfaceConfigureRequestedSpy->wait());
802 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
803 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
804
805 destroyTestWindow();
806}
807
808void TestXdgShellWindowRules::testSizeApplyNow()
809{
810 createTestWindow(ReturnAfterSurfaceConfiguration);
811
812 // The expected surface dimensions should be set by the rule.
813 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
814 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
815 QCOMPARE(m_toplevelConfigureRequestedSpy->last().first().toSize(), QSize(0, 0));
816
817 // Map the window.
818 mapClientToSurface(QSize(100, 50));
819 QVERIFY(m_window->isResizable());
820 QCOMPARE(m_window->size(), QSize(100, 50));
821
822 // We should receive a configure event when the window becomes active.
823 QVERIFY(m_surfaceConfigureRequestedSpy->wait());
824 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
825 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
826
827 setWindowRule("size", QSize(480, 640), int(Rules::ApplyNow));
828
829 // The compositor should send a configure event with a new size.
830 QVERIFY(m_surfaceConfigureRequestedSpy->wait());
831 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 3);
832 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 3);
833 QCOMPARE(m_toplevelConfigureRequestedSpy->last().first().toSize(), QSize(480, 640));
834
835 // Draw the surface with the new size.
836 QSignalSpy frameGeometryChangedSpy(m_window, &Window::frameGeometryChanged);
837 m_shellSurface->xdgSurface()->ack_configure(m_surfaceConfigureRequestedSpy->last().at(0).value<quint32>());
838 Test::render(m_surface.get(), QSize(480, 640), Qt::blue);
839 QVERIFY(frameGeometryChangedSpy.wait());
840 QCOMPARE(m_window->size(), QSize(480, 640));
841 QVERIFY(!m_surfaceConfigureRequestedSpy->wait(100));
842
843 // The rule should not be applied again.
844 m_window->evaluateWindowRules();
845 QVERIFY(!m_surfaceConfigureRequestedSpy->wait(100));
846
847 destroyTestWindow();
848}
849
850void TestXdgShellWindowRules::testSizeForceTemporarily()
851{
852 setWindowRule("size", QSize(480, 640), int(Rules::ForceTemporarily));
853
854 createTestWindow(ReturnAfterSurfaceConfiguration);
855
856 // The initial configure event should contain size hint set by the rule.
857 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
858 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
859 QCOMPARE(m_toplevelConfigureRequestedSpy->last().first().toSize(), QSize(480, 640));
860
861 // Map the window.
862 mapClientToSurface(QSize(480, 640));
863 QVERIFY(!m_window->isResizable());
864 QCOMPARE(m_window->size(), QSize(480, 640));
865
866 // We should receive a configure event when the window becomes active.
867 QVERIFY(m_surfaceConfigureRequestedSpy->wait());
868 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
869 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
870
871 // Any attempt to resize the window should not succeed.
872 QSignalSpy interactiveMoveResizeStartedSpy(m_window, &Window::interactiveMoveResizeStarted);
873 QCOMPARE(workspace()->moveResizeWindow(), nullptr);
874 QVERIFY(!m_window->isInteractiveMove());
875 QVERIFY(!m_window->isInteractiveResize());
877 QCOMPARE(workspace()->moveResizeWindow(), nullptr);
878 QCOMPARE(interactiveMoveResizeStartedSpy.count(), 0);
879 QVERIFY(!m_window->isInteractiveMove());
880 QVERIFY(!m_window->isInteractiveResize());
881 QVERIFY(!m_surfaceConfigureRequestedSpy->wait(100));
882
883 // The rule should be discarded when the window is closed.
884 destroyTestWindow();
885 createTestWindow(ReturnAfterSurfaceConfiguration);
886
887 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
888 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
889 QCOMPARE(m_toplevelConfigureRequestedSpy->last().first().toSize(), QSize(0, 0));
890
891 mapClientToSurface(QSize(100, 50));
892 QVERIFY(m_window->isResizable());
893 QCOMPARE(m_window->size(), QSize(100, 50));
894
895 QVERIFY(m_surfaceConfigureRequestedSpy->wait());
896 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
897 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
898
899 destroyTestWindow();
900}
901
902void TestXdgShellWindowRules::testMaximizeDontAffect()
903{
904 setWindowRule("maximizehoriz", true, int(Rules::DontAffect));
905 setWindowRule("maximizevert", true, int(Rules::DontAffect));
906
907 createTestWindow(ReturnAfterSurfaceConfiguration);
908
909 // Wait for the initial configure event.
910 Test::XdgToplevel::States states;
911 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
912 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
913 QCOMPARE(m_toplevelConfigureRequestedSpy->last().at(0).toSize(), QSize(0, 0));
914 states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
915 QVERIFY(!states.testFlag(Test::XdgToplevel::State::Activated));
916 QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
917
918 // Map the window.
919 mapClientToSurface(QSize(100, 50));
920
921 QVERIFY(m_window->isMaximizable());
922 QCOMPARE(m_window->maximizeMode(), MaximizeMode::MaximizeRestore);
923 QCOMPARE(m_window->requestedMaximizeMode(), MaximizeMode::MaximizeRestore);
924 QCOMPARE(m_window->size(), QSize(100, 50));
925
926 // We should receive a configure event when the window becomes active.
927 QVERIFY(m_surfaceConfigureRequestedSpy->wait());
928 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
929 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
930 states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
931 QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
932 QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
933
934 destroyTestWindow();
935}
936
937void TestXdgShellWindowRules::testMaximizeApply()
938{
939 setWindowRule("maximizehoriz", true, int(Rules::Apply));
940 setWindowRule("maximizevert", true, int(Rules::Apply));
941
942 createTestWindow(ReturnAfterSurfaceConfiguration);
943
944 // Wait for the initial configure event.
945 Test::XdgToplevel::States states;
946 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
947 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
948 QCOMPARE(m_toplevelConfigureRequestedSpy->last().at(0).toSize(), QSize(1280, 1024));
949 states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
950 QVERIFY(!states.testFlag(Test::XdgToplevel::State::Activated));
951 QVERIFY(states.testFlag(Test::XdgToplevel::State::Maximized));
952
953 // Map the window.
954 mapClientToSurface(QSize(1280, 1024));
955
956 QVERIFY(m_window->isMaximizable());
957 QCOMPARE(m_window->maximizeMode(), MaximizeMode::MaximizeFull);
958 QCOMPARE(m_window->requestedMaximizeMode(), MaximizeMode::MaximizeFull);
959 QCOMPARE(m_window->size(), QSize(1280, 1024));
960
961 // We should receive a configure event when the window becomes active.
962 QVERIFY(m_surfaceConfigureRequestedSpy->wait());
963 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
964 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
965 states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
966 QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
967 QVERIFY(states.testFlag(Test::XdgToplevel::State::Maximized));
968
969 // One should still be able to change the maximized state of the window.
971 QVERIFY(m_surfaceConfigureRequestedSpy->wait());
972 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 3);
973 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 3);
974 QCOMPARE(m_toplevelConfigureRequestedSpy->last().at(0).toSize(), QSize(0, 0));
975 states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
976 QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
977 QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
978
979 QSignalSpy frameGeometryChangedSpy(m_window, &Window::frameGeometryChanged);
980 m_shellSurface->xdgSurface()->ack_configure(m_surfaceConfigureRequestedSpy->last().at(0).value<quint32>());
981 Test::render(m_surface.get(), QSize(100, 50), Qt::blue);
982 QVERIFY(frameGeometryChangedSpy.wait());
983 QCOMPARE(m_window->size(), QSize(100, 50));
984 QCOMPARE(m_window->maximizeMode(), MaximizeMode::MaximizeRestore);
985 QCOMPARE(m_window->requestedMaximizeMode(), MaximizeMode::MaximizeRestore);
986
987 // If we create the window again, it should be initially maximized.
988 destroyTestWindow();
989 createTestWindow(ReturnAfterSurfaceConfiguration);
990
991 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
992 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
993 QCOMPARE(m_toplevelConfigureRequestedSpy->last().at(0).toSize(), QSize(1280, 1024));
994 states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
995 QVERIFY(!states.testFlag(Test::XdgToplevel::State::Activated));
996 QVERIFY(states.testFlag(Test::XdgToplevel::State::Maximized));
997
998 mapClientToSurface(QSize(1280, 1024));
999 QVERIFY(m_window->isMaximizable());
1000 QCOMPARE(m_window->maximizeMode(), MaximizeMode::MaximizeFull);
1001 QCOMPARE(m_window->requestedMaximizeMode(), MaximizeMode::MaximizeFull);
1002 QCOMPARE(m_window->size(), QSize(1280, 1024));
1003
1004 QVERIFY(m_surfaceConfigureRequestedSpy->wait());
1005 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
1006 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
1007 states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
1008 QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
1009 QVERIFY(states.testFlag(Test::XdgToplevel::State::Maximized));
1010
1011 destroyTestWindow();
1012}
1013
1014void TestXdgShellWindowRules::testMaximizeRemember()
1015{
1016 setWindowRule("maximizehoriz", true, int(Rules::Remember));
1017 setWindowRule("maximizevert", true, int(Rules::Remember));
1018
1019 createTestWindow(ReturnAfterSurfaceConfiguration);
1020
1021 // Wait for the initial configure event.
1022 Test::XdgToplevel::States states;
1023 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
1024 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
1025 QCOMPARE(m_toplevelConfigureRequestedSpy->last().at(0).toSize(), QSize(1280, 1024));
1026 states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
1027 QVERIFY(!states.testFlag(Test::XdgToplevel::State::Activated));
1028 QVERIFY(states.testFlag(Test::XdgToplevel::State::Maximized));
1029
1030 // Map the window.
1031 mapClientToSurface(QSize(1280, 1024));
1032
1033 QVERIFY(m_window->isMaximizable());
1034 QCOMPARE(m_window->maximizeMode(), MaximizeMode::MaximizeFull);
1035 QCOMPARE(m_window->requestedMaximizeMode(), MaximizeMode::MaximizeFull);
1036 QCOMPARE(m_window->size(), QSize(1280, 1024));
1037
1038 // We should receive a configure event when the window becomes active.
1039 QVERIFY(m_surfaceConfigureRequestedSpy->wait());
1040 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
1041 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
1042 states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
1043 QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
1044 QVERIFY(states.testFlag(Test::XdgToplevel::State::Maximized));
1045
1046 // One should still be able to change the maximized state of the window.
1048 QVERIFY(m_surfaceConfigureRequestedSpy->wait());
1049 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 3);
1050 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 3);
1051 QCOMPARE(m_toplevelConfigureRequestedSpy->last().at(0).toSize(), QSize(0, 0));
1052 states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
1053 QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
1054 QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
1055
1056 QSignalSpy frameGeometryChangedSpy(m_window, &Window::frameGeometryChanged);
1057 m_shellSurface->xdgSurface()->ack_configure(m_surfaceConfigureRequestedSpy->last().at(0).value<quint32>());
1058 Test::render(m_surface.get(), QSize(100, 50), Qt::blue);
1059 QVERIFY(frameGeometryChangedSpy.wait());
1060 QCOMPARE(m_window->size(), QSize(100, 50));
1061 QCOMPARE(m_window->maximizeMode(), MaximizeMode::MaximizeRestore);
1062 QCOMPARE(m_window->requestedMaximizeMode(), MaximizeMode::MaximizeRestore);
1063
1064 // If we create the window again, it should not be maximized (because last time it wasn't).
1065 destroyTestWindow();
1066 createTestWindow(ReturnAfterSurfaceConfiguration);
1067
1068 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
1069 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
1070 QCOMPARE(m_toplevelConfigureRequestedSpy->last().at(0).toSize(), QSize(0, 0));
1071 states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
1072 QVERIFY(!states.testFlag(Test::XdgToplevel::State::Activated));
1073 QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
1074
1075 mapClientToSurface(QSize(100, 50));
1076
1077 QVERIFY(m_window->isMaximizable());
1078 QCOMPARE(m_window->maximizeMode(), MaximizeMode::MaximizeRestore);
1079 QCOMPARE(m_window->requestedMaximizeMode(), MaximizeMode::MaximizeRestore);
1080 QCOMPARE(m_window->size(), QSize(100, 50));
1081
1082 QVERIFY(m_surfaceConfigureRequestedSpy->wait());
1083 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
1084 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
1085 states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
1086 QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
1087 QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
1088
1089 destroyTestWindow();
1090}
1091
1092void TestXdgShellWindowRules::testMaximizeForce()
1093{
1094 setWindowRule("maximizehoriz", true, int(Rules::Force));
1095 setWindowRule("maximizevert", true, int(Rules::Force));
1096
1097 createTestWindow(ReturnAfterSurfaceConfiguration);
1098
1099 // Wait for the initial configure event.
1100 Test::XdgToplevel::States states;
1101 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
1102 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
1103 QCOMPARE(m_toplevelConfigureRequestedSpy->last().at(0).toSize(), QSize(1280, 1024));
1104 states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
1105 QVERIFY(!states.testFlag(Test::XdgToplevel::State::Activated));
1106 QVERIFY(states.testFlag(Test::XdgToplevel::State::Maximized));
1107
1108 // Map the window.
1109 mapClientToSurface(QSize(1280, 1024));
1110
1111 QVERIFY(!m_window->isMaximizable());
1112 QCOMPARE(m_window->maximizeMode(), MaximizeMode::MaximizeFull);
1113 QCOMPARE(m_window->requestedMaximizeMode(), MaximizeMode::MaximizeFull);
1114 QCOMPARE(m_window->size(), QSize(1280, 1024));
1115
1116 // We should receive a configure event when the window becomes active.
1117 QVERIFY(m_surfaceConfigureRequestedSpy->wait());
1118 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
1119 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
1120 states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
1121 QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
1122 QVERIFY(states.testFlag(Test::XdgToplevel::State::Maximized));
1123
1124 // Any attempt to change the maximized state should not succeed.
1125 const QRectF oldGeometry = m_window->frameGeometry();
1127 QVERIFY(!m_surfaceConfigureRequestedSpy->wait(100));
1128 QCOMPARE(m_window->maximizeMode(), MaximizeMode::MaximizeFull);
1129 QCOMPARE(m_window->requestedMaximizeMode(), MaximizeMode::MaximizeFull);
1130 QCOMPARE(m_window->frameGeometry(), oldGeometry);
1131
1132 // If we create the window again, the maximized state should still be forced.
1133 destroyTestWindow();
1134 createTestWindow(ReturnAfterSurfaceConfiguration);
1135
1136 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
1137 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
1138 QCOMPARE(m_toplevelConfigureRequestedSpy->last().at(0).toSize(), QSize(1280, 1024));
1139 states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
1140 QVERIFY(!states.testFlag(Test::XdgToplevel::State::Activated));
1141 QVERIFY(states.testFlag(Test::XdgToplevel::State::Maximized));
1142
1143 mapClientToSurface(QSize(1280, 1024));
1144
1145 QVERIFY(!m_window->isMaximizable());
1146 QCOMPARE(m_window->maximizeMode(), MaximizeMode::MaximizeFull);
1147 QCOMPARE(m_window->requestedMaximizeMode(), MaximizeMode::MaximizeFull);
1148 QCOMPARE(m_window->size(), QSize(1280, 1024));
1149
1150 QVERIFY(m_surfaceConfigureRequestedSpy->wait());
1151 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
1152 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
1153 states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
1154 QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
1155 QVERIFY(states.testFlag(Test::XdgToplevel::State::Maximized));
1156
1157 destroyTestWindow();
1158}
1159
1160void TestXdgShellWindowRules::testMaximizeApplyNow()
1161{
1162 createTestWindow(ReturnAfterSurfaceConfiguration);
1163
1164 // Wait for the initial configure event.
1165 Test::XdgToplevel::States states;
1166 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
1167 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
1168 QCOMPARE(m_toplevelConfigureRequestedSpy->last().at(0).toSize(), QSize(0, 0));
1169 states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
1170 QVERIFY(!states.testFlag(Test::XdgToplevel::State::Activated));
1171 QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
1172
1173 // Map the window.
1174 mapClientToSurface(QSize(100, 50));
1175
1176 QVERIFY(m_window->isMaximizable());
1177 QCOMPARE(m_window->maximizeMode(), MaximizeMode::MaximizeRestore);
1178 QCOMPARE(m_window->requestedMaximizeMode(), MaximizeMode::MaximizeRestore);
1179 QCOMPARE(m_window->size(), QSize(100, 50));
1180
1181 // We should receive a configure event when the window becomes active.
1182 QVERIFY(m_surfaceConfigureRequestedSpy->wait());
1183 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
1184 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
1185 states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
1186 QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
1187 QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
1188
1189 setWindowRule("maximizehoriz", true, int(Rules::ApplyNow));
1190 setWindowRule("maximizevert", true, int(Rules::ApplyNow));
1191
1192 // We should receive a configure event with a new surface size.
1193 QVERIFY(m_surfaceConfigureRequestedSpy->wait());
1194 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 3);
1195 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 3);
1196 QCOMPARE(m_toplevelConfigureRequestedSpy->last().at(0).toSize(), QSize(1280, 1024));
1197 states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
1198 QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
1199 QVERIFY(states.testFlag(Test::XdgToplevel::State::Maximized));
1200
1201 // Draw contents of the maximized client.
1202 QSignalSpy frameGeometryChangedSpy(m_window, &Window::frameGeometryChanged);
1203 m_shellSurface->xdgSurface()->ack_configure(m_surfaceConfigureRequestedSpy->last().at(0).value<quint32>());
1204 Test::render(m_surface.get(), QSize(1280, 1024), Qt::blue);
1205 QVERIFY(frameGeometryChangedSpy.wait());
1206 QCOMPARE(m_window->size(), QSize(1280, 1024));
1207 QCOMPARE(m_window->maximizeMode(), MaximizeMode::MaximizeFull);
1208 QCOMPARE(m_window->requestedMaximizeMode(), MaximizeMode::MaximizeFull);
1209
1210 // The window still has to be maximizeable.
1211 QVERIFY(m_window->isMaximizable());
1212
1213 // Restore the window.
1215 QVERIFY(m_surfaceConfigureRequestedSpy->wait());
1216 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 4);
1217 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 4);
1218 QCOMPARE(m_toplevelConfigureRequestedSpy->last().at(0).toSize(), QSize(100, 50));
1219 states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
1220 QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
1221 QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
1222
1223 m_shellSurface->xdgSurface()->ack_configure(m_surfaceConfigureRequestedSpy->last().at(0).value<quint32>());
1224 Test::render(m_surface.get(), QSize(100, 50), Qt::blue);
1225 QVERIFY(frameGeometryChangedSpy.wait());
1226 QCOMPARE(m_window->size(), QSize(100, 50));
1227 QCOMPARE(m_window->maximizeMode(), MaximizeMode::MaximizeRestore);
1228 QCOMPARE(m_window->requestedMaximizeMode(), MaximizeMode::MaximizeRestore);
1229
1230 // The rule should be discarded after it's been applied.
1231 const QRectF oldGeometry = m_window->frameGeometry();
1232 m_window->evaluateWindowRules();
1233 QVERIFY(!m_surfaceConfigureRequestedSpy->wait(100));
1234 QCOMPARE(m_window->maximizeMode(), MaximizeMode::MaximizeRestore);
1235 QCOMPARE(m_window->requestedMaximizeMode(), MaximizeMode::MaximizeRestore);
1236 QCOMPARE(m_window->frameGeometry(), oldGeometry);
1237
1238 destroyTestWindow();
1239}
1240
1241void TestXdgShellWindowRules::testMaximizeForceTemporarily()
1242{
1243 setWindowRule("maximizehoriz", true, int(Rules::ForceTemporarily));
1244 setWindowRule("maximizevert", true, int(Rules::ForceTemporarily));
1245
1246 createTestWindow(ReturnAfterSurfaceConfiguration);
1247
1248 // Wait for the initial configure event.
1249 Test::XdgToplevel::States states;
1250 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
1251 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
1252 QCOMPARE(m_toplevelConfigureRequestedSpy->last().at(0).toSize(), QSize(1280, 1024));
1253 states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
1254 QVERIFY(!states.testFlag(Test::XdgToplevel::State::Activated));
1255 QVERIFY(states.testFlag(Test::XdgToplevel::State::Maximized));
1256
1257 // Map the window.
1258 mapClientToSurface(QSize(1280, 1024));
1259
1260 QVERIFY(!m_window->isMaximizable());
1261 QCOMPARE(m_window->maximizeMode(), MaximizeMode::MaximizeFull);
1262 QCOMPARE(m_window->requestedMaximizeMode(), MaximizeMode::MaximizeFull);
1263 QCOMPARE(m_window->size(), QSize(1280, 1024));
1264
1265 // We should receive a configure event when the window becomes active.
1266 QVERIFY(m_surfaceConfigureRequestedSpy->wait());
1267 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
1268 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
1269 states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
1270 QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
1271 QVERIFY(states.testFlag(Test::XdgToplevel::State::Maximized));
1272
1273 // Any attempt to change the maximized state should not succeed.
1274 const QRectF oldGeometry = m_window->frameGeometry();
1276 QVERIFY(!m_surfaceConfigureRequestedSpy->wait(100));
1277 QCOMPARE(m_window->maximizeMode(), MaximizeMode::MaximizeFull);
1278 QCOMPARE(m_window->requestedMaximizeMode(), MaximizeMode::MaximizeFull);
1279 QCOMPARE(m_window->frameGeometry(), oldGeometry);
1280
1281 // The rule should be discarded if we close the window.
1282 destroyTestWindow();
1283 createTestWindow(ReturnAfterSurfaceConfiguration);
1284
1285 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 1);
1286 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 1);
1287 QCOMPARE(m_toplevelConfigureRequestedSpy->last().at(0).toSize(), QSize(0, 0));
1288 states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
1289 QVERIFY(!states.testFlag(Test::XdgToplevel::State::Activated));
1290 QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
1291
1292 mapClientToSurface(QSize(100, 50));
1293
1294 QVERIFY(m_window->isMaximizable());
1295 QCOMPARE(m_window->maximizeMode(), MaximizeMode::MaximizeRestore);
1296 QCOMPARE(m_window->requestedMaximizeMode(), MaximizeMode::MaximizeRestore);
1297 QCOMPARE(m_window->size(), QSize(100, 50));
1298
1299 QVERIFY(m_surfaceConfigureRequestedSpy->wait());
1300 QCOMPARE(m_surfaceConfigureRequestedSpy->count(), 2);
1301 QCOMPARE(m_toplevelConfigureRequestedSpy->count(), 2);
1302 states = m_toplevelConfigureRequestedSpy->last().at(1).value<Test::XdgToplevel::States>();
1303 QVERIFY(states.testFlag(Test::XdgToplevel::State::Activated));
1304 QVERIFY(!states.testFlag(Test::XdgToplevel::State::Maximized));
1305
1306 destroyTestWindow();
1307}
1308
1309void TestXdgShellWindowRules::testDesktopsDontAffect()
1310{
1311 // We need at least two virtual desktop for this test.
1312 VirtualDesktopManager::self()->setCount(2);
1313 QCOMPARE(VirtualDesktopManager::self()->count(), 2u);
1314 VirtualDesktop *vd1 = VirtualDesktopManager::self()->desktops().at(0);
1315 VirtualDesktop *vd2 = VirtualDesktopManager::self()->desktops().at(1);
1316
1317 VirtualDesktopManager::self()->setCurrent(vd1);
1318 QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd1);
1319
1320 setWindowRule("desktops", QStringList{vd2->id()}, int(Rules::DontAffect));
1321
1322 createTestWindow();
1323
1324 // The window should appear on the current virtual desktop.
1325 QCOMPARE(m_window->desktops(), {vd1});
1326 QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd1);
1327
1328 destroyTestWindow();
1329}
1330
1331void TestXdgShellWindowRules::testDesktopsApply()
1332{
1333 // We need at least two virtual desktop for this test.
1334 VirtualDesktopManager::self()->setCount(2);
1335 QCOMPARE(VirtualDesktopManager::self()->count(), 2u);
1336 VirtualDesktop *vd1 = VirtualDesktopManager::self()->desktops().at(0);
1337 VirtualDesktop *vd2 = VirtualDesktopManager::self()->desktops().at(1);
1338
1339 VirtualDesktopManager::self()->setCurrent(vd1);
1340 QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd1);
1341
1342 setWindowRule("desktops", QStringList{vd2->id()}, int(Rules::Apply));
1343
1344 createTestWindow();
1345
1346 // The window should appear on the second virtual desktop.
1347 QCOMPARE(m_window->desktops(), {vd2});
1348 QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd2);
1349
1350 // We still should be able to move the window between desktops.
1351 m_window->setDesktops({vd1});
1352 QCOMPARE(m_window->desktops(), {vd1});
1353 QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd2);
1354
1355 // If we re-open the window, it should appear on the second virtual desktop again.
1356 destroyTestWindow();
1357 VirtualDesktopManager::self()->setCurrent(vd1);
1358 QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd1);
1359 createTestWindow();
1360
1361 QCOMPARE(m_window->desktops(), {vd2});
1362 QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd2);
1363
1364 destroyTestWindow();
1365}
1366
1367void TestXdgShellWindowRules::testDesktopsRemember()
1368{
1369 // We need at least two virtual desktop for this test.
1370 VirtualDesktopManager::self()->setCount(2);
1371 QCOMPARE(VirtualDesktopManager::self()->count(), 2u);
1372 VirtualDesktop *vd1 = VirtualDesktopManager::self()->desktops().at(0);
1373 VirtualDesktop *vd2 = VirtualDesktopManager::self()->desktops().at(1);
1374
1375 VirtualDesktopManager::self()->setCurrent(vd1);
1376 QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd1);
1377
1378 setWindowRule("desktops", QStringList{vd2->id()}, int(Rules::Remember));
1379
1380 createTestWindow();
1381
1382 QCOMPARE(m_window->desktops(), {vd2});
1383 QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd2);
1384
1385 // Move the window to the first virtual desktop.
1386 m_window->setDesktops({vd1});
1387 QCOMPARE(m_window->desktops(), {vd1});
1388 QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd2);
1389
1390 // If we create the window again, it should appear on the first virtual desktop.
1391 destroyTestWindow();
1392 createTestWindow();
1393
1394 QCOMPARE(m_window->desktops(), {vd1});
1395 QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd1);
1396
1397 destroyTestWindow();
1398}
1399
1400void TestXdgShellWindowRules::testDesktopsForce()
1401{
1402 // We need at least two virtual desktop for this test.
1403 VirtualDesktopManager::self()->setCount(2);
1404 QCOMPARE(VirtualDesktopManager::self()->count(), 2u);
1405 VirtualDesktop *vd1 = VirtualDesktopManager::self()->desktops().at(0);
1406 VirtualDesktop *vd2 = VirtualDesktopManager::self()->desktops().at(1);
1407
1408 VirtualDesktopManager::self()->setCurrent(vd1);
1409 QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd1);
1410
1411 setWindowRule("desktops", QStringList{vd2->id()}, int(Rules::Force));
1412
1413 createTestWindow();
1414
1415 // The window should appear on the second virtual desktop.
1416 QCOMPARE(m_window->desktops(), {vd2});
1417 QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd2);
1418
1419 // Any attempt to move the window to another virtual desktop should fail.
1420 m_window->setDesktops({vd1});
1421 QCOMPARE(m_window->desktops(), {vd2});
1422 QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd2);
1423
1424 // If we re-open the window, it should appear on the second virtual desktop again.
1425 destroyTestWindow();
1426 VirtualDesktopManager::self()->setCurrent(vd1);
1427 QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd1);
1428 createTestWindow();
1429
1430 QCOMPARE(m_window->desktops(), {vd2});
1431 QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd2);
1432
1433 destroyTestWindow();
1434}
1435
1436void TestXdgShellWindowRules::testDesktopsApplyNow()
1437{
1438 // We need at least two virtual desktop for this test.
1439 VirtualDesktopManager::self()->setCount(2);
1440 QCOMPARE(VirtualDesktopManager::self()->count(), 2u);
1441 VirtualDesktop *vd1 = VirtualDesktopManager::self()->desktops().at(0);
1442 VirtualDesktop *vd2 = VirtualDesktopManager::self()->desktops().at(1);
1443
1444 VirtualDesktopManager::self()->setCurrent(vd1);
1445 QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd1);
1446
1447 createTestWindow();
1448
1449 QCOMPARE(m_window->desktops(), {vd1});
1450 QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd1);
1451
1452 setWindowRule("desktops", QStringList{vd2->id()}, int(Rules::ApplyNow));
1453
1454 // The window should have been moved to the second virtual desktop.
1455 QCOMPARE(m_window->desktops(), {vd2});
1456 QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd1);
1457
1458 // One should still be able to move the window between desktops.
1459 m_window->setDesktops({vd1});
1460 QCOMPARE(m_window->desktops(), {vd1});
1461 QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd1);
1462
1463 // The rule should not be applied again.
1464 m_window->evaluateWindowRules();
1465 QCOMPARE(m_window->desktops(), {vd1});
1466 QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd1);
1467
1468 destroyTestWindow();
1469}
1470
1471void TestXdgShellWindowRules::testDesktopsForceTemporarily()
1472{
1473 // We need at least two virtual desktop for this test.
1474 VirtualDesktopManager::self()->setCount(2);
1475 QCOMPARE(VirtualDesktopManager::self()->count(), 2u);
1476 VirtualDesktop *vd1 = VirtualDesktopManager::self()->desktops().at(0);
1477 VirtualDesktop *vd2 = VirtualDesktopManager::self()->desktops().at(1);
1478
1479 VirtualDesktopManager::self()->setCurrent(vd1);
1480 QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd1);
1481
1482 setWindowRule("desktops", QStringList{vd2->id()}, int(Rules::ForceTemporarily));
1483
1484 createTestWindow();
1485
1486 // The window should appear on the second virtual desktop.
1487 QCOMPARE(m_window->desktops(), {vd2});
1488 QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd2);
1489
1490 // Any attempt to move the window to another virtual desktop should fail.
1491 m_window->setDesktops({vd1});
1492 QCOMPARE(m_window->desktops(), {vd2});
1493 QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd2);
1494
1495 // The rule should be discarded when the window is withdrawn.
1496 destroyTestWindow();
1497 VirtualDesktopManager::self()->setCurrent(vd1);
1498 QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd1);
1499 createTestWindow();
1500
1501 QCOMPARE(m_window->desktops(), {vd1});
1502 QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd1);
1503
1504 // One should be able to move the window between desktops.
1505 m_window->setDesktops({vd2});
1506 QCOMPARE(m_window->desktops(), {vd2});
1507 QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd1);
1508
1509 m_window->setDesktops({vd1});
1510 QCOMPARE(m_window->desktops(), {vd1});
1511 QCOMPARE(VirtualDesktopManager::self()->currentDesktop(), vd1);
1512
1513 destroyTestWindow();
1514}
1515
1516void TestXdgShellWindowRules::testMinimizeDontAffect()
1517{
1518 setWindowRule("minimize", true, int(Rules::DontAffect));
1519
1520 createTestWindow();
1521 QVERIFY(m_window->isMinimizable());
1522
1523 // The window should not be minimized.
1524 QVERIFY(!m_window->isMinimized());
1525
1526 destroyTestWindow();
1527}
1528
1529void TestXdgShellWindowRules::testMinimizeApply()
1530{
1531 setWindowRule("minimize", true, int(Rules::Apply));
1532
1533 createTestWindow(ClientShouldBeInactive);
1534 QVERIFY(m_window->isMinimizable());
1535
1536 // The window should be minimized.
1537 QVERIFY(m_window->isMinimized());
1538
1539 // We should still be able to unminimize the window.
1540 m_window->setMinimized(false);
1541 QVERIFY(!m_window->isMinimized());
1542
1543 // If we re-open the window, it should be minimized back again.
1544 destroyTestWindow();
1545 createTestWindow(ClientShouldBeInactive);
1546 QVERIFY(m_window->isMinimizable());
1547 QVERIFY(m_window->isMinimized());
1548
1549 destroyTestWindow();
1550}
1551
1552void TestXdgShellWindowRules::testMinimizeRemember()
1553{
1554 setWindowRule("minimize", false, int(Rules::Remember));
1555
1556 createTestWindow();
1557 QVERIFY(m_window->isMinimizable());
1558 QVERIFY(!m_window->isMinimized());
1559
1560 // Minimize the window.
1561 m_window->setMinimized(true);
1562 QVERIFY(m_window->isMinimized());
1563
1564 // If we open the window again, it should be minimized.
1565 destroyTestWindow();
1566 createTestWindow(ClientShouldBeInactive);
1567 QVERIFY(m_window->isMinimizable());
1568 QVERIFY(m_window->isMinimized());
1569
1570 destroyTestWindow();
1571}
1572
1573void TestXdgShellWindowRules::testMinimizeForce()
1574{
1575 setWindowRule("minimize", false, int(Rules::Force));
1576
1577 createTestWindow();
1578 QVERIFY(!m_window->isMinimizable());
1579 QVERIFY(!m_window->isMinimized());
1580
1581 // Any attempt to minimize the window should fail.
1582 m_window->setMinimized(true);
1583 QVERIFY(!m_window->isMinimized());
1584
1585 // If we re-open the window, the minimized state should still be forced.
1586 destroyTestWindow();
1587 createTestWindow();
1588 QVERIFY(!m_window->isMinimizable());
1589 QVERIFY(!m_window->isMinimized());
1590 m_window->setMinimized(true);
1591 QVERIFY(!m_window->isMinimized());
1592
1593 destroyTestWindow();
1594}
1595
1596void TestXdgShellWindowRules::testMinimizeApplyNow()
1597{
1598 createTestWindow();
1599 QVERIFY(m_window->isMinimizable());
1600 QVERIFY(!m_window->isMinimized());
1601
1602 setWindowRule("minimize", true, int(Rules::ApplyNow));
1603
1604 // The window should be minimized now.
1605 QVERIFY(m_window->isMinimizable());
1606 QVERIFY(m_window->isMinimized());
1607
1608 // One is still able to unminimize the window.
1609 m_window->setMinimized(false);
1610 QVERIFY(!m_window->isMinimized());
1611
1612 // The rule should not be applied again.
1613 m_window->evaluateWindowRules();
1614 QVERIFY(m_window->isMinimizable());
1615 QVERIFY(!m_window->isMinimized());
1616
1617 destroyTestWindow();
1618}
1619
1620void TestXdgShellWindowRules::testMinimizeForceTemporarily()
1621{
1622 setWindowRule("minimize", false, int(Rules::ForceTemporarily));
1623
1624 createTestWindow();
1625 QVERIFY(!m_window->isMinimizable());
1626 QVERIFY(!m_window->isMinimized());
1627
1628 // Any attempt to minimize the window should fail until the window is closed.
1629 m_window->setMinimized(true);
1630 QVERIFY(!m_window->isMinimized());
1631
1632 // The rule should be discarded when the window is closed.
1633 destroyTestWindow();
1634 createTestWindow();
1635 QVERIFY(m_window->isMinimizable());
1636 QVERIFY(!m_window->isMinimized());
1637 m_window->setMinimized(true);
1638 QVERIFY(m_window->isMinimized());
1639
1640 destroyTestWindow();
1641}
1642
1643void TestXdgShellWindowRules::testSkipTaskbarDontAffect()
1644{
1645 setWindowRule("skiptaskbar", true, int(Rules::DontAffect));
1646
1647 createTestWindow();
1648
1649 // The window should not be affected by the rule.
1650 QVERIFY(!m_window->skipTaskbar());
1651
1652 destroyTestWindow();
1653}
1654
1655void TestXdgShellWindowRules::testSkipTaskbarApply()
1656{
1657 setWindowRule("skiptaskbar", true, int(Rules::Apply));
1658
1659 createTestWindow();
1660
1661 // The window should not be included on a taskbar.
1662 QVERIFY(m_window->skipTaskbar());
1663
1664 // Though one can change that.
1665 m_window->setOriginalSkipTaskbar(false);
1666 QVERIFY(!m_window->skipTaskbar());
1667
1668 // Reopen the window, the rule should be applied again.
1669 destroyTestWindow();
1670 createTestWindow();
1671 QVERIFY(m_window->skipTaskbar());
1672
1673 destroyTestWindow();
1674}
1675
1676void TestXdgShellWindowRules::testSkipTaskbarRemember()
1677{
1678 setWindowRule("skiptaskbar", true, int(Rules::Remember));
1679
1680 createTestWindow();
1681
1682 // The window should not be included on a taskbar.
1683 QVERIFY(m_window->skipTaskbar());
1684
1685 // Change the skip-taskbar state.
1686 m_window->setOriginalSkipTaskbar(false);
1687 QVERIFY(!m_window->skipTaskbar());
1688
1689 // Reopen the window.
1690 destroyTestWindow();
1691 createTestWindow();
1692
1693 // The window should be included on a taskbar.
1694 QVERIFY(!m_window->skipTaskbar());
1695
1696 destroyTestWindow();
1697}
1698
1699void TestXdgShellWindowRules::testSkipTaskbarForce()
1700{
1701 setWindowRule("skiptaskbar", true, int(Rules::Force));
1702
1703 createTestWindow();
1704
1705 // The window should not be included on a taskbar.
1706 QVERIFY(m_window->skipTaskbar());
1707
1708 // Any attempt to change the skip-taskbar state should not succeed.
1709 m_window->setOriginalSkipTaskbar(false);
1710 QVERIFY(m_window->skipTaskbar());
1711
1712 // Reopen the window.
1713 destroyTestWindow();
1714 createTestWindow();
1715
1716 // The skip-taskbar state should be still forced.
1717 QVERIFY(m_window->skipTaskbar());
1718
1719 destroyTestWindow();
1720}
1721
1722void TestXdgShellWindowRules::testSkipTaskbarApplyNow()
1723{
1724 createTestWindow();
1725 QVERIFY(!m_window->skipTaskbar());
1726
1727 setWindowRule("skiptaskbar", true, int(Rules::ApplyNow));
1728
1729 // The window should not be on a taskbar now.
1730 QVERIFY(m_window->skipTaskbar());
1731
1732 // Also, one change the skip-taskbar state.
1733 m_window->setOriginalSkipTaskbar(false);
1734 QVERIFY(!m_window->skipTaskbar());
1735
1736 // The rule should not be applied again.
1737 m_window->evaluateWindowRules();
1738 QVERIFY(!m_window->skipTaskbar());
1739
1740 destroyTestWindow();
1741}
1742
1743void TestXdgShellWindowRules::testSkipTaskbarForceTemporarily()
1744{
1745 setWindowRule("skiptaskbar", true, int(Rules::ForceTemporarily));
1746
1747 createTestWindow();
1748
1749 // The window should not be included on a taskbar.
1750 QVERIFY(m_window->skipTaskbar());
1751
1752 // Any attempt to change the skip-taskbar state should not succeed.
1753 m_window->setOriginalSkipTaskbar(false);
1754 QVERIFY(m_window->skipTaskbar());
1755
1756 // The rule should be discarded when the window is closed.
1757 destroyTestWindow();
1758 createTestWindow();
1759 QVERIFY(!m_window->skipTaskbar());
1760
1761 // The skip-taskbar state is no longer forced.
1762 m_window->setOriginalSkipTaskbar(true);
1763 QVERIFY(m_window->skipTaskbar());
1764
1765 destroyTestWindow();
1766}
1767
1768void TestXdgShellWindowRules::testSkipPagerDontAffect()
1769{
1770 setWindowRule("skippager", true, int(Rules::DontAffect));
1771
1772 createTestWindow();
1773
1774 // The window should not be affected by the rule.
1775 QVERIFY(!m_window->skipPager());
1776
1777 destroyTestWindow();
1778}
1779
1780void TestXdgShellWindowRules::testSkipPagerApply()
1781{
1782 setWindowRule("skippager", true, int(Rules::Apply));
1783
1784 createTestWindow();
1785
1786 // The window should not be included on a pager.
1787 QVERIFY(m_window->skipPager());
1788
1789 // Though one can change that.
1790 m_window->setSkipPager(false);
1791 QVERIFY(!m_window->skipPager());
1792
1793 // Reopen the window, the rule should be applied again.
1794 destroyTestWindow();
1795 createTestWindow();
1796 QVERIFY(m_window->skipPager());
1797
1798 destroyTestWindow();
1799}
1800
1801void TestXdgShellWindowRules::testSkipPagerRemember()
1802{
1803 setWindowRule("skippager", true, int(Rules::Remember));
1804
1805 createTestWindow();
1806
1807 // The window should not be included on a pager.
1808 QVERIFY(m_window->skipPager());
1809
1810 // Change the skip-pager state.
1811 m_window->setSkipPager(false);
1812 QVERIFY(!m_window->skipPager());
1813
1814 // Reopen the window.
1815 destroyTestWindow();
1816 createTestWindow();
1817
1818 // The window should be included on a pager.
1819 QVERIFY(!m_window->skipPager());
1820
1821 destroyTestWindow();
1822}
1823
1824void TestXdgShellWindowRules::testSkipPagerForce()
1825{
1826 setWindowRule("skippager", true, int(Rules::Force));
1827
1828 createTestWindow();
1829
1830 // The window should not be included on a pager.
1831 QVERIFY(m_window->skipPager());
1832
1833 // Any attempt to change the skip-pager state should not succeed.
1834 m_window->setSkipPager(false);
1835 QVERIFY(m_window->skipPager());
1836
1837 // Reopen the window.
1838 destroyTestWindow();
1839 createTestWindow();
1840
1841 // The skip-pager state should be still forced.
1842 QVERIFY(m_window->skipPager());
1843
1844 destroyTestWindow();
1845}
1846
1847void TestXdgShellWindowRules::testSkipPagerApplyNow()
1848{
1849 createTestWindow();
1850 QVERIFY(!m_window->skipPager());
1851
1852 setWindowRule("skippager", true, int(Rules::ApplyNow));
1853
1854 // The window should not be on a pager now.
1855 QVERIFY(m_window->skipPager());
1856
1857 // Also, one change the skip-pager state.
1858 m_window->setSkipPager(false);
1859 QVERIFY(!m_window->skipPager());
1860
1861 // The rule should not be applied again.
1862 m_window->evaluateWindowRules();
1863 QVERIFY(!m_window->skipPager());
1864
1865 destroyTestWindow();
1866}
1867
1868void TestXdgShellWindowRules::testSkipPagerForceTemporarily()
1869{
1870 setWindowRule("skippager", true, int(Rules::ForceTemporarily));
1871
1872 createTestWindow();
1873
1874 // The window should not be included on a pager.
1875 QVERIFY(m_window->skipPager());
1876
1877 // Any attempt to change the skip-pager state should not succeed.
1878 m_window->setSkipPager(false);
1879 QVERIFY(m_window->skipPager());
1880
1881 // The rule should be discarded when the window is closed.
1882 destroyTestWindow();
1883 createTestWindow();
1884 QVERIFY(!m_window->skipPager());
1885
1886 // The skip-pager state is no longer forced.
1887 m_window->setSkipPager(true);
1888 QVERIFY(m_window->skipPager());
1889
1890 destroyTestWindow();
1891}
1892
1893void TestXdgShellWindowRules::testSkipSwitcherDontAffect()
1894{
1895 setWindowRule("skipswitcher", true, int(Rules::DontAffect));
1896
1897 createTestWindow();
1898
1899 // The window should not be affected by the rule.
1900 QVERIFY(!m_window->skipSwitcher());
1901
1902 destroyTestWindow();
1903}
1904
1905void TestXdgShellWindowRules::testSkipSwitcherApply()
1906{
1907 setWindowRule("skipswitcher", true, int(Rules::Apply));
1908
1909 createTestWindow();
1910
1911 // The window should be excluded from window switching effects.
1912 QVERIFY(m_window->skipSwitcher());
1913
1914 // Though one can change that.
1915 m_window->setSkipSwitcher(false);
1916 QVERIFY(!m_window->skipSwitcher());
1917
1918 // Reopen the window, the rule should be applied again.
1919 destroyTestWindow();
1920 createTestWindow();
1921 QVERIFY(m_window->skipSwitcher());
1922
1923 destroyTestWindow();
1924}
1925
1926void TestXdgShellWindowRules::testSkipSwitcherRemember()
1927{
1928 setWindowRule("skipswitcher", true, int(Rules::Remember));
1929
1930 createTestWindow();
1931
1932 // The window should be excluded from window switching effects.
1933 QVERIFY(m_window->skipSwitcher());
1934
1935 // Change the skip-switcher state.
1936 m_window->setSkipSwitcher(false);
1937 QVERIFY(!m_window->skipSwitcher());
1938
1939 // Reopen the window.
1940 destroyTestWindow();
1941 createTestWindow();
1942
1943 // The window should be included in window switching effects.
1944 QVERIFY(!m_window->skipSwitcher());
1945
1946 destroyTestWindow();
1947}
1948
1949void TestXdgShellWindowRules::testSkipSwitcherForce()
1950{
1951 setWindowRule("skipswitcher", true, int(Rules::Force));
1952
1953 createTestWindow();
1954
1955 // The window should be excluded from window switching effects.
1956 QVERIFY(m_window->skipSwitcher());
1957
1958 // Any attempt to change the skip-switcher state should not succeed.
1959 m_window->setSkipSwitcher(false);
1960 QVERIFY(m_window->skipSwitcher());
1961
1962 // Reopen the window.
1963 destroyTestWindow();
1964 createTestWindow();
1965
1966 // The skip-switcher state should be still forced.
1967 QVERIFY(m_window->skipSwitcher());
1968
1969 destroyTestWindow();
1970}
1971
1972void TestXdgShellWindowRules::testSkipSwitcherApplyNow()
1973{
1974 createTestWindow();
1975 QVERIFY(!m_window->skipSwitcher());
1976
1977 setWindowRule("skipswitcher", true, int(Rules::ApplyNow));
1978
1979 // The window should be excluded from window switching effects now.
1980 QVERIFY(m_window->skipSwitcher());
1981
1982 // Also, one change the skip-switcher state.
1983 m_window->setSkipSwitcher(false);
1984 QVERIFY(!m_window->skipSwitcher());
1985
1986 // The rule should not be applied again.
1987 m_window->evaluateWindowRules();
1988 QVERIFY(!m_window->skipSwitcher());
1989
1990 destroyTestWindow();
1991}
1992
1993void TestXdgShellWindowRules::testSkipSwitcherForceTemporarily()
1994{
1995 setWindowRule("skipswitcher", true, int(Rules::ForceTemporarily));
1996
1997 createTestWindow();
1998
1999 // The window should be excluded from window switching effects.
2000 QVERIFY(m_window->skipSwitcher());
2001
2002 // Any attempt to change the skip-switcher state should not succeed.
2003 m_window->setSkipSwitcher(false);
2004 QVERIFY(m_window->skipSwitcher());
2005
2006 // The rule should be discarded when the window is closed.
2007 destroyTestWindow();
2008 createTestWindow();
2009 QVERIFY(!m_window->skipSwitcher());
2010
2011 // The skip-switcher state is no longer forced.
2012 m_window->setSkipSwitcher(true);
2013 QVERIFY(m_window->skipSwitcher());
2014
2015 destroyTestWindow();
2016}
2017
2018void TestXdgShellWindowRules::testKeepAboveDontAffect()
2019{
2020 setWindowRule("above", true, int(Rules::DontAffect));
2021
2022 createTestWindow();
2023
2024 // The keep-above state of the window should not be affected by the rule.
2025 QVERIFY(!m_window->keepAbove());
2026
2027 destroyTestWindow();
2028}
2029
2030void TestXdgShellWindowRules::testKeepAboveApply()
2031{
2032 setWindowRule("above", true, int(Rules::Apply));
2033
2034 createTestWindow();
2035
2036 // Initially, the window should be kept above.
2037 QVERIFY(m_window->keepAbove());
2038
2039 // One should also be able to alter the keep-above state.
2040 m_window->setKeepAbove(false);
2041 QVERIFY(!m_window->keepAbove());
2042
2043 // If one re-opens the window, it should be kept above back again.
2044 destroyTestWindow();
2045 createTestWindow();
2046 QVERIFY(m_window->keepAbove());
2047
2048 destroyTestWindow();
2049}
2050
2051void TestXdgShellWindowRules::testKeepAboveRemember()
2052{
2053 setWindowRule("above", true, int(Rules::Remember));
2054
2055 createTestWindow();
2056
2057 // Initially, the window should be kept above.
2058 QVERIFY(m_window->keepAbove());
2059
2060 // Unset the keep-above state.
2061 m_window->setKeepAbove(false);
2062 QVERIFY(!m_window->keepAbove());
2063 destroyTestWindow();
2064
2065 // Re-open the window, it should not be kept above.
2066 createTestWindow();
2067 QVERIFY(!m_window->keepAbove());
2068
2069 destroyTestWindow();
2070}
2071
2072void TestXdgShellWindowRules::testKeepAboveForce()
2073{
2074 setWindowRule("above", true, int(Rules::Force));
2075
2076 createTestWindow();
2077
2078 // Initially, the window should be kept above.
2079 QVERIFY(m_window->keepAbove());
2080
2081 // Any attemt to unset the keep-above should not succeed.
2082 m_window->setKeepAbove(false);
2083 QVERIFY(m_window->keepAbove());
2084
2085 // If we re-open the window, it should still be kept above.
2086 destroyTestWindow();
2087 createTestWindow();
2088 QVERIFY(m_window->keepAbove());
2089
2090 destroyTestWindow();
2091}
2092
2093void TestXdgShellWindowRules::testKeepAboveApplyNow()
2094{
2095 createTestWindow();
2096 QVERIFY(!m_window->keepAbove());
2097
2098 setWindowRule("above", true, int(Rules::ApplyNow));
2099
2100 // The window should now be kept above other windows.
2101 QVERIFY(m_window->keepAbove());
2102
2103 // One is still able to change the keep-above state of the window.
2104 m_window->setKeepAbove(false);
2105 QVERIFY(!m_window->keepAbove());
2106
2107 // The rule should not be applied again.
2108 m_window->evaluateWindowRules();
2109 QVERIFY(!m_window->keepAbove());
2110
2111 destroyTestWindow();
2112}
2113
2114void TestXdgShellWindowRules::testKeepAboveForceTemporarily()
2115{
2116 setWindowRule("above", true, int(Rules::ForceTemporarily));
2117
2118 createTestWindow();
2119
2120 // Initially, the window should be kept above.
2121 QVERIFY(m_window->keepAbove());
2122
2123 // Any attempt to alter the keep-above state should not succeed.
2124 m_window->setKeepAbove(false);
2125 QVERIFY(m_window->keepAbove());
2126
2127 // The rule should be discarded when the window is closed.
2128 destroyTestWindow();
2129 createTestWindow();
2130 QVERIFY(!m_window->keepAbove());
2131
2132 // The keep-above state is no longer forced.
2133 m_window->setKeepAbove(true);
2134 QVERIFY(m_window->keepAbove());
2135 m_window->setKeepAbove(false);
2136 QVERIFY(!m_window->keepAbove());
2137
2138 destroyTestWindow();
2139}
2140
2141void TestXdgShellWindowRules::testKeepBelowDontAffect()
2142{
2143 setWindowRule("below", true, int(Rules::DontAffect));
2144
2145 createTestWindow();
2146
2147 // The keep-below state of the window should not be affected by the rule.
2148 QVERIFY(!m_window->keepBelow());
2149
2150 destroyTestWindow();
2151}
2152
2153void TestXdgShellWindowRules::testKeepBelowApply()
2154{
2155 setWindowRule("below", true, int(Rules::Apply));
2156
2157 createTestWindow();
2158
2159 // Initially, the window should be kept below.
2160 QVERIFY(m_window->keepBelow());
2161
2162 // One should also be able to alter the keep-below state.
2163 m_window->setKeepBelow(false);
2164 QVERIFY(!m_window->keepBelow());
2165
2166 // If one re-opens the window, it should be kept above back again.
2167 destroyTestWindow();
2168 createTestWindow();
2169 QVERIFY(m_window->keepBelow());
2170
2171 destroyTestWindow();
2172}
2173
2174void TestXdgShellWindowRules::testKeepBelowRemember()
2175{
2176 setWindowRule("below", true, int(Rules::Remember));
2177
2178 createTestWindow();
2179
2180 // Initially, the window should be kept below.
2181 QVERIFY(m_window->keepBelow());
2182
2183 // Unset the keep-below state.
2184 m_window->setKeepBelow(false);
2185 QVERIFY(!m_window->keepBelow());
2186 destroyTestWindow();
2187
2188 // Re-open the window, it should not be kept below.
2189 createTestWindow();
2190 QVERIFY(!m_window->keepBelow());
2191
2192 destroyTestWindow();
2193}
2194
2195void TestXdgShellWindowRules::testKeepBelowForce()
2196{
2197 setWindowRule("below", true, int(Rules::Force));
2198
2199 createTestWindow();
2200
2201 // Initially, the window should be kept below.
2202 QVERIFY(m_window->keepBelow());
2203
2204 // Any attemt to unset the keep-below should not succeed.
2205 m_window->setKeepBelow(false);
2206 QVERIFY(m_window->keepBelow());
2207
2208 // If we re-open the window, it should still be kept below.
2209 destroyTestWindow();
2210 createTestWindow();
2211 QVERIFY(m_window->keepBelow());
2212
2213 destroyTestWindow();
2214}
2215
2216void TestXdgShellWindowRules::testKeepBelowApplyNow()
2217{
2218 createTestWindow();
2219 QVERIFY(!m_window->keepBelow());
2220
2221 setWindowRule("below", true, int(Rules::ApplyNow));
2222
2223 // The window should now be kept below other windows.
2224 QVERIFY(m_window->keepBelow());
2225
2226 // One is still able to change the keep-below state of the window.
2227 m_window->setKeepBelow(false);
2228 QVERIFY(!m_window->keepBelow());
2229
2230 // The rule should not be applied again.
2231 m_window->evaluateWindowRules();
2232 QVERIFY(!m_window->keepBelow());
2233
2234 destroyTestWindow();
2235}
2236
2237void TestXdgShellWindowRules::testKeepBelowForceTemporarily()
2238{
2239 setWindowRule("below", true, int(Rules::ForceTemporarily));
2240
2241 createTestWindow();
2242
2243 // Initially, the window should be kept below.
2244 QVERIFY(m_window->keepBelow());
2245
2246 // Any attempt to alter the keep-below state should not succeed.
2247 m_window->setKeepBelow(false);
2248 QVERIFY(m_window->keepBelow());
2249
2250 // The rule should be discarded when the window is closed.
2251 destroyTestWindow();
2252 createTestWindow();
2253 QVERIFY(!m_window->keepBelow());
2254
2255 // The keep-below state is no longer forced.
2256 m_window->setKeepBelow(true);
2257 QVERIFY(m_window->keepBelow());
2258 m_window->setKeepBelow(false);
2259 QVERIFY(!m_window->keepBelow());
2260
2261 destroyTestWindow();
2262}
2263
2264void TestXdgShellWindowRules::testShortcutDontAffect()
2265{
2266#if !KWIN_BUILD_GLOBALSHORTCUTS
2267 QSKIP("Can't test shortcuts without shortcuts");
2268 return;
2269#endif
2270
2271 setWindowRule("shortcut", "Ctrl+Alt+1", int(Rules::DontAffect));
2272
2273 createTestWindow();
2274 QCOMPARE(m_window->shortcut(), QKeySequence());
2275 m_window->setMinimized(true);
2276 QVERIFY(m_window->isMinimized());
2277
2278 // If we press the window shortcut, nothing should happen.
2279 QSignalSpy minimizedChangedSpy(m_window, &Window::minimizedChanged);
2280 quint32 timestamp = 1;
2281 Test::keyboardKeyPressed(KEY_LEFTCTRL, timestamp++);
2282 Test::keyboardKeyPressed(KEY_LEFTALT, timestamp++);
2283 Test::keyboardKeyPressed(KEY_1, timestamp++);
2284 Test::keyboardKeyReleased(KEY_1, timestamp++);
2285 Test::keyboardKeyReleased(KEY_LEFTALT, timestamp++);
2286 Test::keyboardKeyReleased(KEY_LEFTCTRL, timestamp++);
2287 QVERIFY(!minimizedChangedSpy.wait(100));
2288 QVERIFY(m_window->isMinimized());
2289
2290 destroyTestWindow();
2291}
2292
2293void TestXdgShellWindowRules::testShortcutApply()
2294{
2295#if !KWIN_BUILD_GLOBALSHORTCUTS
2296 QSKIP("Can't test shortcuts without shortcuts");
2297 return;
2298#endif
2299 setWindowRule("shortcut", "Ctrl+Alt+1", int(Rules::Apply));
2300
2301 createTestWindow();
2302
2303 // If we press the window shortcut, the window should be brought back to user.
2304 QSignalSpy minimizedChangedSpy(m_window, &Window::minimizedChanged);
2305 quint32 timestamp = 1;
2306 QCOMPARE(m_window->shortcut(), (QKeySequence{Qt::CTRL | Qt::ALT | Qt::Key_1}));
2307 m_window->setMinimized(true);
2308 QVERIFY(m_window->isMinimized());
2309 Test::keyboardKeyPressed(KEY_LEFTCTRL, timestamp++);
2310 Test::keyboardKeyPressed(KEY_LEFTALT, timestamp++);
2311 Test::keyboardKeyPressed(KEY_1, timestamp++);
2312 Test::keyboardKeyReleased(KEY_1, timestamp++);
2313 Test::keyboardKeyReleased(KEY_LEFTALT, timestamp++);
2314 Test::keyboardKeyReleased(KEY_LEFTCTRL, timestamp++);
2315 QVERIFY(minimizedChangedSpy.wait());
2316 QVERIFY(!m_window->isMinimized());
2317
2318 // One can also change the shortcut.
2319 m_window->setShortcut(QStringLiteral("Ctrl+Alt+2"));
2320 QCOMPARE(m_window->shortcut(), (QKeySequence{Qt::CTRL | Qt::ALT | Qt::Key_2}));
2321 m_window->setMinimized(true);
2322 QVERIFY(m_window->isMinimized());
2323 Test::keyboardKeyPressed(KEY_LEFTCTRL, timestamp++);
2324 Test::keyboardKeyPressed(KEY_LEFTALT, timestamp++);
2325 Test::keyboardKeyPressed(KEY_2, timestamp++);
2326 Test::keyboardKeyReleased(KEY_2, timestamp++);
2327 Test::keyboardKeyReleased(KEY_LEFTALT, timestamp++);
2328 Test::keyboardKeyReleased(KEY_LEFTCTRL, timestamp++);
2329 QVERIFY(minimizedChangedSpy.wait());
2330 QVERIFY(!m_window->isMinimized());
2331
2332 // The old shortcut should do nothing.
2333 m_window->setMinimized(true);
2334 QVERIFY(m_window->isMinimized());
2335 Test::keyboardKeyPressed(KEY_LEFTCTRL, timestamp++);
2336 Test::keyboardKeyPressed(KEY_LEFTALT, timestamp++);
2337 Test::keyboardKeyPressed(KEY_1, timestamp++);
2338 Test::keyboardKeyReleased(KEY_1, timestamp++);
2339 Test::keyboardKeyReleased(KEY_LEFTALT, timestamp++);
2340 Test::keyboardKeyReleased(KEY_LEFTCTRL, timestamp++);
2341 QVERIFY(!minimizedChangedSpy.wait(100));
2342 QVERIFY(m_window->isMinimized());
2343
2344 // Reopen the window.
2345 destroyTestWindow();
2346 createTestWindow();
2347
2348 // The window shortcut should be set back to Ctrl+Alt+1.
2349 QCOMPARE(m_window->shortcut(), (QKeySequence{Qt::CTRL | Qt::ALT | Qt::Key_1}));
2350
2351 destroyTestWindow();
2352}
2353
2354void TestXdgShellWindowRules::testShortcutRemember()
2355{
2356 QSKIP("KWin core doesn't try to save the last used window shortcut");
2357
2358 setWindowRule("shortcut", "Ctrl+Alt+1", int(Rules::Remember));
2359
2360 createTestWindow();
2361
2362 // If we press the window shortcut, the window should be brought back to user.
2363 QSignalSpy minimizedChangedSpy(m_window, &Window::minimizedChanged);
2364 quint32 timestamp = 1;
2365 QCOMPARE(m_window->shortcut(), (QKeySequence{Qt::CTRL | Qt::ALT | Qt::Key_1}));
2366 m_window->setMinimized(true);
2367 QVERIFY(m_window->isMinimized());
2368 Test::keyboardKeyPressed(KEY_LEFTCTRL, timestamp++);
2369 Test::keyboardKeyPressed(KEY_LEFTALT, timestamp++);
2370 Test::keyboardKeyPressed(KEY_1, timestamp++);
2371 Test::keyboardKeyReleased(KEY_1, timestamp++);
2372 Test::keyboardKeyReleased(KEY_LEFTALT, timestamp++);
2373 Test::keyboardKeyReleased(KEY_LEFTCTRL, timestamp++);
2374 QVERIFY(minimizedChangedSpy.wait());
2375 QVERIFY(!m_window->isMinimized());
2376
2377 // Change the window shortcut to Ctrl+Alt+2.
2378 m_window->setShortcut(QStringLiteral("Ctrl+Alt+2"));
2379 QCOMPARE(m_window->shortcut(), (QKeySequence{Qt::CTRL | Qt::ALT | Qt::Key_2}));
2380 m_window->setMinimized(true);
2381 QVERIFY(m_window->isMinimized());
2382 Test::keyboardKeyPressed(KEY_LEFTCTRL, timestamp++);
2383 Test::keyboardKeyPressed(KEY_LEFTALT, timestamp++);
2384 Test::keyboardKeyPressed(KEY_2, timestamp++);
2385 Test::keyboardKeyReleased(KEY_2, timestamp++);
2386 Test::keyboardKeyReleased(KEY_LEFTALT, timestamp++);
2387 Test::keyboardKeyReleased(KEY_LEFTCTRL, timestamp++);
2388 QVERIFY(minimizedChangedSpy.wait());
2389 QVERIFY(!m_window->isMinimized());
2390
2391 // Reopen the window.
2392 destroyTestWindow();
2393 createTestWindow();
2394
2395 // The window shortcut should be set to the last known value.
2396 QCOMPARE(m_window->shortcut(), (QKeySequence{Qt::CTRL | Qt::ALT | Qt::Key_2}));
2397
2398 destroyTestWindow();
2399}
2400
2401void TestXdgShellWindowRules::testShortcutForce()
2402{
2403 QSKIP("KWin core can't release forced window shortcuts");
2404
2405 setWindowRule("shortcut", "Ctrl+Alt+1", int(Rules::Force));
2406
2407 createTestWindow();
2408
2409 // If we press the window shortcut, the window should be brought back to user.
2410 QSignalSpy minimizedChangedSpy(m_window, &Window::minimizedChanged);
2411 quint32 timestamp = 1;
2412 QCOMPARE(m_window->shortcut(), (QKeySequence{Qt::CTRL | Qt::ALT | Qt::Key_1}));
2413 m_window->setMinimized(true);
2414 QVERIFY(m_window->isMinimized());
2415 Test::keyboardKeyPressed(KEY_LEFTCTRL, timestamp++);
2416 Test::keyboardKeyPressed(KEY_LEFTALT, timestamp++);
2417 Test::keyboardKeyPressed(KEY_1, timestamp++);
2418 Test::keyboardKeyReleased(KEY_1, timestamp++);
2419 Test::keyboardKeyReleased(KEY_LEFTALT, timestamp++);
2420 Test::keyboardKeyReleased(KEY_LEFTCTRL, timestamp++);
2421 QVERIFY(minimizedChangedSpy.wait());
2422 QVERIFY(!m_window->isMinimized());
2423
2424 // Any attempt to change the window shortcut should not succeed.
2425 m_window->setShortcut(QStringLiteral("Ctrl+Alt+2"));
2426 QCOMPARE(m_window->shortcut(), (QKeySequence{Qt::CTRL | Qt::ALT | Qt::Key_1}));
2427 m_window->setMinimized(true);
2428 QVERIFY(m_window->isMinimized());
2429 Test::keyboardKeyPressed(KEY_LEFTCTRL, timestamp++);
2430 Test::keyboardKeyPressed(KEY_LEFTALT, timestamp++);
2431 Test::keyboardKeyPressed(KEY_2, timestamp++);
2432 Test::keyboardKeyReleased(KEY_2, timestamp++);
2433 Test::keyboardKeyReleased(KEY_LEFTALT, timestamp++);
2434 Test::keyboardKeyReleased(KEY_LEFTCTRL, timestamp++);
2435 QVERIFY(!minimizedChangedSpy.wait(100));
2436 QVERIFY(m_window->isMinimized());
2437
2438 // Reopen the window.
2439 destroyTestWindow();
2440 createTestWindow();
2441
2442 // The window shortcut should still be forced.
2443 QCOMPARE(m_window->shortcut(), (QKeySequence{Qt::CTRL | Qt::ALT | Qt::Key_1}));
2444
2445 destroyTestWindow();
2446}
2447
2448void TestXdgShellWindowRules::testShortcutApplyNow()
2449{
2450#if !KWIN_BUILD_GLOBALSHORTCUTS
2451 QSKIP("Can't test shortcuts without shortcuts");
2452 return;
2453#endif
2454
2455 createTestWindow();
2456 QVERIFY(m_window->shortcut().isEmpty());
2457
2458 setWindowRule("shortcut", "Ctrl+Alt+1", int(Rules::ApplyNow));
2459
2460 // The window should now have a window shortcut assigned.
2461 QCOMPARE(m_window->shortcut(), (QKeySequence{Qt::CTRL | Qt::ALT | Qt::Key_1}));
2462 QSignalSpy minimizedChangedSpy(m_window, &Window::minimizedChanged);
2463 quint32 timestamp = 1;
2464 m_window->setMinimized(true);
2465 QVERIFY(m_window->isMinimized());
2466 Test::keyboardKeyPressed(KEY_LEFTCTRL, timestamp++);
2467 Test::keyboardKeyPressed(KEY_LEFTALT, timestamp++);
2468 Test::keyboardKeyPressed(KEY_1, timestamp++);
2469 Test::keyboardKeyReleased(KEY_1, timestamp++);
2470 Test::keyboardKeyReleased(KEY_LEFTALT, timestamp++);
2471 Test::keyboardKeyReleased(KEY_LEFTCTRL, timestamp++);
2472 QVERIFY(minimizedChangedSpy.wait());
2473 QVERIFY(!m_window->isMinimized());
2474
2475 // Assign a different shortcut.
2476 m_window->setShortcut(QStringLiteral("Ctrl+Alt+2"));
2477 QCOMPARE(m_window->shortcut(), (QKeySequence{Qt::CTRL | Qt::ALT | Qt::Key_2}));
2478 m_window->setMinimized(true);
2479 QVERIFY(m_window->isMinimized());
2480 Test::keyboardKeyPressed(KEY_LEFTCTRL, timestamp++);
2481 Test::keyboardKeyPressed(KEY_LEFTALT, timestamp++);
2482 Test::keyboardKeyPressed(KEY_2, timestamp++);
2483 Test::keyboardKeyReleased(KEY_2, timestamp++);
2484 Test::keyboardKeyReleased(KEY_LEFTALT, timestamp++);
2485 Test::keyboardKeyReleased(KEY_LEFTCTRL, timestamp++);
2486 QVERIFY(minimizedChangedSpy.wait());
2487 QVERIFY(!m_window->isMinimized());
2488
2489 // The rule should not be applied again.
2490 m_window->evaluateWindowRules();
2491 QCOMPARE(m_window->shortcut(), (QKeySequence{Qt::CTRL | Qt::ALT | Qt::Key_2}));
2492
2493 destroyTestWindow();
2494}
2495
2496void TestXdgShellWindowRules::testShortcutForceTemporarily()
2497{
2498 QSKIP("KWin core can't release forced window shortcuts");
2499
2500 setWindowRule("shortcut", "Ctrl+Alt+1", int(Rules::ForceTemporarily));
2501
2502 createTestWindow();
2503
2504 // If we press the window shortcut, the window should be brought back to user.
2505 QSignalSpy minimizedChangedSpy(m_window, &Window::minimizedChanged);
2506 quint32 timestamp = 1;
2507 QCOMPARE(m_window->shortcut(), (QKeySequence{Qt::CTRL | Qt::ALT | Qt::Key_1}));
2508 m_window->setMinimized(true);
2509 QVERIFY(m_window->isMinimized());
2510 Test::keyboardKeyPressed(KEY_LEFTCTRL, timestamp++);
2511 Test::keyboardKeyPressed(KEY_LEFTALT, timestamp++);
2512 Test::keyboardKeyPressed(KEY_1, timestamp++);
2513 Test::keyboardKeyReleased(KEY_1, timestamp++);
2514 Test::keyboardKeyReleased(KEY_LEFTALT, timestamp++);
2515 Test::keyboardKeyReleased(KEY_LEFTCTRL, timestamp++);
2516 QVERIFY(minimizedChangedSpy.wait());
2517 QVERIFY(!m_window->isMinimized());
2518
2519 // Any attempt to change the window shortcut should not succeed.
2520 m_window->setShortcut(QStringLiteral("Ctrl+Alt+2"));
2521 QCOMPARE(m_window->shortcut(), (QKeySequence{Qt::CTRL | Qt::ALT | Qt::Key_1}));
2522 m_window->setMinimized(true);
2523 QVERIFY(m_window->isMinimized());
2524 Test::keyboardKeyPressed(KEY_LEFTCTRL, timestamp++);
2525 Test::keyboardKeyPressed(KEY_LEFTALT, timestamp++);
2526 Test::keyboardKeyPressed(KEY_2, timestamp++);
2527 Test::keyboardKeyReleased(KEY_2, timestamp++);
2528 Test::keyboardKeyReleased(KEY_LEFTALT, timestamp++);
2529 Test::keyboardKeyReleased(KEY_LEFTCTRL, timestamp++);
2530 QVERIFY(!minimizedChangedSpy.wait(100));
2531 QVERIFY(m_window->isMinimized());
2532
2533 // The rule should be discarded when the window is closed.
2534 destroyTestWindow();
2535 createTestWindow();
2536 QVERIFY(m_window->shortcut().isEmpty());
2537
2538 destroyTestWindow();
2539}
2540
2541void TestXdgShellWindowRules::testDesktopFileDontAffect()
2542{
2543 // Currently, the desktop file name is derived from the app id. If the app id is
2544 // changed, then the old rules will be lost. Either setDesktopFileName should
2545 // be exposed or the desktop file name rule should be removed for wayland windows.
2546 QSKIP("Needs changes in KWin core to pass");
2547}
2548
2549void TestXdgShellWindowRules::testDesktopFileApply()
2550{
2551 // Currently, the desktop file name is derived from the app id. If the app id is
2552 // changed, then the old rules will be lost. Either setDesktopFileName should
2553 // be exposed or the desktop file name rule should be removed for wayland windows.
2554 QSKIP("Needs changes in KWin core to pass");
2555}
2556
2557void TestXdgShellWindowRules::testDesktopFileRemember()
2558{
2559 // Currently, the desktop file name is derived from the app id. If the app id is
2560 // changed, then the old rules will be lost. Either setDesktopFileName should
2561 // be exposed or the desktop file name rule should be removed for wayland windows.
2562 QSKIP("Needs changes in KWin core to pass");
2563}
2564
2565void TestXdgShellWindowRules::testDesktopFileForce()
2566{
2567 // Currently, the desktop file name is derived from the app id. If the app id is
2568 // changed, then the old rules will be lost. Either setDesktopFileName should
2569 // be exposed or the desktop file name rule should be removed for wayland windows.
2570 QSKIP("Needs changes in KWin core to pass");
2571}
2572
2573void TestXdgShellWindowRules::testDesktopFileApplyNow()
2574{
2575 // Currently, the desktop file name is derived from the app id. If the app id is
2576 // changed, then the old rules will be lost. Either setDesktopFileName should
2577 // be exposed or the desktop file name rule should be removed for wayland windows.
2578 QSKIP("Needs changes in KWin core to pass");
2579}
2580
2581void TestXdgShellWindowRules::testDesktopFileForceTemporarily()
2582{
2583 // Currently, the desktop file name is derived from the app id. If the app id is
2584 // changed, then the old rules will be lost. Either setDesktopFileName should
2585 // be exposed or the desktop file name rule should be removed for wayland windows.
2586 QSKIP("Needs changes in KWin core to pass");
2587}
2588
2589void TestXdgShellWindowRules::testActiveOpacityDontAffect()
2590{
2591 setWindowRule("opacityactive", 90, int(Rules::DontAffect));
2592
2593 createTestWindow();
2594 QVERIFY(m_window->isActive());
2595
2596 // The opacity should not be affected by the rule.
2597 QCOMPARE(m_window->opacity(), 1.0);
2598
2599 destroyTestWindow();
2600}
2601
2602void TestXdgShellWindowRules::testActiveOpacityForce()
2603{
2604 setWindowRule("opacityactive", 90, int(Rules::Force));
2605
2606 createTestWindow();
2607 QVERIFY(m_window->isActive());
2608 QCOMPARE(m_window->opacity(), 0.9);
2609
2610 destroyTestWindow();
2611}
2612
2613void TestXdgShellWindowRules::testActiveOpacityForceTemporarily()
2614{
2615 setWindowRule("opacityactive", 90, int(Rules::ForceTemporarily));
2616
2617 createTestWindow();
2618 QVERIFY(m_window->isActive());
2619 QCOMPARE(m_window->opacity(), 0.9);
2620
2621 // The rule should be discarded when the window is closed.
2622 destroyTestWindow();
2623 createTestWindow();
2624 QVERIFY(m_window->isActive());
2625 QCOMPARE(m_window->opacity(), 1.0);
2626
2627 destroyTestWindow();
2628}
2629
2630void TestXdgShellWindowRules::testInactiveOpacityDontAffect()
2631{
2632 setWindowRule("opacityinactive", 80, int(Rules::DontAffect));
2633
2634 createTestWindow();
2635 QVERIFY(m_window->isActive());
2636
2637 // Make the window inactive.
2638 workspace()->setActiveWindow(nullptr);
2639 QVERIFY(!m_window->isActive());
2640
2641 // The opacity of the window should not be affected by the rule.
2642 QCOMPARE(m_window->opacity(), 1.0);
2643
2644 destroyTestWindow();
2645}
2646
2647void TestXdgShellWindowRules::testInactiveOpacityForce()
2648{
2649 setWindowRule("opacityinactive", 80, int(Rules::Force));
2650
2651 createTestWindow();
2652 QVERIFY(m_window->isActive());
2653 QCOMPARE(m_window->opacity(), 1.0);
2654
2655 // Make the window inactive.
2656 workspace()->setActiveWindow(nullptr);
2657 QVERIFY(!m_window->isActive());
2658
2659 // The opacity should be forced by the rule.
2660 QCOMPARE(m_window->opacity(), 0.8);
2661
2662 destroyTestWindow();
2663}
2664
2665void TestXdgShellWindowRules::testInactiveOpacityForceTemporarily()
2666{
2667 setWindowRule("opacityinactive", 80, int(Rules::ForceTemporarily));
2668
2669 createTestWindow();
2670 QVERIFY(m_window->isActive());
2671 QCOMPARE(m_window->opacity(), 1.0);
2672
2673 // Make the window inactive.
2674 workspace()->setActiveWindow(nullptr);
2675 QVERIFY(!m_window->isActive());
2676
2677 // The opacity should be forced by the rule.
2678 QCOMPARE(m_window->opacity(), 0.8);
2679
2680 // The rule should be discarded when the window is closed.
2681 destroyTestWindow();
2682 createTestWindow();
2683
2684 QVERIFY(m_window->isActive());
2685 QCOMPARE(m_window->opacity(), 1.0);
2686 workspace()->setActiveWindow(nullptr);
2687 QVERIFY(!m_window->isActive());
2688 QCOMPARE(m_window->opacity(), 1.0);
2689
2690 destroyTestWindow();
2691}
2692
2693void TestXdgShellWindowRules::testNoBorderDontAffect()
2694{
2695 setWindowRule("noborder", true, int(Rules::DontAffect));
2696 createTestWindow(ServerSideDecoration);
2697
2698 // The window should not be affected by the rule.
2699 QVERIFY(!m_window->noBorder());
2700
2701 destroyTestWindow();
2702}
2703
2704void TestXdgShellWindowRules::testNoBorderApply()
2705{
2706 setWindowRule("noborder", true, int(Rules::Apply));
2707 createTestWindow(ServerSideDecoration);
2708
2709 // Initially, the window should not be decorated.
2710 QVERIFY(m_window->noBorder());
2711 QVERIFY(!m_window->isDecorated());
2712
2713 // But you should be able to change "no border" property afterwards.
2714 QVERIFY(m_window->userCanSetNoBorder());
2715 m_window->setNoBorder(false);
2716 QVERIFY(!m_window->noBorder());
2717
2718 // If one re-opens the window, it should have no border again.
2719 destroyTestWindow();
2720 createTestWindow(ServerSideDecoration);
2721 QVERIFY(m_window->noBorder());
2722
2723 destroyTestWindow();
2724}
2725
2726void TestXdgShellWindowRules::testNoBorderRemember()
2727{
2728 setWindowRule("noborder", true, int(Rules::Remember));
2729 createTestWindow(ServerSideDecoration);
2730
2731 // Initially, the window should not be decorated.
2732 QVERIFY(m_window->noBorder());
2733 QVERIFY(!m_window->isDecorated());
2734
2735 // Unset the "no border" property.
2736 QVERIFY(m_window->userCanSetNoBorder());
2737 m_window->setNoBorder(false);
2738 QVERIFY(!m_window->noBorder());
2739
2740 // Re-open the window, it should be decorated.
2741 destroyTestWindow();
2742 createTestWindow(ServerSideDecoration);
2743 QVERIFY(m_window->isDecorated());
2744 QVERIFY(!m_window->noBorder());
2745
2746 destroyTestWindow();
2747}
2748
2749void TestXdgShellWindowRules::testNoBorderForce()
2750{
2751 setWindowRule("noborder", true, int(Rules::Force));
2752 createTestWindow(ServerSideDecoration);
2753
2754 // The window should not be decorated.
2755 QVERIFY(m_window->noBorder());
2756 QVERIFY(!m_window->isDecorated());
2757
2758 // And the user should not be able to change the "no border" property.
2759 m_window->setNoBorder(false);
2760 QVERIFY(m_window->noBorder());
2761
2762 // Reopen the window.
2763 destroyTestWindow();
2764 createTestWindow(ServerSideDecoration);
2765
2766 // The "no border" property should be still forced.
2767 QVERIFY(m_window->noBorder());
2768
2769 destroyTestWindow();
2770}
2771
2772void TestXdgShellWindowRules::testNoBorderApplyNow()
2773{
2774 createTestWindow(ServerSideDecoration);
2775 QVERIFY(!m_window->noBorder());
2776
2777 // Initialize RuleBook with the test rule.
2778 setWindowRule("noborder", true, int(Rules::ApplyNow));
2779
2780 // The "no border" property should be set now.
2781 QVERIFY(m_window->noBorder());
2782
2783 // One should be still able to change the "no border" property.
2784 m_window->setNoBorder(false);
2785 QVERIFY(!m_window->noBorder());
2786
2787 // The rule should not be applied again.
2788 m_window->evaluateWindowRules();
2789 QVERIFY(!m_window->noBorder());
2790
2791 destroyTestWindow();
2792}
2793
2794void TestXdgShellWindowRules::testNoBorderForceTemporarily()
2795{
2796 setWindowRule("noborder", true, int(Rules::ForceTemporarily));
2797 createTestWindow(ServerSideDecoration);
2798
2799 // The "no border" property should be set.
2800 QVERIFY(m_window->noBorder());
2801
2802 // And you should not be able to change it.
2803 m_window->setNoBorder(false);
2804 QVERIFY(m_window->noBorder());
2805
2806 // The rule should be discarded when the window is closed.
2807 destroyTestWindow();
2808 createTestWindow(ServerSideDecoration);
2809 QVERIFY(!m_window->noBorder());
2810
2811 // The "no border" property is no longer forced.
2812 m_window->setNoBorder(true);
2813 QVERIFY(m_window->noBorder());
2814 m_window->setNoBorder(false);
2815 QVERIFY(!m_window->noBorder());
2816
2817 destroyTestWindow();
2818}
2819
2820void TestXdgShellWindowRules::testScreenDontAffect()
2821{
2822 const QList<KWin::Output *> outputs = workspace()->outputs();
2823
2824 setWindowRule("screen", int(1), int(Rules::DontAffect));
2825
2826 createTestWindow();
2827
2828 // The window should not be affected by the rule.
2829 QCOMPARE(m_window->output()->name(), outputs.at(0)->name());
2830
2831 // The user can still move the window to another screen.
2832 workspace()->sendWindowToOutput(m_window, outputs.at(1));
2833 QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
2834
2835 destroyTestWindow();
2836}
2837
2838void TestXdgShellWindowRules::testScreenApply()
2839{
2840 const QList<KWin::Output *> outputs = workspace()->outputs();
2841
2842 setWindowRule("screen", int(1), int(Rules::Apply));
2843
2844 createTestWindow();
2845
2846 // The window should be in the screen specified by the rule.
2847 QEXPECT_FAIL("", "Applying a screen rule on a new client fails on Wayland", Continue);
2848 QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
2849
2850 // The user can move the window to another screen.
2851 workspace()->sendWindowToOutput(m_window, outputs.at(0));
2852 QCOMPARE(m_window->output()->name(), outputs.at(0)->name());
2853
2854 destroyTestWindow();
2855}
2856
2857void TestXdgShellWindowRules::testScreenRemember()
2858{
2859 const QList<KWin::Output *> outputs = workspace()->outputs();
2860
2861 setWindowRule("screen", int(1), int(Rules::Remember));
2862
2863 createTestWindow();
2864
2865 // Initially, the window should be in the first screen
2866 QCOMPARE(m_window->output()->name(), outputs.at(0)->name());
2867
2868 // Move the window to the second screen.
2869 workspace()->sendWindowToOutput(m_window, outputs.at(1));
2870 QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
2871
2872 // Close and reopen the window.
2873 destroyTestWindow();
2874 createTestWindow();
2875
2876 QEXPECT_FAIL("", "Applying a screen rule on a new client fails on Wayland", Continue);
2877 QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
2878
2879 destroyTestWindow();
2880}
2881
2882void TestXdgShellWindowRules::testScreenForce()
2883{
2884 const QList<KWin::Output *> outputs = workspace()->outputs();
2885
2886 createTestWindow();
2887 QVERIFY(m_window->isActive());
2888
2889 setWindowRule("screen", int(1), int(Rules::Force));
2890
2891 // The window should be forced to the screen specified by the rule.
2892 QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
2893
2894 // User should not be able to move the window to another screen.
2895 workspace()->sendWindowToOutput(m_window, outputs.at(0));
2896 QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
2897
2898 // Disable the output where the window is on, so the window is moved the other screen
2899 OutputConfiguration config;
2900 auto changeSet = config.changeSet(outputs.at(1));
2901 changeSet->enabled = false;
2903
2904 QVERIFY(!outputs.at(1)->isEnabled());
2905 QCOMPARE(m_window->output()->name(), outputs.at(0)->name());
2906
2907 // Enable the output and check that the window is moved there again
2908 changeSet->enabled = true;
2910
2911 QVERIFY(outputs.at(1)->isEnabled());
2912 QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
2913
2914 // Close and reopen the window.
2915 destroyTestWindow();
2916 createTestWindow();
2917
2918 QEXPECT_FAIL("", "Applying a screen rule on a new client fails on Wayland", Continue);
2919 QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
2920
2921 destroyTestWindow();
2922}
2923
2924void TestXdgShellWindowRules::testScreenApplyNow()
2925{
2926 const QList<KWin::Output *> outputs = workspace()->outputs();
2927
2928 createTestWindow();
2929
2930 QCOMPARE(m_window->output()->name(), outputs.at(0)->name());
2931
2932 // Set the rule so the window should move to the screen specified by the rule.
2933 setWindowRule("screen", int(1), int(Rules::ApplyNow));
2934 QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
2935
2936 // The user can move the window to another screen.
2937 workspace()->sendWindowToOutput(m_window, outputs.at(0));
2938 QCOMPARE(m_window->output()->name(), outputs.at(0)->name());
2939
2940 // The rule should not be applied again.
2941 m_window->evaluateWindowRules();
2942 QCOMPARE(m_window->output()->name(), outputs.at(0)->name());
2943
2944 destroyTestWindow();
2945}
2946
2947void TestXdgShellWindowRules::testScreenForceTemporarily()
2948{
2949 const QList<KWin::Output *> outputs = workspace()->outputs();
2950
2951 createTestWindow();
2952
2953 setWindowRule("screen", int(1), int(Rules::ForceTemporarily));
2954
2955 // The window should be forced the second screen
2956 QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
2957
2958 // User is not allowed to move it
2959 workspace()->sendWindowToOutput(m_window, outputs.at(0));
2960 QCOMPARE(m_window->output()->name(), outputs.at(1)->name());
2961
2962 // Close and reopen the window.
2963 destroyTestWindow();
2964 createTestWindow();
2965
2966 // The rule should be discarded now
2967 QCOMPARE(m_window->output()->name(), outputs.at(0)->name());
2968
2969 destroyTestWindow();
2970}
2971
2972void TestXdgShellWindowRules::testMatchAfterNameChange()
2973{
2974 setWindowRule("above", true, int(Rules::Force));
2975
2976 std::unique_ptr<KWayland::Client::Surface> surface(Test::createSurface());
2977 std::unique_ptr<Test::XdgToplevel> shellSurface(Test::createXdgToplevelSurface(surface.get()));
2978
2979 auto window = Test::renderAndWaitForShown(surface.get(), QSize(100, 50), Qt::blue);
2980 QVERIFY(window);
2981 QVERIFY(window->isActive());
2982 QCOMPARE(window->keepAbove(), false);
2983
2984 QSignalSpy desktopFileNameSpy(window, &Window::desktopFileNameChanged);
2985
2986 shellSurface->set_app_id(QStringLiteral("org.kde.foo"));
2987 QVERIFY(desktopFileNameSpy.wait());
2988 QCOMPARE(window->keepAbove(), true);
2989}
2990
2991void TestXdgShellWindowRules::testLayerDontAffect()
2992{
2993 setWindowRule("layer", QStringLiteral("overlay"), int(Rules::DontAffect));
2994
2995 createTestWindow();
2996
2997 // The layer should not be affected by the rule.
2998 QCOMPARE(m_window->layer(), NormalLayer);
2999
3000 destroyTestWindow();
3001}
3002
3003void TestXdgShellWindowRules::testLayerForce()
3004{
3005 setWindowRule("layer", QStringLiteral("overlay"), int(Rules::Force));
3006
3007 createTestWindow();
3008 QCOMPARE(m_window->layer(), OverlayLayer);
3009
3010 destroyTestWindow();
3011}
3012
3013void TestXdgShellWindowRules::testLayerForceTemporarily()
3014{
3015 setWindowRule("layer", QStringLiteral("overlay"), int(Rules::ForceTemporarily));
3016
3017 createTestWindow();
3018 QCOMPARE(m_window->layer(), OverlayLayer);
3019
3020 // The rule should be discarded when the window is closed.
3021 destroyTestWindow();
3022 createTestWindow();
3023 QCOMPARE(m_window->layer(), NormalLayer);
3024
3025 destroyTestWindow();
3026}
3027
3028void TestXdgShellWindowRules::testCloseableDontAffect()
3029{
3030 setWindowRule("closeable", false, int(Rules::DontAffect));
3031
3032 createTestWindow();
3033
3034 QVERIFY(m_window->isCloseable());
3035
3036 destroyTestWindow();
3037}
3038
3039void TestXdgShellWindowRules::testCloseableForce()
3040{
3041 setWindowRule("closeable", false, int(Rules::Force));
3042
3043 createTestWindow();
3044 QVERIFY(!m_window->isCloseable());
3045
3046 destroyTestWindow();
3047}
3048
3049void TestXdgShellWindowRules::testCloseableForceTemporarily()
3050{
3051 setWindowRule("closeable", false, int(Rules::ForceTemporarily));
3052
3053 createTestWindow();
3054 QVERIFY(!m_window->isCloseable());
3055
3056 // The rule should be discarded when the window is closed.
3057 destroyTestWindow();
3058 createTestWindow();
3059 QVERIFY(m_window->isCloseable());
3060
3061 destroyTestWindow();
3062}
3063
3065#include "xdgshellwindow_rules_test.moc"
QPointF pos()
Definition cursor.cpp:204
static Cursors * self()
Definition cursor.cpp:35
Cursor * mouse() const
Definition cursor.h:266
std::shared_ptr< OutputChangeSet > changeSet(Output *output)
QString name
Definition output.h:136
void setConfig(const KSharedConfig::Ptr &config)
Definition rules.h:313
@ ForceTemporarily
Definition rules.h:127
@ DontAffect
Definition rules.h:122
@ ExactMatch
Definition rules.h:132
void configureRequested(quint32 serial)
void configureRequested(const QSize &size, KWin::Test::XdgToplevel::States states)
QPointF pos
Definition window.h:79
bool skipSwitcher
Definition window.h:320
void setMinimized(bool set)
Definition window.cpp:984
QRectF frameGeometry
Definition window.h:431
bool isInteractiveMove() const
Definition window.h:1107
virtual bool isMaximizable() const
Definition window.cpp:3977
void updateInteractiveMoveResize(const QPointF &currentGlobalCursor)
Definition window.cpp:1378
void keyPressEvent(uint key_code)
Definition window.cpp:2531
virtual bool isMinimizable() const
Definition window.cpp:3967
QSizeF size
Definition window.h:84
bool keepBelow
Definition window.h:337
bool noBorder
Definition window.h:456
void setKeepBelow(bool)
Definition window.cpp:649
virtual bool isMovableAcrossScreens() const =0
bool isInteractiveResize() const
Definition window.h:1114
void setDesktops(QList< VirtualDesktop * > desktops)
Definition window.cpp:726
virtual MaximizeMode requestedMaximizeMode() const
Definition window.cpp:3999
void setOriginalSkipTaskbar(bool set)
Definition window.cpp:479
bool isMinimized() const
Definition window.h:988
bool isActive() const
Definition window.h:882
bool isDecorated() const
Definition window.h:1162
QList< KWin::VirtualDesktop * > desktops
Definition window.h:295
virtual bool isMovable() const =0
virtual bool isCloseable() const =0
void evaluateWindowRules()
Definition window.cpp:4074
void setSkipPager(bool set)
Definition window.cpp:448
qreal opacity
Definition window.h:106
virtual void setNoBorder(bool set)
Definition window.cpp:4034
virtual bool isResizable() const =0
bool skipPager
Definition window.h:315
KWin::Layer layer
Definition window.h:529
bool keepAbove
Definition window.h:332
void setKeepAbove(bool)
Definition window.cpp:628
const QKeySequence & shortcut() const
Definition window.h:979
KWin::Output * output
Definition window.h:111
virtual bool userCanSetNoBorder() const
Definition window.cpp:4029
virtual MaximizeMode maximizeMode() const
Definition window.cpp:3987
void setShortcut(const QString &cut)
void setSkipSwitcher(bool set)
Definition window.cpp:436
bool skipTaskbar
Definition window.h:310
bool applyOutputConfiguration(const OutputConfiguration &config, const QList< Output * > &outputOrder={})
void sendWindowToOutput(Window *window, Output *output)
RuleBook * rulebook() const
QList< Output * > outputs() const
Definition workspace.h:762
void setActiveWindow(Window *window)
void setActiveOutput(Output *output)
void slotReconfigure()
#define WAYLANDTEST_MAIN(TestObject)
Window * renderAndWaitForShown(KWayland::Client::Surface *surface, const QSize &size, const QColor &color, const QImage::Format &format=QImage::Format_ARGB32, int timeout=5000)
void keyboardKeyReleased(quint32 key, quint32 time)
void destroyWaylandConnection()
void setOutputConfig(const QList< QRect > &geometries)
void keyboardKeyPressed(quint32 key, quint32 time)
bool setupWaylandConnection(AdditionalWaylandInterfaces flags=AdditionalWaylandInterfaces())
void render(KWayland::Client::Surface *surface, const QSize &size, const QColor &color, const QImage::Format &format=QImage::Format_ARGB32_Premultiplied)
QList< KWayland::Client::Output * > outputs
std::unique_ptr< KWayland::Client::Surface > createSurface()
XdgToplevel * createXdgToplevelSurface(KWayland::Client::Surface *surface, QObject *parent=nullptr)
XdgToplevelDecorationV1 * createXdgToplevelDecorationV1(XdgToplevel *toplevel, QObject *parent=nullptr)
bool waitForWindowClosed(Window *window)
WaylandServer * waylandServer()
Workspace * workspace()
Definition workspace.h:830
@ OverlayLayer
Definition globals.h:174
@ NormalLayer
Definition globals.h:167