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