1 /*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 1999, 2000 Matthias Ettrich <ettrich@kde.org>
6 SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org>
7
8 SPDX-License-Identifier: GPL-2.0-or-later
9 */
10
11 // SELI zmenit doc
12
13 /*
14
15 This file contains things relevant to stacking order and layers.
16
17 Design:
18
19 Normal unconstrained stacking order, as requested by the user (by clicking
20 on windows to raise them, etc.), is in Workspace::unconstrained_stacking_order.
21 That list shouldn't be used at all, except for building
22 Workspace::stacking_order. The building is done
23 in Workspace::constrainedStackingOrder(). Only Workspace::stackingOrder() should
24 be used to get the stacking order, because it also checks the stacking order
25 is up to date.
26 All clients are also stored in Workspace::clients (except for isDesktop() clients,
27 as those are very special, and are stored in Workspace::desktops), in the order
28 the clients were created.
29
30 Every window has one layer assigned in which it is. There are 7 layers,
31 from bottom : DesktopLayer, BelowLayer, NormalLayer, DockLayer, AboveLayer, NotificationLayer,
32 ActiveLayer, CriticalNotificationLayer, and OnScreenDisplayLayer (see also NETWM sect.7.10.).
33 The layer a window is in depends on the window type, and on other things like whether the window
34 is active. We extend the layers provided in NETWM by the NotificationLayer, OnScreenDisplayLayer,
35 and CriticalNotificationLayer.
36 The NoficationLayer contains notification windows which are kept above all windows except the active
37 fullscreen window. The CriticalNotificationLayer contains notification windows which are important
38 enough to keep them even above fullscreen windows. The OnScreenDisplayLayer is used for eg. volume
39 and brightness change feedback and is kept above all windows since it provides immediate response
40 to a user action.
41
42 NET::Splash clients belong to the Normal layer. NET::TopMenu clients
43 belong to Dock layer. Clients that are both NET::Dock and NET::KeepBelow
44 are in the Normal layer in order to keep the 'allow window to cover
45 the panel' Kicker setting to work as intended (this may look like a slight
46 spec violation, but a) I have no better idea, b) the spec allows adjusting
47 the stacking order if the WM thinks it's a good idea . We put all
48 NET::KeepAbove above all Docks too, even though the spec suggests putting
49 them in the same layer.
50
51 Most transients are in the same layer as their mainwindow,
52 see Workspace::constrainedStackingOrder(), they may also be in higher layers, but
53 they should never be below their mainwindow.
54
55 Currently the things that affect client in which layer a client
56 belongs: KeepAbove/Keep Below flags, window type, fullscreen
57 state and whether the client is active, mainclient (transiency).
58
59 Make sure updateStackingOrder() is called in order to make
60 Workspace::stackingOrder() up to date and propagated to the world.
61 Using Workspace::blockStackingUpdates() (or the StackingUpdatesBlocker
62 helper class) it's possible to temporarily disable updates
63 and the stacking order will be updated once after it's allowed again.
64
65 */
66
67 #include "utils.h"
68 #include "x11client.h"
69 #include "focuschain.h"
70 #include "netinfo.h"
71 #include "workspace.h"
72 #include "tabbox.h"
73 #include "group.h"
74 #include "rules.h"
75 #include "screens.h"
76 #include "unmanaged.h"
77 #include "deleted.h"
78 #include "effects.h"
79 #include "composite.h"
80 #include "screenedge.h"
81 #include "wayland_server.h"
82 #include "internal_client.h"
83 #include "virtualdesktops.h"
84
85 #include <array>
86
87 #include <QDebug>
88 #include <QQueue>
89
90 namespace KWin
91 {
92
93 //*******************************
94 // Workspace
95 //*******************************
96
updateStackingOrder(bool propagate_new_clients)97 void Workspace::updateStackingOrder(bool propagate_new_clients)
98 {
99 if (block_stacking_updates > 0) {
100 if (propagate_new_clients)
101 blocked_propagating_new_clients = true;
102 return;
103 }
104 QList<Toplevel *> new_stacking_order = constrainedStackingOrder();
105 bool changed = (force_restacking || new_stacking_order != stacking_order);
106 force_restacking = false;
107 stacking_order = new_stacking_order;
108 if (changed || propagate_new_clients) {
109 propagateClients(propagate_new_clients);
110 markXStackingOrderAsDirty();
111
112 for (int i = 0; i < stacking_order.size(); ++i) {
113 stacking_order[i]->setStackingOrder(i);
114 }
115
116 Q_EMIT stackingOrderChanged();
117 if (m_compositor) {
118 m_compositor->addRepaintFull();
119 }
120
121 if (active_client)
122 active_client->updateMouseGrab();
123 }
124 }
125
126 /**
127 * Some fullscreen effects have to raise the screenedge on top of an input window, thus all windows
128 * this function puts them back where they belong for regular use and is some cheap variant of
129 * the regular propagateClients function in that it completely ignores managed clients and everything
130 * else and also does not update the NETWM property.
131 * Called from Effects::destroyInputWindow so far.
132 */
stackScreenEdgesUnderOverrideRedirect()133 void Workspace::stackScreenEdgesUnderOverrideRedirect()
134 {
135 if (!rootInfo()) {
136 return;
137 }
138 Xcb::restackWindows(QVector<xcb_window_t>() << rootInfo()->supportWindow() << ScreenEdges::self()->windows());
139 }
140
141 /**
142 * Propagates the managed clients to the world.
143 * Called ONLY from updateStackingOrder().
144 */
propagateClients(bool propagate_new_clients)145 void Workspace::propagateClients(bool propagate_new_clients)
146 {
147 if (!rootInfo()) {
148 return;
149 }
150 // restack the windows according to the stacking order
151 // supportWindow > electric borders > clients > hidden clients
152 QVector<xcb_window_t> newWindowStack;
153
154 // Stack all windows under the support window. The support window is
155 // not used for anything (besides the NETWM property), and it's not shown,
156 // but it was lowered after kwin startup. Stacking all clients below
157 // it ensures that no client will be ever shown above override-redirect
158 // windows (e.g. popups).
159 newWindowStack << rootInfo()->supportWindow();
160
161 newWindowStack << ScreenEdges::self()->windows();
162
163 newWindowStack << manual_overlays;
164
165 newWindowStack.reserve(newWindowStack.size() + 2*stacking_order.size()); // *2 for inputWindow
166
167 for (int i = stacking_order.size() - 1; i >= 0; --i) {
168 X11Client *client = qobject_cast<X11Client *>(stacking_order.at(i));
169 if (!client || client->hiddenPreview()) {
170 continue;
171 }
172
173 if (client->inputId())
174 // Stack the input window above the frame
175 newWindowStack << client->inputId();
176
177 newWindowStack << client->frameId();
178 }
179
180 // when having hidden previews, stack hidden windows below everything else
181 // (as far as pure X stacking order is concerned), in order to avoid having
182 // these windows that should be unmapped to interfere with other windows
183 for (int i = stacking_order.size() - 1; i >= 0; --i) {
184 X11Client *client = qobject_cast<X11Client *>(stacking_order.at(i));
185 if (!client || !client->hiddenPreview())
186 continue;
187 newWindowStack << client->frameId();
188 }
189 // TODO isn't it too inefficient to restack always all clients?
190 // TODO don't restack not visible windows?
191 Q_ASSERT(newWindowStack.at(0) == rootInfo()->supportWindow());
192 Xcb::restackWindows(newWindowStack);
193
194 int pos = 0;
195 xcb_window_t *cl(nullptr);
196 if (propagate_new_clients) {
197 cl = new xcb_window_t[ manual_overlays.count() + m_x11Clients.count()];
198 for (const auto win : qAsConst(manual_overlays)) {
199 cl[pos++] = win;
200 }
201 for (auto it = m_x11Clients.constBegin(); it != m_x11Clients.constEnd(); ++it)
202 cl[pos++] = (*it)->window();
203 rootInfo()->setClientList(cl, pos);
204 delete [] cl;
205 }
206
207 cl = new xcb_window_t[ manual_overlays.count() + stacking_order.count()];
208 pos = 0;
209 for (auto it = stacking_order.constBegin(); it != stacking_order.constEnd(); ++it) {
210 X11Client *client = qobject_cast<X11Client *>(*it);
211 if (client) {
212 cl[pos++] = client->window();
213 }
214 }
215 for (const auto win : qAsConst(manual_overlays)) {
216 cl[pos++] = win;
217 }
218 rootInfo()->setClientListStacking(cl, pos);
219 delete [] cl;
220 }
221
222 /**
223 * Returns topmost visible client. Windows on the dock, the desktop
224 * or of any other special kind are excluded. Also if the window
225 * doesn't accept focus it's excluded.
226 */
227 // TODO misleading name for this method, too many slightly different ways to use it
topClientOnDesktop(VirtualDesktop * desktop,AbstractOutput * output,bool unconstrained,bool only_normal) const228 AbstractClient *Workspace::topClientOnDesktop(VirtualDesktop *desktop, AbstractOutput *output, bool unconstrained, bool only_normal) const
229 {
230 // TODO Q_ASSERT( block_stacking_updates == 0 );
231 QList<Toplevel *> list;
232 if (!unconstrained)
233 list = stacking_order;
234 else
235 list = unconstrained_stacking_order;
236 for (int i = list.size() - 1;
237 i >= 0;
238 --i) {
239 AbstractClient *c = qobject_cast<AbstractClient*>(list.at(i));
240 if (!c) {
241 continue;
242 }
243 if (c->isOnDesktop(desktop) && c->isShown(false) && c->isOnCurrentActivity()) {
244 if (output && c->output() != output)
245 continue;
246 if (!only_normal)
247 return c;
248 if (c->wantsTabFocus() && !c->isSpecialWindow())
249 return c;
250 }
251 }
252 return nullptr;
253 }
254
findDesktop(bool topmost,VirtualDesktop * desktop) const255 AbstractClient *Workspace::findDesktop(bool topmost, VirtualDesktop *desktop) const
256 {
257 // TODO Q_ASSERT( block_stacking_updates == 0 );
258 if (topmost) {
259 for (int i = stacking_order.size() - 1; i >= 0; i--) {
260 AbstractClient *c = qobject_cast<AbstractClient*>(stacking_order.at(i));
261 if (c && c->isOnDesktop(desktop) && c->isDesktop()
262 && c->isShown(true))
263 return c;
264 }
265 } else { // bottom-most
266 Q_FOREACH (Toplevel * c, stacking_order) {
267 AbstractClient *client = qobject_cast<AbstractClient*>(c);
268 if (client && c->isOnDesktop(desktop) && c->isDesktop()
269 && client->isShown(true))
270 return client;
271 }
272 }
273 return nullptr;
274 }
275
raiseOrLowerClient(AbstractClient * c)276 void Workspace::raiseOrLowerClient(AbstractClient *c)
277 {
278 if (!c || !c->isOnCurrentDesktop()) {
279 return;
280 }
281
282 const AbstractClient *topmost =
283 topClientOnDesktop(VirtualDesktopManager::self()->currentDesktop(),
284 options->isSeparateScreenFocus() ? c->output() : nullptr);
285
286 if (c == topmost)
287 lowerClient(c);
288 else
289 raiseClient(c);
290 }
291
292
lowerClient(AbstractClient * c,bool nogroup)293 void Workspace::lowerClient(AbstractClient* c, bool nogroup)
294 {
295 if (!c)
296 return;
297
298 c->cancelAutoRaise();
299
300 StackingUpdatesBlocker blocker(this);
301
302 unconstrained_stacking_order.removeAll(c);
303 unconstrained_stacking_order.prepend(c);
304 if (!nogroup && c->isTransient()) {
305 // lower also all windows in the group, in their reversed stacking order
306 QList<X11Client *> wins;
307 if (auto group = c->group()) {
308 wins = ensureStackingOrder(group->members());
309 }
310 for (int i = wins.size() - 1;
311 i >= 0;
312 --i) {
313 if (wins[ i ] != c)
314 lowerClient(wins[ i ], true);
315 }
316 }
317 }
318
lowerClientWithinApplication(AbstractClient * c)319 void Workspace::lowerClientWithinApplication(AbstractClient* c)
320 {
321 if (!c)
322 return;
323
324 c->cancelAutoRaise();
325
326 StackingUpdatesBlocker blocker(this);
327
328 unconstrained_stacking_order.removeAll(c);
329 bool lowered = false;
330 // first try to put it below the bottom-most window of the application
331 for (auto it = unconstrained_stacking_order.begin();
332 it != unconstrained_stacking_order.end();
333 ++it) {
334 AbstractClient *client = qobject_cast<AbstractClient*>(*it);
335 if (!client) {
336 continue;
337 }
338 if (AbstractClient::belongToSameApplication(client, c)) {
339 unconstrained_stacking_order.insert(it, c);
340 lowered = true;
341 break;
342 }
343 }
344 if (!lowered)
345 unconstrained_stacking_order.prepend(c);
346 // ignore mainwindows
347 }
348
raiseClient(AbstractClient * c,bool nogroup)349 void Workspace::raiseClient(AbstractClient* c, bool nogroup)
350 {
351 if (!c)
352 return;
353
354 c->cancelAutoRaise();
355
356 StackingUpdatesBlocker blocker(this);
357
358 if (!nogroup && c->isTransient()) {
359 QList<AbstractClient*> transients;
360 AbstractClient *transient_parent = c;
361 while ((transient_parent = transient_parent->transientFor()))
362 transients << transient_parent;
363 Q_FOREACH (transient_parent, transients)
364 raiseClient(transient_parent, true);
365 }
366
367 unconstrained_stacking_order.removeAll(c);
368 unconstrained_stacking_order.append(c);
369 }
370
raiseClientWithinApplication(AbstractClient * c)371 void Workspace::raiseClientWithinApplication(AbstractClient* c)
372 {
373 if (!c)
374 return;
375
376 c->cancelAutoRaise();
377
378 StackingUpdatesBlocker blocker(this);
379 // ignore mainwindows
380
381 // first try to put it above the top-most window of the application
382 for (int i = unconstrained_stacking_order.size() - 1; i > -1 ; --i) {
383 AbstractClient *other = qobject_cast<AbstractClient*>(unconstrained_stacking_order.at(i));
384 if (!other) {
385 continue;
386 }
387 if (other == c) // don't lower it just because it asked to be raised
388 return;
389 if (AbstractClient::belongToSameApplication(other, c)) {
390 unconstrained_stacking_order.removeAll(c);
391 unconstrained_stacking_order.insert(unconstrained_stacking_order.indexOf(other) + 1, c); // insert after the found one
392 break;
393 }
394 }
395 }
396
raiseClientRequest(KWin::AbstractClient * c,NET::RequestSource src,xcb_timestamp_t timestamp)397 void Workspace::raiseClientRequest(KWin::AbstractClient *c, NET::RequestSource src, xcb_timestamp_t timestamp)
398 {
399 if (src == NET::FromTool || allowFullClientRaising(c, timestamp))
400 raiseClient(c);
401 else {
402 raiseClientWithinApplication(c);
403 c->demandAttention();
404 }
405 }
406
lowerClientRequest(KWin::X11Client * c,NET::RequestSource src,xcb_timestamp_t)407 void Workspace::lowerClientRequest(KWin::X11Client *c, NET::RequestSource src, xcb_timestamp_t /*timestamp*/)
408 {
409 // If the client has support for all this focus stealing prevention stuff,
410 // do only lowering within the application, as that's the more logical
411 // variant of lowering when application requests it.
412 // No demanding of attention here of course.
413 if (src == NET::FromTool || !c->hasUserTimeSupport())
414 lowerClient(c);
415 else
416 lowerClientWithinApplication(c);
417 }
418
lowerClientRequest(KWin::AbstractClient * c)419 void Workspace::lowerClientRequest(KWin::AbstractClient *c)
420 {
421 lowerClientWithinApplication(c);
422 }
423
restack(AbstractClient * c,AbstractClient * under,bool force)424 void Workspace::restack(AbstractClient* c, AbstractClient* under, bool force)
425 {
426 Q_ASSERT(unconstrained_stacking_order.contains(under));
427 if (!force && !AbstractClient::belongToSameApplication(under, c)) {
428 // put in the stacking order below _all_ windows belonging to the active application
429 for (int i = 0; i < unconstrained_stacking_order.size(); ++i) {
430 AbstractClient *other = qobject_cast<AbstractClient*>(unconstrained_stacking_order.at(i));
431 if (other && other->layer() == c->layer() && AbstractClient::belongToSameApplication(under, other)) {
432 under = (c == other) ? nullptr : other;
433 break;
434 }
435 }
436 }
437 if (under) {
438 unconstrained_stacking_order.removeAll(c);
439 unconstrained_stacking_order.insert(unconstrained_stacking_order.indexOf(under), c);
440 }
441
442 Q_ASSERT(unconstrained_stacking_order.contains(c));
443 FocusChain::self()->moveAfterClient(c, under);
444 updateStackingOrder();
445 }
446
restackClientUnderActive(AbstractClient * c)447 void Workspace::restackClientUnderActive(AbstractClient* c)
448 {
449 if (!active_client || active_client == c || active_client->layer() != c->layer()) {
450 raiseClient(c);
451 return;
452 }
453 restack(c, active_client);
454 }
455
restoreSessionStackingOrder(X11Client * c)456 void Workspace::restoreSessionStackingOrder(X11Client *c)
457 {
458 if (c->sessionStackingOrder() < 0)
459 return;
460 StackingUpdatesBlocker blocker(this);
461 unconstrained_stacking_order.removeAll(c);
462 for (auto it = unconstrained_stacking_order.begin(); // from bottom
463 it != unconstrained_stacking_order.end();
464 ++it) {
465 X11Client *current = qobject_cast<X11Client *>(*it);
466 if (!current) {
467 continue;
468 }
469 if (current->sessionStackingOrder() > c->sessionStackingOrder()) {
470 unconstrained_stacking_order.insert(it, c);
471 return;
472 }
473 }
474 unconstrained_stacking_order.append(c);
475 }
476
layerForClient(const X11Client * client)477 static Layer layerForClient(const X11Client *client)
478 {
479 Layer layer = client->layer();
480
481 // Desktop windows cannot be promoted to upper layers.
482 if (layer == DesktopLayer) {
483 return layer;
484 }
485
486 if (const Group *group = client->group()) {
487 const auto members = group->members();
488 for (const X11Client *member : members) {
489 if (member == client) {
490 continue;
491 } else if (member->output() != client->output()) {
492 continue;
493 }
494 if (member->layer() == ActiveLayer) {
495 return ActiveLayer;
496 }
497 }
498 }
499
500 return layer;
501 }
502
computeLayer(const Toplevel * toplevel)503 static Layer computeLayer(const Toplevel *toplevel)
504 {
505 if (auto client = qobject_cast<const X11Client *>(toplevel)) {
506 return layerForClient(client);
507 } else {
508 return toplevel->layer();
509 }
510 }
511
512 /**
513 * Returns a stacking order based upon \a list that fulfills certain contained.
514 */
constrainedStackingOrder()515 QList<Toplevel *> Workspace::constrainedStackingOrder()
516 {
517 // Sort the windows based on their layers while preserving their relative order in the
518 // unconstrained stacking order.
519 std::array<QList<Toplevel *>, NumLayers> windows;
520 for (Toplevel *window : qAsConst(unconstrained_stacking_order)) {
521 const Layer layer = computeLayer(window);
522 windows[layer] << window;
523 }
524
525 QList<Toplevel *> stacking;
526 stacking.reserve(unconstrained_stacking_order.count());
527 for (uint layer = FirstLayer; layer < NumLayers; ++layer) {
528 stacking += windows[layer];
529 }
530
531 // Apply the stacking order constraints. First, we enqueue the root constraints, i.e.
532 // the ones that are not affected by other constraints.
533 QQueue<Constraint *> constraints;
534 constraints.reserve(m_constraints.count());
535 for (Constraint *constraint : qAsConst(m_constraints)) {
536 if (constraint->parents.isEmpty()) {
537 constraint->enqueued = true;
538 constraints.enqueue(constraint);
539 } else {
540 constraint->enqueued = false;
541 }
542 }
543
544 // Once we've enqueued all the root constraints, we traverse the constraints tree in
545 // the breadth-first search fashion. A constraint is applied only if its condition is
546 // not met.
547 while (!constraints.isEmpty()) {
548 Constraint *constraint = constraints.dequeue();
549
550 const int belowIndex = stacking.indexOf(constraint->below);
551 const int aboveIndex = stacking.indexOf(constraint->above);
552 if (belowIndex == -1 || aboveIndex == -1) {
553 continue;
554 } else if (aboveIndex < belowIndex) {
555 stacking.removeAt(aboveIndex);
556 stacking.insert(belowIndex, constraint->above);
557 }
558
559 for (Constraint *child : qAsConst(constraint->children)) {
560 if (!child->enqueued) {
561 child->enqueued = true;
562 constraints.enqueue(child);
563 }
564 }
565 }
566
567 return stacking;
568 }
569
blockStackingUpdates(bool block)570 void Workspace::blockStackingUpdates(bool block)
571 {
572 if (block) {
573 if (block_stacking_updates == 0)
574 blocked_propagating_new_clients = false;
575 ++block_stacking_updates;
576 } else // !block
577 if (--block_stacking_updates == 0) {
578 updateStackingOrder(blocked_propagating_new_clients);
579 if (effects)
580 static_cast<EffectsHandlerImpl*>(effects)->checkInputWindowStacking();
581 }
582 }
583
584 namespace {
585 template <class T>
ensureStackingOrderInList(const QList<Toplevel * > & stackingOrder,const QList<T * > & list)586 QList<T*> ensureStackingOrderInList(const QList<Toplevel *> &stackingOrder, const QList<T*> &list)
587 {
588 static_assert(std::is_base_of<Toplevel, T>::value,
589 "U must be derived from T");
590 // TODO Q_ASSERT( block_stacking_updates == 0 );
591 if (list.count() < 2)
592 return list;
593 // TODO is this worth optimizing?
594 QList<T*> result = list;
595 for (auto it = stackingOrder.begin();
596 it != stackingOrder.end();
597 ++it) {
598 T *c = qobject_cast<T*>(*it);
599 if (!c) {
600 continue;
601 }
602 if (result.removeAll(c) != 0)
603 result.append(c);
604 }
605 return result;
606 }
607 }
608
609 // Ensure list is in stacking order
ensureStackingOrder(const QList<X11Client * > & list) const610 QList<X11Client *> Workspace::ensureStackingOrder(const QList<X11Client *> &list) const
611 {
612 return ensureStackingOrderInList(stacking_order, list);
613 }
614
ensureStackingOrder(const QList<AbstractClient * > & list) const615 QList<AbstractClient*> Workspace::ensureStackingOrder(const QList<AbstractClient*> &list) const
616 {
617 return ensureStackingOrderInList(stacking_order, list);
618 }
619
620 // Returns all windows in their stacking order on the root window.
xStackingOrder() const621 QList<Toplevel *> Workspace::xStackingOrder() const
622 {
623 if (m_xStackingDirty) {
624 const_cast<Workspace*>(this)->updateXStackingOrder();
625 }
626 return x_stacking;
627 }
628
updateXStackingOrder()629 void Workspace::updateXStackingOrder()
630 {
631 // use our own stacking order, not the X one, as they may differ
632 x_stacking = stacking_order;
633
634 if (m_xStackingQueryTree && !m_xStackingQueryTree->isNull()) {
635 std::unique_ptr<Xcb::Tree> tree{std::move(m_xStackingQueryTree)};
636 xcb_window_t *windows = tree->children();
637 const auto count = tree->data()->children_len;
638 int foundUnmanagedCount = m_unmanaged.count();
639 for (unsigned int i = 0;
640 i < count;
641 ++i) {
642 for (auto it = m_unmanaged.constBegin(); it != m_unmanaged.constEnd(); ++it) {
643 Unmanaged *u = *it;
644 if (u->window() == windows[i]) {
645 x_stacking.append(u);
646 foundUnmanagedCount--;
647 break;
648 }
649 }
650 if (foundUnmanagedCount == 0) {
651 break;
652 }
653 }
654 }
655
656 m_xStackingDirty = false;
657 }
658
659 //*******************************
660 // Client
661 //*******************************
662
restackWindow(xcb_window_t above,int detail,NET::RequestSource src,xcb_timestamp_t timestamp,bool send_event)663 void X11Client::restackWindow(xcb_window_t above, int detail, NET::RequestSource src, xcb_timestamp_t timestamp, bool send_event)
664 {
665 X11Client *other = nullptr;
666 if (detail == XCB_STACK_MODE_OPPOSITE) {
667 other = workspace()->findClient(Predicate::WindowMatch, above);
668 if (!other) {
669 workspace()->raiseOrLowerClient(this);
670 return;
671 }
672 auto it = workspace()->stackingOrder().constBegin(),
673 end = workspace()->stackingOrder().constEnd();
674 while (it != end) {
675 if (*it == this) {
676 detail = XCB_STACK_MODE_ABOVE;
677 break;
678 } else if (*it == other) {
679 detail = XCB_STACK_MODE_BELOW;
680 break;
681 }
682 ++it;
683 }
684 }
685 else if (detail == XCB_STACK_MODE_TOP_IF) {
686 other = workspace()->findClient(Predicate::WindowMatch, above);
687 if (other && other->frameGeometry().intersects(frameGeometry()))
688 workspace()->raiseClientRequest(this, src, timestamp);
689 return;
690 }
691 else if (detail == XCB_STACK_MODE_BOTTOM_IF) {
692 other = workspace()->findClient(Predicate::WindowMatch, above);
693 if (other && other->frameGeometry().intersects(frameGeometry()))
694 workspace()->lowerClientRequest(this, src, timestamp);
695 return;
696 }
697
698 if (!other)
699 other = workspace()->findClient(Predicate::WindowMatch, above);
700
701 if (other && detail == XCB_STACK_MODE_ABOVE) {
702 auto it = workspace()->stackingOrder().constEnd(),
703 begin = workspace()->stackingOrder().constBegin();
704 while (--it != begin) {
705
706 if (*it == other) { // the other one is top on stack
707 it = begin; // invalidate
708 src = NET::FromTool; // force
709 break;
710 }
711 X11Client *c = qobject_cast<X11Client *>(*it);
712
713 if (!c || !( (*it)->isNormalWindow() && c->isShown(true) &&
714 (*it)->isOnCurrentDesktop() && (*it)->isOnCurrentActivity() && (*it)->isOnOutput(output()) ))
715 continue; // irrelevant clients
716
717 if (*(it - 1) == other)
718 break; // "it" is the one above the target one, stack below "it"
719 }
720
721 if (it != begin && (*(it - 1) == other))
722 other = qobject_cast<X11Client *>(*it);
723 else
724 other = nullptr;
725 }
726
727 if (other)
728 workspace()->restack(this, other);
729 else if (detail == XCB_STACK_MODE_BELOW)
730 workspace()->lowerClientRequest(this, src, timestamp);
731 else if (detail == XCB_STACK_MODE_ABOVE)
732 workspace()->raiseClientRequest(this, src, timestamp);
733
734 if (send_event)
735 sendSyntheticConfigureNotify();
736 }
737
belongsToDesktop() const738 bool X11Client::belongsToDesktop() const
739 {
740 Q_FOREACH (const X11Client *c, group()->members()) {
741 if (c->isDesktop())
742 return true;
743 }
744 return false;
745 }
746
747 } // namespace
748