19#include <KWayland/Client/compositor.h>
20#include <KWayland/Client/surface.h>
23#include <xcb/xcb_icccm.h>
27static const QString s_socketName = QStringLiteral(
"wayland_test_kwin_stacking_order-0");
38 void testTransientIsAboveParent();
39 void testRaiseTransient();
40 void testDeletedTransient();
42 void testGroupTransientIsAboveWindowGroup();
43 void testRaiseGroupTransient();
44 void testDeletedGroupTransient();
45 void testDontKeepAboveNonModalDialogGroupTransients();
50 void testPreserveRelativeWindowStacking();
53void StackingOrderTest::initTestCase()
55 qRegisterMetaType<KWin::Window *>();
60 QRect(0, 0, 1280, 1024),
61 QRect(1280, 0, 1280, 1024),
64 kwinApp()->setConfig(KSharedConfig::openConfig(QString(), KConfig::SimpleConfig));
67 QVERIFY(applicationStartedSpy.wait());
70void StackingOrderTest::init()
75void StackingOrderTest::cleanup()
80void StackingOrderTest::testTransientIsAboveParent()
86 QVERIFY(parentSurface);
88 QVERIFY(parentShellSurface);
91 QVERIFY(parent->isActive());
92 QVERIFY(!parent->isTransient());
95 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{parent}));
99 QVERIFY(transientSurface);
101 QVERIFY(transientShellSurface);
102 transientShellSurface->set_parent(parentShellSurface->object());
105 QVERIFY(transient->isActive());
106 QVERIFY(transient->isTransient());
109 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{parent, transient}));
113 QTRY_VERIFY(parent->isActive());
114 QTRY_VERIFY(!transient->isActive());
115 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{parent, transient}));
118void StackingOrderTest::testRaiseTransient()
125 QVERIFY(parentSurface);
127 QVERIFY(parentShellSurface);
130 QVERIFY(parent->isActive());
131 QVERIFY(!parent->isTransient());
134 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{parent}));
138 QVERIFY(transientSurface);
140 QVERIFY(transientShellSurface);
141 transientShellSurface->set_parent(parentShellSurface->object());
144 QTRY_VERIFY(transient->isActive());
145 QVERIFY(transient->isTransient());
148 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{parent, transient}));
152 QVERIFY(anotherSurface);
154 QVERIFY(anotherShellSurface);
156 QVERIFY(anotherWindow);
157 QVERIFY(anotherWindow->isActive());
158 QVERIFY(!anotherWindow->isTransient());
161 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{parent, transient, anotherWindow}));
165 QTRY_VERIFY(parent->isActive());
166 QTRY_VERIFY(!transient->isActive());
167 QTRY_VERIFY(!anotherWindow->isActive());
168 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{anotherWindow, parent, transient}));
172 QTRY_VERIFY(!parent->isActive());
173 QTRY_VERIFY(!transient->isActive());
174 QTRY_VERIFY(anotherWindow->isActive());
175 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{parent, transient, anotherWindow}));
179 QTRY_VERIFY(!parent->isActive());
180 QTRY_VERIFY(transient->isActive());
181 QTRY_VERIFY(!anotherWindow->isActive());
182 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{anotherWindow, parent, transient}));
195void StackingOrderTest::testDeletedTransient()
202 QVERIFY(parentSurface);
203 std::unique_ptr<Test::XdgToplevel>
205 QVERIFY(parentShellSurface);
208 QVERIFY(parent->isActive());
209 QVERIFY(!parent->isTransient());
211 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{parent}));
215 QVERIFY(transient1Surface);
217 QVERIFY(transient1ShellSurface);
218 transient1ShellSurface->set_parent(parentShellSurface->object());
221 QTRY_VERIFY(transient1->isActive());
222 QVERIFY(transient1->isTransient());
223 QCOMPARE(transient1->transientFor(), parent);
225 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{parent, transient1}));
229 QVERIFY(transient2Surface);
231 QVERIFY(transient2ShellSurface);
232 transient2ShellSurface->set_parent(transient1ShellSurface->object());
235 QTRY_VERIFY(transient2->isActive());
236 QVERIFY(transient2->isTransient());
237 QCOMPARE(transient2->transientFor(), transient1);
239 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{parent, transient1, transient2}));
243 QTRY_VERIFY(parent->isActive());
244 QTRY_VERIFY(!transient1->isActive());
245 QTRY_VERIFY(!transient2->isActive());
248 connect(transient2, &Window::closed, transient2, &Window::ref);
249 auto cleanup = qScopeGuard([transient2]() {
253 QSignalSpy windowClosedSpy(transient2, &Window::closed);
254 transient2ShellSurface.reset();
255 transient2Surface.reset();
256 QVERIFY(windowClosedSpy.wait());
259 QTRY_VERIFY(parent->isActive());
260 QTRY_VERIFY(!transient1->isActive());
261 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{parent, transient1, transient2}));
264static xcb_window_t createGroupWindow(xcb_connection_t *conn,
265 const QRect &geometry,
266 xcb_window_t leaderWid = XCB_WINDOW_NONE)
268 xcb_window_t wid = xcb_generate_id(conn);
271 XCB_COPY_FROM_PARENT,
279 XCB_WINDOW_CLASS_INPUT_OUTPUT,
280 XCB_COPY_FROM_PARENT,
285 xcb_size_hints_t sizeHints = {};
286 xcb_icccm_size_hints_set_position(&sizeHints, 1, geometry.x(), geometry.y());
287 xcb_icccm_size_hints_set_size(&sizeHints, 1, geometry.width(), geometry.height());
288 xcb_icccm_set_wm_normal_hints(conn, wid, &sizeHints);
290 if (leaderWid == XCB_WINDOW_NONE) {
296 XCB_PROP_MODE_REPLACE,
308void StackingOrderTest::testGroupTransientIsAboveWindowGroup()
313 const QRect geometry = QRect(0, 0, 128, 128);
320 xcb_window_t leaderWid = createGroupWindow(conn.get(), geometry);
321 xcb_map_window(conn.get(), leaderWid);
322 xcb_flush(conn.get());
324 QVERIFY(windowCreatedSpy.wait());
328 QCOMPARE(leader->
window(), leaderWid);
331 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{leader}));
334 windowCreatedSpy.clear();
335 xcb_window_t member1Wid = createGroupWindow(conn.get(), geometry, leaderWid);
336 xcb_map_window(conn.get(), member1Wid);
337 xcb_flush(conn.get());
339 QVERIFY(windowCreatedSpy.wait());
343 QCOMPARE(member1->
window(), member1Wid);
347 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{leader, member1}));
350 windowCreatedSpy.clear();
351 xcb_window_t member2Wid = createGroupWindow(conn.get(), geometry, leaderWid);
352 xcb_map_window(conn.get(), member2Wid);
353 xcb_flush(conn.get());
355 QVERIFY(windowCreatedSpy.wait());
359 QCOMPARE(member2->
window(), member2Wid);
363 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{leader, member1, member2}));
366 windowCreatedSpy.clear();
367 xcb_window_t transientWid = createGroupWindow(conn.get(), geometry, leaderWid);
368 xcb_icccm_set_wm_transient_for(conn.get(), transientWid,
rootWindow());
375 xcb_atom_t net_wm_window_type =
Xcb::Atom(
376 QByteArrayLiteral(
"_NET_WM_WINDOW_TYPE"),
false, conn.get());
377 xcb_atom_t net_wm_window_type_normal =
Xcb::Atom(
378 QByteArrayLiteral(
"_NET_WM_WINDOW_TYPE_NORMAL"),
false, conn.get());
381 XCB_PROP_MODE_REPLACE,
387 &net_wm_window_type_normal
390 xcb_map_window(conn.get(), transientWid);
391 xcb_flush(conn.get());
393 QVERIFY(windowCreatedSpy.wait());
397 QCOMPARE(transient->
window(), transientWid);
398 QCOMPARE(transient->
group(), leader->
group());
403 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{leader, member1, member2, transient}));
408 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{member1, member2, leader, transient}));
412 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{member2, leader, member1, transient}));
416 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{leader, member1, member2, transient}));
420 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{leader, member1, member2, transient}));
423void StackingOrderTest::testRaiseGroupTransient()
425 const QRect geometry = QRect(0, 0, 128, 128);
432 xcb_window_t leaderWid = createGroupWindow(conn.get(), geometry);
433 xcb_map_window(conn.get(), leaderWid);
434 xcb_flush(conn.get());
436 QVERIFY(windowCreatedSpy.wait());
440 QCOMPARE(leader->
window(), leaderWid);
443 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{leader}));
446 windowCreatedSpy.clear();
447 xcb_window_t member1Wid = createGroupWindow(conn.get(), geometry, leaderWid);
448 xcb_map_window(conn.get(), member1Wid);
449 xcb_flush(conn.get());
451 QVERIFY(windowCreatedSpy.wait());
455 QCOMPARE(member1->
window(), member1Wid);
459 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{leader, member1}));
462 windowCreatedSpy.clear();
463 xcb_window_t member2Wid = createGroupWindow(conn.get(), geometry, leaderWid);
464 xcb_map_window(conn.get(), member2Wid);
465 xcb_flush(conn.get());
467 QVERIFY(windowCreatedSpy.wait());
471 QCOMPARE(member2->
window(), member2Wid);
475 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{leader, member1, member2}));
478 windowCreatedSpy.clear();
479 xcb_window_t transientWid = createGroupWindow(conn.get(), geometry, leaderWid);
480 xcb_icccm_set_wm_transient_for(conn.get(), transientWid,
rootWindow());
487 xcb_atom_t net_wm_window_type =
Xcb::Atom(
488 QByteArrayLiteral(
"_NET_WM_WINDOW_TYPE"),
false, conn.get());
489 xcb_atom_t net_wm_window_type_normal =
Xcb::Atom(
490 QByteArrayLiteral(
"_NET_WM_WINDOW_TYPE_NORMAL"),
false, conn.get());
493 XCB_PROP_MODE_REPLACE,
499 &net_wm_window_type_normal
502 xcb_map_window(conn.get(), transientWid);
503 xcb_flush(conn.get());
505 QVERIFY(windowCreatedSpy.wait());
509 QCOMPARE(transient->
window(), transientWid);
510 QCOMPARE(transient->
group(), leader->
group());
515 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{leader, member1, member2, transient}));
519 QVERIFY(anotherSurface);
521 QVERIFY(anotherShellSurface);
523 QVERIFY(anotherWindow);
524 QVERIFY(anotherWindow->isActive());
525 QVERIFY(!anotherWindow->isTransient());
527 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{leader, member1, member2, transient, anotherWindow}));
532 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{member1, member2, anotherWindow, leader, transient}));
538 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{member1, anotherWindow, leader, member2, transient}));
542 QTRY_VERIFY(anotherWindow->isActive());
543 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{member1, leader, member2, transient, anotherWindow}));
547 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{member1, leader, member2, anotherWindow, transient}));
550void StackingOrderTest::testDeletedGroupTransient()
555 const QRect geometry = QRect(0, 0, 128, 128);
562 xcb_window_t leaderWid = createGroupWindow(conn.get(), geometry);
563 xcb_map_window(conn.get(), leaderWid);
564 xcb_flush(conn.get());
566 QVERIFY(windowCreatedSpy.wait());
570 QCOMPARE(leader->
window(), leaderWid);
573 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{leader}));
576 windowCreatedSpy.clear();
577 xcb_window_t member1Wid = createGroupWindow(conn.get(), geometry, leaderWid);
578 xcb_map_window(conn.get(), member1Wid);
579 xcb_flush(conn.get());
581 QVERIFY(windowCreatedSpy.wait());
585 QCOMPARE(member1->
window(), member1Wid);
589 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{leader, member1}));
592 windowCreatedSpy.clear();
593 xcb_window_t member2Wid = createGroupWindow(conn.get(), geometry, leaderWid);
594 xcb_map_window(conn.get(), member2Wid);
595 xcb_flush(conn.get());
597 QVERIFY(windowCreatedSpy.wait());
601 QCOMPARE(member2->
window(), member2Wid);
605 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{leader, member1, member2}));
608 windowCreatedSpy.clear();
609 xcb_window_t transientWid = createGroupWindow(conn.get(), geometry, leaderWid);
610 xcb_icccm_set_wm_transient_for(conn.get(), transientWid,
rootWindow());
617 xcb_atom_t net_wm_window_type =
Xcb::Atom(
618 QByteArrayLiteral(
"_NET_WM_WINDOW_TYPE"),
false, conn.get());
619 xcb_atom_t net_wm_window_type_normal =
Xcb::Atom(
620 QByteArrayLiteral(
"_NET_WM_WINDOW_TYPE_NORMAL"),
false, conn.get());
623 XCB_PROP_MODE_REPLACE,
629 &net_wm_window_type_normal
632 xcb_map_window(conn.get(), transientWid);
633 xcb_flush(conn.get());
635 QVERIFY(windowCreatedSpy.wait());
639 QCOMPARE(transient->
window(), transientWid);
640 QCOMPARE(transient->
group(), leader->
group());
645 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{leader, member1, member2, transient}));
648 connect(transient, &Window::closed, transient, &Window::ref);
649 auto cleanup = qScopeGuard([transient]() {
653 QSignalSpy windowClosedSpy(transient, &X11Window::closed);
654 xcb_unmap_window(conn.get(), transientWid);
655 xcb_flush(conn.get());
656 QVERIFY(windowClosedSpy.wait());
659 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{leader, member1, member2, transient}));
662void StackingOrderTest::testDontKeepAboveNonModalDialogGroupTransients()
666 const QRect geometry = QRect(0, 0, 128, 128);
673 xcb_window_t leaderWid = createGroupWindow(conn.get(), geometry);
674 xcb_map_window(conn.get(), leaderWid);
675 xcb_flush(conn.get());
677 QVERIFY(windowCreatedSpy.wait());
681 QCOMPARE(leader->
window(), leaderWid);
684 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{leader}));
687 windowCreatedSpy.clear();
688 xcb_window_t member1Wid = createGroupWindow(conn.get(), geometry, leaderWid);
689 xcb_map_window(conn.get(), member1Wid);
690 xcb_flush(conn.get());
692 QVERIFY(windowCreatedSpy.wait());
696 QCOMPARE(member1->
window(), member1Wid);
700 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{leader, member1}));
703 windowCreatedSpy.clear();
704 xcb_window_t member2Wid = createGroupWindow(conn.get(), geometry, leaderWid);
705 xcb_map_window(conn.get(), member2Wid);
706 xcb_flush(conn.get());
708 QVERIFY(windowCreatedSpy.wait());
712 QCOMPARE(member2->
window(), member2Wid);
716 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{leader, member1, member2}));
719 windowCreatedSpy.clear();
720 xcb_window_t transientWid = createGroupWindow(conn.get(), geometry, leaderWid);
721 xcb_icccm_set_wm_transient_for(conn.get(), transientWid,
rootWindow());
722 xcb_map_window(conn.get(), transientWid);
723 xcb_flush(conn.get());
725 QVERIFY(windowCreatedSpy.wait());
729 QCOMPARE(transient->
window(), transientWid);
730 QCOMPARE(transient->
group(), leader->
group());
734 QVERIFY(!transient->
isModal());
736 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{leader, member1, member2, transient}));
740 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{member1, member2, transient, leader}));
744 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{member2, transient, leader, member1}));
748 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{transient, leader, member1, member2}));
752 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{leader, member1, member2, transient}));
755void StackingOrderTest::testKeepAbove()
763 QVERIFY(shellSurface1);
766 QVERIFY(window1->isActive());
767 QVERIFY(!window1->keepAbove());
769 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{window1}));
775 QVERIFY(shellSurface2);
778 QVERIFY(window2->isActive());
779 QVERIFY(!window2->keepAbove());
781 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{window1, window2}));
785 QTRY_VERIFY(window1->isActive());
786 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{window2, window1}));
791 window2->setKeepAbove(
true);
794 QVERIFY(window2->keepAbove());
795 QVERIFY(!window2->isActive());
796 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{window1, window2}));
799void StackingOrderTest::testKeepBelow()
807 QVERIFY(shellSurface1);
810 QVERIFY(window1->isActive());
811 QVERIFY(!window1->keepBelow());
813 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{window1}));
819 QVERIFY(shellSurface2);
822 QVERIFY(window2->isActive());
823 QVERIFY(!window2->keepBelow());
825 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{window1, window2}));
830 window2->setKeepBelow(
true);
833 QVERIFY(window2->isActive());
834 QVERIFY(window2->keepBelow());
835 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{window2, window1}));
838void StackingOrderTest::testPreserveRelativeWindowStacking()
843 const int windowsQuantity = 5;
845 std::unique_ptr<KWayland::Client::Surface> surfaces[windowsQuantity];
846 std::unique_ptr<Test::XdgToplevel> shellSurfaces[windowsQuantity];
847 Window *windows[windowsQuantity];
850 for (
int i = 0; i < windowsQuantity; i++) {
852 QVERIFY(surfaces[i]);
854 QVERIFY(shellSurfaces[i]);
861 shellSurfaces[1]->set_parent(shellSurfaces[0]->
object());
862 shellSurfaces[2]->set_parent(shellSurfaces[0]->
object());
863 shellSurfaces[3]->set_parent(shellSurfaces[0]->
object());
864 shellSurfaces[4]->set_parent(shellSurfaces[3]->
object());
866 for (
int i = 0; i < windowsQuantity; i++) {
872 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{windows[0], windows[1], windows[2], windows[3], windows[4]}));
877 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{windows[0], windows[1], windows[2], windows[3], windows[4]}));
883 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{windows[0], windows[3], windows[4], windows[2], windows[1]}));
888 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{windows[0], windows[3], windows[4], windows[2], windows[1]}));
893 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{windows[0], windows[2], windows[1], windows[3], windows[4]}));
898 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{windows[0], windows[2], windows[1], windows[3], windows[4]}));
901 windows[0]->setKeepAbove(
true);
903 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{windows[0], windows[2], windows[1], windows[3], windows[4]}));
907 QCOMPARE(
workspace()->stackingOrder(), (QList<Window *>{windows[0], windows[3], windows[4], windows[1], windows[2]}));
911#include "stacking_order_test.moc"
Xcb::Atom wm_client_leader
void activateWindow(Window *window, bool force=false)
void windowAdded(KWin::Window *)
bool isTransient() const override
xcb_window_t window() const
const Group * group() const override
bool groupTransient() const override
#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 destroyWaylandConnection()
void setOutputConfig(const QList< QRect > &geometries)
bool setupWaylandConnection(AdditionalWaylandInterfaces flags=AdditionalWaylandInterfaces())
XcbConnectionPtr createX11Connection()
std::unique_ptr< KWayland::Client::Surface > createSurface()
XdgToplevel * createXdgToplevelSurface(KWayland::Client::Surface *surface, QObject *parent=nullptr)
std::unique_ptr< xcb_connection_t, XcbConnectionDeleter > XcbConnectionPtr
KWIN_EXPORT xcb_window_t rootWindow()
WaylandServer * waylandServer()
KWIN_EXPORT Atoms * atoms
void operator()(Window *d)