File indexing completed on 2024-04-28 16:48:53

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 1999, 2000 Matthias Ettrich <ettrich@kde.org>
0006     SPDX-FileCopyrightText: 2003 Lubos Lunak <l.lunak@kde.org>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 // SELI zmenit doc
0012 
0013 /*
0014 
0015  This file contains things relevant to stacking order and layers.
0016 
0017  Design:
0018 
0019  Normal unconstrained stacking order, as requested by the user (by clicking
0020  on windows to raise them, etc.), is in Workspace::unconstrained_stacking_order.
0021  That list shouldn't be used at all, except for building
0022  Workspace::stacking_order. The building is done
0023  in Workspace::constrainedStackingOrder(). Only Workspace::stackingOrder() should
0024  be used to get the stacking order, because it also checks the stacking order
0025  is up to date.
0026  All clients are also stored in Workspace::clients (except for isDesktop() clients,
0027  as those are very special, and are stored in Workspace::desktops), in the order
0028  the clients were created.
0029 
0030  Every window has one layer assigned in which it is. There are 7 layers,
0031  from bottom : DesktopLayer, BelowLayer, NormalLayer, DockLayer, AboveLayer, NotificationLayer,
0032  ActiveLayer, CriticalNotificationLayer, and OnScreenDisplayLayer (see also NETWM sect.7.10.).
0033  The layer a window is in depends on the window type, and on other things like whether the window
0034  is active. We extend the layers provided in NETWM by the NotificationLayer, OnScreenDisplayLayer,
0035  and CriticalNotificationLayer.
0036  The NoficationLayer contains notification windows which are kept above all windows except the active
0037  fullscreen window. The CriticalNotificationLayer contains notification windows which are important
0038  enough to keep them even above fullscreen windows. The OnScreenDisplayLayer is used for eg. volume
0039  and brightness change feedback and is kept above all windows since it provides immediate response
0040  to a user action.
0041 
0042  NET::Splash clients belong to the Normal layer. NET::TopMenu clients
0043  belong to Dock layer. Clients that are both NET::Dock and NET::KeepBelow
0044  are in the Normal layer in order to keep the 'allow window to cover
0045  the panel' Kicker setting to work as intended (this may look like a slight
0046  spec violation, but a) I have no better idea, b) the spec allows adjusting
0047  the stacking order if the WM thinks it's a good idea . We put all
0048  NET::KeepAbove above all Docks too, even though the spec suggests putting
0049  them in the same layer.
0050 
0051  Most transients are in the same layer as their mainwindow,
0052  see Workspace::constrainedStackingOrder(), they may also be in higher layers, but
0053  they should never be below their mainwindow.
0054 
0055  Currently the things that affect client in which layer a client
0056  belongs: KeepAbove/Keep Below flags, window type, fullscreen
0057  state and whether the client is active, mainclient (transiency).
0058 
0059  Make sure updateStackingOrder() is called in order to make
0060  Workspace::stackingOrder() up to date and propagated to the world.
0061  Using Workspace::blockStackingUpdates() (or the StackingUpdatesBlocker
0062  helper class) it's possible to temporarily disable updates
0063  and the stacking order will be updated once after it's allowed again.
0064 
0065 */
0066 
0067 #include "composite.h"
0068 #include "deleted.h"
0069 #include "effects.h"
0070 #include "focuschain.h"
0071 #include "group.h"
0072 #include "internalwindow.h"
0073 #include "netinfo.h"
0074 #include "rules.h"
0075 #include "screenedge.h"
0076 #include "tabbox.h"
0077 #include "unmanaged.h"
0078 #include "utils/common.h"
0079 #include "virtualdesktops.h"
0080 #include "wayland_server.h"
0081 #include "workspace.h"
0082 #include "x11window.h"
0083 
0084 #include <array>
0085 
0086 #include <QDebug>
0087 
0088 namespace KWin
0089 {
0090 
0091 //*******************************
0092 // Workspace
0093 //*******************************
0094 
0095 void Workspace::updateStackingOrder(bool propagate_new_windows)
0096 {
0097     if (m_blockStackingUpdates > 0) {
0098         if (propagate_new_windows) {
0099             m_blockedPropagatingNewWindows = true;
0100         }
0101         return;
0102     }
0103     QList<Window *> new_stacking_order = constrainedStackingOrder();
0104     bool changed = (force_restacking || new_stacking_order != stacking_order);
0105     force_restacking = false;
0106     stacking_order = new_stacking_order;
0107     if (changed || propagate_new_windows) {
0108         propagateWindows(propagate_new_windows);
0109 
0110         for (int i = 0; i < stacking_order.size(); ++i) {
0111             stacking_order[i]->setStackingOrder(i);
0112         }
0113 
0114         Q_EMIT stackingOrderChanged();
0115 
0116         if (m_activeWindow) {
0117             m_activeWindow->updateMouseGrab();
0118         }
0119     }
0120 }
0121 
0122 /**
0123  * Some fullscreen effects have to raise the screenedge on top of an input window, thus all windows
0124  * this function puts them back where they belong for regular use and is some cheap variant of
0125  * the regular propagateWindows function in that it completely ignores managed windows and everything
0126  * else and also does not update the NETWM property.
0127  * Called from Effects::destroyInputWindow so far.
0128  */
0129 void Workspace::stackScreenEdgesUnderOverrideRedirect()
0130 {
0131     if (!rootInfo()) {
0132         return;
0133     }
0134     Xcb::restackWindows(QVector<xcb_window_t>() << rootInfo()->supportWindow() << workspace()->screenEdges()->windows());
0135 }
0136 
0137 /**
0138  * Propagates the managed windows to the world.
0139  * Called ONLY from updateStackingOrder().
0140  */
0141 void Workspace::propagateWindows(bool propagate_new_windows)
0142 {
0143     if (!rootInfo()) {
0144         return;
0145     }
0146     // restack the windows according to the stacking order
0147     // supportWindow > electric borders > windows > hidden windows
0148     QVector<xcb_window_t> newWindowStack;
0149 
0150     // Stack all windows under the support window. The support window is
0151     // not used for anything (besides the NETWM property), and it's not shown,
0152     // but it was lowered after kwin startup. Stacking all windows below
0153     // it ensures that no window will be ever shown above override-redirect
0154     // windows (e.g. popups).
0155     newWindowStack << rootInfo()->supportWindow();
0156 
0157     newWindowStack << workspace()->screenEdges()->windows();
0158 
0159     newWindowStack << manual_overlays;
0160 
0161     newWindowStack.reserve(newWindowStack.size() + 2 * stacking_order.size()); // *2 for inputWindow
0162 
0163     for (int i = stacking_order.size() - 1; i >= 0; --i) {
0164         X11Window *window = qobject_cast<X11Window *>(stacking_order.at(i));
0165         if (!window || window->hiddenPreview()) {
0166             continue;
0167         }
0168 
0169         if (window->inputId()) {
0170             // Stack the input window above the frame
0171             newWindowStack << window->inputId();
0172         }
0173 
0174         newWindowStack << window->frameId();
0175     }
0176 
0177     // when having hidden previews, stack hidden windows below everything else
0178     // (as far as pure X stacking order is concerned), in order to avoid having
0179     // these windows that should be unmapped to interfere with other windows
0180     for (int i = stacking_order.size() - 1; i >= 0; --i) {
0181         X11Window *window = qobject_cast<X11Window *>(stacking_order.at(i));
0182         if (!window || !window->hiddenPreview()) {
0183             continue;
0184         }
0185         newWindowStack << window->frameId();
0186     }
0187     // TODO isn't it too inefficient to restack always all windows?
0188     // TODO don't restack not visible windows?
0189     Q_ASSERT(newWindowStack.at(0) == rootInfo()->supportWindow());
0190     Xcb::restackWindows(newWindowStack);
0191 
0192     QVector<xcb_window_t> cl;
0193     if (propagate_new_windows) {
0194         cl.reserve(manual_overlays.size() + m_x11Clients.size());
0195         for (const auto win : std::as_const(manual_overlays)) {
0196             cl.push_back(win);
0197         }
0198         for (auto it = m_x11Clients.constBegin(); it != m_x11Clients.constEnd(); ++it) {
0199             cl.push_back((*it)->window());
0200         }
0201         rootInfo()->setClientList(cl.constData(), cl.size());
0202     }
0203 
0204     cl.clear();
0205     for (auto it = stacking_order.constBegin(); it != stacking_order.constEnd(); ++it) {
0206         X11Window *window = qobject_cast<X11Window *>(*it);
0207         if (window) {
0208             cl.push_back(window->window());
0209         }
0210     }
0211     for (const auto win : std::as_const(manual_overlays)) {
0212         cl.push_back(win);
0213     }
0214     rootInfo()->setClientListStacking(cl.constData(), cl.size());
0215 }
0216 
0217 /**
0218  * Returns topmost visible window. Windows on the dock, the desktop
0219  * or of any other special kind are excluded. Also if the window
0220  * doesn't accept focus it's excluded.
0221  */
0222 // TODO misleading name for this method, too many slightly different ways to use it
0223 Window *Workspace::topWindowOnDesktop(VirtualDesktop *desktop, Output *output, bool unconstrained, bool only_normal) const
0224 {
0225     // TODO    Q_ASSERT( block_stacking_updates == 0 );
0226     QList<Window *> list;
0227     if (!unconstrained) {
0228         list = stacking_order;
0229     } else {
0230         list = unconstrained_stacking_order;
0231     }
0232     for (int i = list.size() - 1; i >= 0; --i) {
0233         auto window = list.at(i);
0234         if (!window->isClient()) {
0235             continue;
0236         }
0237         if (window->isOnDesktop(desktop) && window->isShown() && window->isOnCurrentActivity() && !window->isShade()) {
0238             if (output && window->output() != output) {
0239                 continue;
0240             }
0241             if (!only_normal) {
0242                 return window;
0243             }
0244             if (window->wantsTabFocus() && !window->isSpecialWindow()) {
0245                 return window;
0246             }
0247         }
0248     }
0249     return nullptr;
0250 }
0251 
0252 Window *Workspace::findDesktop(bool topmost, VirtualDesktop *desktop) const
0253 {
0254     // TODO    Q_ASSERT( block_stacking_updates == 0 );
0255     if (topmost) {
0256         for (int i = stacking_order.size() - 1; i >= 0; i--) {
0257             auto window = stacking_order.at(i);
0258             if (window->isClient() && window->isOnDesktop(desktop) && window->isDesktop() && window->isShown()) {
0259                 return window;
0260             }
0261         }
0262     } else { // bottom-most
0263         for (Window *window : std::as_const(stacking_order)) {
0264             if (window->isClient() && window->isOnDesktop(desktop) && window->isDesktop() && window->isShown()) {
0265                 return window;
0266             }
0267         }
0268     }
0269     return nullptr;
0270 }
0271 
0272 void Workspace::raiseOrLowerWindow(Window *window)
0273 {
0274     if (!window || !window->isOnCurrentDesktop()) {
0275         return;
0276     }
0277 
0278     const Window *topmost =
0279         topWindowOnDesktop(VirtualDesktopManager::self()->currentDesktop(),
0280                            options->isSeparateScreenFocus() ? window->output() : nullptr);
0281 
0282     if (window == topmost) {
0283         lowerWindow(window);
0284     } else {
0285         raiseWindow(window);
0286     }
0287 }
0288 
0289 void Workspace::lowerWindow(Window *window, bool nogroup)
0290 {
0291     if (!window) {
0292         return;
0293     }
0294     if (window->isDeleted()) {
0295         qCWarning(KWIN_CORE) << "Workspace::lowerWindow: closed window" << window << "cannot be restacked";
0296         return;
0297     }
0298 
0299     window->cancelAutoRaise();
0300 
0301     StackingUpdatesBlocker blocker(this);
0302 
0303     unconstrained_stacking_order.removeAll(window);
0304     unconstrained_stacking_order.prepend(window);
0305     if (!nogroup && window->isTransient()) {
0306         // lower also all windows in the group, in their reversed stacking order
0307         QList<X11Window *> wins;
0308         if (auto group = window->group()) {
0309             wins = ensureStackingOrder(group->members());
0310         }
0311         for (int i = wins.size() - 1; i >= 0; --i) {
0312             if (wins[i] != window) {
0313                 lowerWindow(wins[i], true);
0314             }
0315         }
0316     }
0317 }
0318 
0319 void Workspace::lowerWindowWithinApplication(Window *window)
0320 {
0321     if (!window) {
0322         return;
0323     }
0324     if (window->isDeleted()) {
0325         qCWarning(KWIN_CORE) << "Workspace::lowerWindowWithinApplication: closed window" << window << "cannot be restacked";
0326         return;
0327     }
0328 
0329     window->cancelAutoRaise();
0330 
0331     StackingUpdatesBlocker blocker(this);
0332 
0333     unconstrained_stacking_order.removeAll(window);
0334     bool lowered = false;
0335     // first try to put it below the bottom-most window of the application
0336     for (auto it = unconstrained_stacking_order.begin(); it != unconstrained_stacking_order.end(); ++it) {
0337         auto other = *it;
0338         if (!other->isClient()) {
0339             continue;
0340         }
0341         if (Window::belongToSameApplication(other, window)) {
0342             unconstrained_stacking_order.insert(it, window);
0343             lowered = true;
0344             break;
0345         }
0346     }
0347     if (!lowered) {
0348         unconstrained_stacking_order.prepend(window);
0349     }
0350     // ignore mainwindows
0351 }
0352 
0353 void Workspace::raiseWindow(Window *window, bool nogroup)
0354 {
0355     if (!window) {
0356         return;
0357     }
0358     if (window->isDeleted()) {
0359         qCWarning(KWIN_CORE) << "Workspace::raiseWindow: closed window" << window << "cannot be restacked";
0360         return;
0361     }
0362 
0363     window->cancelAutoRaise();
0364 
0365     StackingUpdatesBlocker blocker(this);
0366 
0367     if (!nogroup && window->isTransient()) {
0368         QList<Window *> transients;
0369         Window *transient_parent = window;
0370         while ((transient_parent = transient_parent->transientFor())) {
0371             transients << transient_parent;
0372         }
0373         for (const auto &transient_parent : std::as_const(transients)) {
0374             raiseWindow(transient_parent, true);
0375         }
0376     }
0377 
0378     unconstrained_stacking_order.removeAll(window);
0379     unconstrained_stacking_order.append(window);
0380 }
0381 
0382 void Workspace::raiseWindowWithinApplication(Window *window)
0383 {
0384     if (!window) {
0385         return;
0386     }
0387     if (window->isDeleted()) {
0388         qCWarning(KWIN_CORE) << "Workspace::raiseWindowWithinApplication: closed window" << window << "cannot be restacked";
0389         return;
0390     }
0391 
0392     window->cancelAutoRaise();
0393 
0394     StackingUpdatesBlocker blocker(this);
0395     // ignore mainwindows
0396 
0397     // first try to put it above the top-most window of the application
0398     for (int i = unconstrained_stacking_order.size() - 1; i > -1; --i) {
0399         auto other = unconstrained_stacking_order.at(i);
0400         if (!other->isClient()) {
0401             continue;
0402         }
0403         if (other == window) { // don't lower it just because it asked to be raised
0404             return;
0405         }
0406         if (Window::belongToSameApplication(other, window)) {
0407             unconstrained_stacking_order.removeAll(window);
0408             unconstrained_stacking_order.insert(unconstrained_stacking_order.indexOf(other) + 1, window); // insert after the found one
0409             break;
0410         }
0411     }
0412 }
0413 
0414 void Workspace::raiseWindowRequest(Window *window, NET::RequestSource src, xcb_timestamp_t timestamp)
0415 {
0416     if (src == NET::FromTool || allowFullClientRaising(window, timestamp)) {
0417         raiseWindow(window);
0418     } else {
0419         raiseWindowWithinApplication(window);
0420         window->demandAttention();
0421     }
0422 }
0423 
0424 void Workspace::lowerWindowRequest(X11Window *window, NET::RequestSource src, xcb_timestamp_t /*timestamp*/)
0425 {
0426     // If the window has support for all this focus stealing prevention stuff,
0427     // do only lowering within the application, as that's the more logical
0428     // variant of lowering when application requests it.
0429     // No demanding of attention here of course.
0430     if (src == NET::FromTool || !window->hasUserTimeSupport()) {
0431         lowerWindow(window);
0432     } else {
0433         lowerWindowWithinApplication(window);
0434     }
0435 }
0436 
0437 void Workspace::lowerWindowRequest(Window *window)
0438 {
0439     lowerWindowWithinApplication(window);
0440 }
0441 
0442 void Workspace::restack(Window *window, Window *under, bool force)
0443 {
0444     if (window->isDeleted()) {
0445         qCWarning(KWIN_CORE) << "Workspace::restack: closed window" << window << "cannot be restacked";
0446         return;
0447     }
0448     Q_ASSERT(unconstrained_stacking_order.contains(under));
0449     if (!force && !Window::belongToSameApplication(under, window)) {
0450         // put in the stacking order below _all_ windows belonging to the active application
0451         for (int i = 0; i < unconstrained_stacking_order.size(); ++i) {
0452             auto other = unconstrained_stacking_order.at(i);
0453             if (other->isClient() && other->layer() == window->layer() && Window::belongToSameApplication(under, other)) {
0454                 under = (window == other) ? nullptr : other;
0455                 break;
0456             }
0457         }
0458     }
0459     if (under) {
0460         unconstrained_stacking_order.removeAll(window);
0461         unconstrained_stacking_order.insert(unconstrained_stacking_order.indexOf(under), window);
0462     }
0463 
0464     Q_ASSERT(unconstrained_stacking_order.contains(window));
0465     m_focusChain->moveAfterWindow(window, under);
0466     updateStackingOrder();
0467 }
0468 
0469 void Workspace::restackWindowUnderActive(Window *window)
0470 {
0471     if (!m_activeWindow || m_activeWindow == window || m_activeWindow->layer() != window->layer()) {
0472         raiseWindow(window);
0473         return;
0474     }
0475     restack(window, m_activeWindow);
0476 }
0477 
0478 void Workspace::restoreSessionStackingOrder(X11Window *window)
0479 {
0480     if (window->sessionStackingOrder() < 0) {
0481         return;
0482     }
0483     StackingUpdatesBlocker blocker(this);
0484     unconstrained_stacking_order.removeAll(window);
0485     for (auto it = unconstrained_stacking_order.begin(); it != unconstrained_stacking_order.end(); ++it) {
0486         X11Window *current = qobject_cast<X11Window *>(*it);
0487         if (!current) {
0488             continue;
0489         }
0490         if (current->sessionStackingOrder() > window->sessionStackingOrder()) {
0491             unconstrained_stacking_order.insert(it, window);
0492             return;
0493         }
0494     }
0495     unconstrained_stacking_order.append(window);
0496 }
0497 
0498 static Layer layerForWindow(const X11Window *window)
0499 {
0500     Layer layer = window->layer();
0501 
0502     // Desktop windows cannot be promoted to upper layers.
0503     if (layer == DesktopLayer) {
0504         return layer;
0505     }
0506 
0507     if (const Group *group = window->group()) {
0508         const auto members = group->members();
0509         for (const X11Window *member : members) {
0510             if (member == window) {
0511                 continue;
0512             } else if (member->output() != window->output()) {
0513                 continue;
0514             }
0515             if (member->layer() == ActiveLayer) {
0516                 return ActiveLayer;
0517             }
0518         }
0519     }
0520 
0521     return layer;
0522 }
0523 
0524 static Layer computeLayer(const Window *window)
0525 {
0526     if (auto x11Window = qobject_cast<const X11Window *>(window)) {
0527         return layerForWindow(x11Window);
0528     } else {
0529         return window->layer();
0530     }
0531 }
0532 
0533 /**
0534  * Returns a stacking order based upon \a list that fulfills certain contained.
0535  */
0536 QList<Window *> Workspace::constrainedStackingOrder()
0537 {
0538     // Sort the windows based on their layers while preserving their relative order in the
0539     // unconstrained stacking order.
0540     std::array<QList<Window *>, NumLayers> windows;
0541     for (Window *window : std::as_const(unconstrained_stacking_order)) {
0542         const Layer layer = computeLayer(window);
0543         windows[layer] << window;
0544     }
0545 
0546     QList<Window *> stacking;
0547     stacking.reserve(unconstrained_stacking_order.count());
0548     for (uint layer = FirstLayer; layer < NumLayers; ++layer) {
0549         stacking += windows[layer];
0550     }
0551 
0552     // Apply the stacking order constraints. First, we enqueue the root constraints, i.e.
0553     // the ones that are not affected by other constraints.
0554     QList<Constraint *> constraints;
0555     constraints.reserve(m_constraints.count());
0556     for (Constraint *constraint : std::as_const(m_constraints)) {
0557         if (constraint->parents.isEmpty()) {
0558             constraint->enqueued = true;
0559             constraints.append(constraint);
0560         } else {
0561             constraint->enqueued = false;
0562         }
0563     }
0564 
0565     // Preserve the relative order of transient siblings in the unconstrained stacking order.
0566     auto constraintComparator = [&stacking](Constraint *a, Constraint *b) {
0567         return stacking.indexOf(a->above) > stacking.indexOf(b->above);
0568     };
0569     std::sort(constraints.begin(), constraints.end(), constraintComparator);
0570 
0571     // Once we've enqueued all the root constraints, we traverse the constraints tree in
0572     // the reverse breadth-first search fashion. A constraint is applied only if its condition is
0573     // not met.
0574     while (!constraints.isEmpty()) {
0575         Constraint *constraint = constraints.takeFirst();
0576 
0577         const int belowIndex = stacking.indexOf(constraint->below);
0578         const int aboveIndex = stacking.indexOf(constraint->above);
0579         if (belowIndex == -1 || aboveIndex == -1) {
0580             continue;
0581         } else if (aboveIndex < belowIndex) {
0582             stacking.removeAt(aboveIndex);
0583             stacking.insert(belowIndex, constraint->above);
0584         }
0585 
0586         // Preserve the relative order of transient siblings in the unconstrained stacking order.
0587         QList<Constraint *> children = constraint->children;
0588         std::sort(children.begin(), children.end(), constraintComparator);
0589 
0590         for (Constraint *child : std::as_const(children)) {
0591             if (!child->enqueued) {
0592                 child->enqueued = true;
0593                 constraints.append(child);
0594             }
0595         }
0596     }
0597 
0598     return stacking;
0599 }
0600 
0601 void Workspace::blockStackingUpdates(bool block)
0602 {
0603     if (block) {
0604         if (m_blockStackingUpdates == 0) {
0605             m_blockedPropagatingNewWindows = false;
0606         }
0607         ++m_blockStackingUpdates;
0608     } else // !block
0609         if (--m_blockStackingUpdates == 0) {
0610             updateStackingOrder(m_blockedPropagatingNewWindows);
0611             if (effects) {
0612                 static_cast<EffectsHandlerImpl *>(effects)->checkInputWindowStacking();
0613             }
0614         }
0615 }
0616 
0617 namespace
0618 {
0619 template<class T>
0620 QList<T *> ensureStackingOrderInList(const QList<Window *> &stackingOrder, const QList<T *> &list)
0621 {
0622     static_assert(std::is_base_of<Window, T>::value,
0623                   "U must be derived from T");
0624     // TODO    Q_ASSERT( block_stacking_updates == 0 );
0625     if (list.count() < 2) {
0626         return list;
0627     }
0628     // TODO is this worth optimizing?
0629     QList<T *> result = list;
0630     for (auto it = stackingOrder.begin(); it != stackingOrder.end(); ++it) {
0631         T *window = qobject_cast<T *>(*it);
0632         if (!window) {
0633             continue;
0634         }
0635         if (result.removeAll(window) != 0) {
0636             result.append(window);
0637         }
0638     }
0639     return result;
0640 }
0641 }
0642 
0643 // Ensure list is in stacking order
0644 QList<X11Window *> Workspace::ensureStackingOrder(const QList<X11Window *> &list) const
0645 {
0646     return ensureStackingOrderInList(stacking_order, list);
0647 }
0648 
0649 QList<Window *> Workspace::ensureStackingOrder(const QList<Window *> &list) const
0650 {
0651     return ensureStackingOrderInList(stacking_order, list);
0652 }
0653 
0654 QList<Window *> Workspace::unconstrainedStackingOrder() const
0655 {
0656     return unconstrained_stacking_order;
0657 }
0658 
0659 void Workspace::updateXStackingOrder()
0660 {
0661     // we use our stacking order for managed windows, but X's for override-redirect windows
0662     Xcb::Tree tree(kwinApp()->x11RootWindow());
0663     xcb_window_t *windows = tree.children();
0664 
0665     const auto count = tree.data()->children_len;
0666     int remainingCount = m_unmanaged.count();
0667     for (unsigned int i = 0; i < count; ++i) {
0668         auto window = findUnmanaged(windows[i]);
0669         if (window) {
0670             unconstrained_stacking_order.removeAll(window);
0671             unconstrained_stacking_order.append(window);
0672             remainingCount--;
0673         }
0674         if (remainingCount == 0) {
0675             break;
0676         }
0677     }
0678 
0679     if (!m_unmanaged.isEmpty()) {
0680         updateStackingOrder();
0681     }
0682 }
0683 
0684 //*******************************
0685 // Client
0686 //*******************************
0687 
0688 void X11Window::restackWindow(xcb_window_t above, int detail, NET::RequestSource src, xcb_timestamp_t timestamp, bool send_event)
0689 {
0690     X11Window *other = nullptr;
0691     if (detail == XCB_STACK_MODE_OPPOSITE) {
0692         other = workspace()->findClient(Predicate::WindowMatch, above);
0693         if (!other) {
0694             workspace()->raiseOrLowerWindow(this);
0695             return;
0696         }
0697         auto it = workspace()->stackingOrder().constBegin(),
0698              end = workspace()->stackingOrder().constEnd();
0699         while (it != end) {
0700             if (*it == this) {
0701                 detail = XCB_STACK_MODE_ABOVE;
0702                 break;
0703             } else if (*it == other) {
0704                 detail = XCB_STACK_MODE_BELOW;
0705                 break;
0706             }
0707             ++it;
0708         }
0709     } else if (detail == XCB_STACK_MODE_TOP_IF) {
0710         other = workspace()->findClient(Predicate::WindowMatch, above);
0711         if (other && other->frameGeometry().intersects(frameGeometry())) {
0712             workspace()->raiseWindowRequest(this, src, timestamp);
0713         }
0714         return;
0715     } else if (detail == XCB_STACK_MODE_BOTTOM_IF) {
0716         other = workspace()->findClient(Predicate::WindowMatch, above);
0717         if (other && other->frameGeometry().intersects(frameGeometry())) {
0718             workspace()->lowerWindowRequest(this, src, timestamp);
0719         }
0720         return;
0721     }
0722 
0723     if (!other) {
0724         other = workspace()->findClient(Predicate::WindowMatch, above);
0725     }
0726 
0727     if (other && detail == XCB_STACK_MODE_ABOVE) {
0728         auto it = workspace()->stackingOrder().constEnd(),
0729              begin = workspace()->stackingOrder().constBegin();
0730         while (--it != begin) {
0731 
0732             if (*it == other) { // the other one is top on stack
0733                 it = begin; // invalidate
0734                 src = NET::FromTool; // force
0735                 break;
0736             }
0737             X11Window *window = qobject_cast<X11Window *>(*it);
0738 
0739             if (!window || !((*it)->isNormalWindow() && window->isShown() && (*it)->isOnCurrentDesktop() && (*it)->isOnCurrentActivity() && (*it)->isOnOutput(output()))) {
0740                 continue; // irrelevant windows
0741             }
0742 
0743             if (*(it - 1) == other) {
0744                 break; // "it" is the one above the target one, stack below "it"
0745             }
0746         }
0747 
0748         if (it != begin && (*(it - 1) == other)) {
0749             other = qobject_cast<X11Window *>(*it);
0750         } else {
0751             other = nullptr;
0752         }
0753     }
0754 
0755     if (other) {
0756         workspace()->restack(this, other);
0757     } else if (detail == XCB_STACK_MODE_BELOW) {
0758         workspace()->lowerWindowRequest(this, src, timestamp);
0759     } else if (detail == XCB_STACK_MODE_ABOVE) {
0760         workspace()->raiseWindowRequest(this, src, timestamp);
0761     }
0762 
0763     if (send_event) {
0764         sendSyntheticConfigureNotify();
0765     }
0766 }
0767 
0768 bool X11Window::belongsToDesktop() const
0769 {
0770     const auto members = group()->members();
0771     for (const X11Window *window : members) {
0772         if (window->isDesktop()) {
0773             return true;
0774         }
0775     }
0776     return false;
0777 }
0778 
0779 } // namespace