1/********************************************************************
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5Copyright (C) 1999, 2000 Matthias Ettrich <ettrich@kde.org>
6Copyright (C) 2003 Lubos Lunak <l.lunak@kde.org>
7
8This program is free software; you can redistribute it and/or modify
9it under the terms of the GNU General Public License as published by
10the Free Software Foundation; either version 2 of the License, or
11(at your option) any later version.
12
13This program is distributed in the hope that it will be useful,
14but WITHOUT ANY WARRANTY; without even the implied warranty of
15MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16GNU General Public License for more details.
17
18You should have received a copy of the GNU General Public License
19along with this program. If not, see <http://www.gnu.org/licenses/>.
20*********************************************************************/
21
22// SELI zmenit doc
23
24/*
25
26 This file contains things relevant to stacking order and layers.
27
28 Design:
29
30 Normal unconstrained stacking order, as requested by the user (by clicking
31 on windows to raise them, etc.), is in Workspace::unconstrained_stacking_order.
32 That list shouldn't be used at all, except for building
33 Workspace::stacking_order. The building is done
34 in Workspace::constrainedStackingOrder(). Only Workspace::stackingOrder() should
35 be used to get the stacking order, because it also checks the stacking order
36 is up to date.
37 All clients are also stored in Workspace::clients (except for isDesktop() clients,
38 as those are very special, and are stored in Workspace::desktops), in the order
39 the clients were created.
40
41 Every window has one layer assigned in which it is. There are 6 layers,
42 from bottom : DesktopLayer, BelowLayer, NormalLayer, DockLayer, AboveLayer
43 and ActiveLayer (see also NETWM sect.7.10.). The layer a window is in depends
44 on the window type, and on other things like whether the window is active.
45
46 NET::Splash clients belong to the Normal layer. NET::TopMenu clients
47 belong to Dock layer. Clients that are both NET::Dock and NET::KeepBelow
48 are in the Normal layer in order to keep the 'allow window to cover
49 the panel' Kicker setting to work as intended (this may look like a slight
50 spec violation, but a) I have no better idea, b) the spec allows adjusting
51 the stacking order if the WM thinks it's a good idea . We put all
52 NET::KeepAbove above all Docks too, even though the spec suggests putting
53 them in the same layer.
54
55 Most transients are in the same layer as their mainwindow,
56 see Workspace::constrainedStackingOrder(), they may also be in higher layers, but
57 they should never be below their mainwindow.
58
59 When some client attribute changes (above/below flag, transiency...),
60 Workspace::updateClientLayer() should be called in order to make
61 sure it's moved to the appropriate layer ClientList if needed.
62
63 Currently the things that affect client in which layer a client
64 belongs: KeepAbove/Keep Below flags, window type, fullscreen
65 state and whether the client is active, mainclient (transiency).
66
67 Make sure updateStackingOrder() is called in order to make
68 Workspace::stackingOrder() up to date and propagated to the world.
69 Using Workspace::blockStackingUpdates() (or the StackingUpdatesBlocker
70 helper class) it's possible to temporarily disable updates
71 and the stacking order will be updated once after it's allowed again.
72
73*/
74
75#include <assert.h>
76
77#include <kdebug.h>
78
79#include "utils.h"
80#include "client.h"
81#include "focuschain.h"
82#include "netinfo.h"
83#include "workspace.h"
84#include "tabbox.h"
85#include "group.h"
86#include "rules.h"
87#include "screens.h"
88#include "unmanaged.h"
89#include "deleted.h"
90#include "effects.h"
91#include <QX11Info>
92#include "composite.h"
93#ifdef KWIN_BUILD_SCREENEDGES
94#include "screenedge.h"
95#endif
96
97namespace KWin
98{
99
100//*******************************
101// Workspace
102//*******************************
103
104void Workspace::updateClientLayer(Client* c)
105{
106 if (c)
107 c->updateLayer();
108}
109
110void Workspace::updateStackingOrder(bool propagate_new_clients)
111{
112 if (block_stacking_updates > 0) {
113 if (propagate_new_clients)
114 blocked_propagating_new_clients = true;
115 return;
116 }
117 ToplevelList new_stacking_order = constrainedStackingOrder();
118 bool changed = (force_restacking || new_stacking_order != stacking_order);
119 force_restacking = false;
120 stacking_order = new_stacking_order;
121#if 0
122 kDebug(1212) << "stacking:" << changed;
123 if (changed || propagate_new_clients) {
124 for (ClientList::ConstIterator it = stacking_order.begin();
125 it != stacking_order.end();
126 ++it)
127 kDebug(1212) << (void*)(*it) << *it << ":" << (*it)->layer();
128 }
129#endif
130 if (changed || propagate_new_clients) {
131 propagateClients(propagate_new_clients);
132 emit stackingOrderChanged();
133 if (m_compositor) {
134 m_compositor->addRepaintFull();
135 }
136
137 if (active_client)
138 active_client->updateMouseGrab();
139 }
140}
141
142#ifdef KWIN_BUILD_SCREENEDGES
143/*!
144 * Some fullscreen effects have to raise the screenedge on top of an input window, thus all windows
145 * this function puts them back where they belong for regular use and is some cheap variant of
146 * the regular propagateClients function in that it completely ignores managed clients and everything
147 * else and also does not update the NETWM property.
148 * Called from Effects::destroyInputWindow so far.
149 */
150void Workspace::stackScreenEdgesUnderOverrideRedirect()
151{
152 Xcb::restackWindows(QVector<xcb_window_t>() << rootInfo()->supportWindow() << ScreenEdges::self()->windows());
153}
154#endif
155
156/*!
157 Propagates the managed clients to the world.
158 Called ONLY from updateStackingOrder().
159 */
160void Workspace::propagateClients(bool propagate_new_clients)
161{
162 // restack the windows according to the stacking order
163 // supportWindow > electric borders > clients > hidden clients
164 QVector<xcb_window_t> newWindowStack;
165
166 // Stack all windows under the support window. The support window is
167 // not used for anything (besides the NETWM property), and it's not shown,
168 // but it was lowered after kwin startup. Stacking all clients below
169 // it ensures that no client will be ever shown above override-redirect
170 // windows (e.g. popups).
171 newWindowStack << rootInfo()->supportWindow();
172
173#ifdef KWIN_BUILD_SCREENEDGES
174 newWindowStack << ScreenEdges::self()->windows();
175#endif
176
177 newWindowStack.reserve(newWindowStack.size() + 2*stacking_order.size()); // *2 for inputWindow
178
179 for (int i = stacking_order.size() - 1; i >= 0; --i) {
180 Client *client = qobject_cast<Client*>(stacking_order.at(i));
181 if (!client || client->hiddenPreview()) {
182 continue;
183 }
184
185 if (client->inputId())
186 // Stack the input window above the frame
187 newWindowStack << client->inputId();
188
189 newWindowStack << client->frameId();
190 }
191
192 // when having hidden previews, stack hidden windows below everything else
193 // (as far as pure X stacking order is concerned), in order to avoid having
194 // these windows that should be unmapped to interfere with other windows
195 for (int i = stacking_order.size() - 1; i >= 0; --i) {
196 Client *client = qobject_cast<Client*>(stacking_order.at(i));
197 if (!client || !client->hiddenPreview())
198 continue;
199 newWindowStack << client->frameId();
200 }
201 // TODO isn't it too inefficient to restack always all clients?
202 // TODO don't restack not visible windows?
203 assert(newWindowStack.at(0) == rootInfo()->supportWindow());
204 Xcb::restackWindows(newWindowStack);
205
206 int pos = 0;
207 Window *cl(NULL);
208 if (propagate_new_clients) {
209 cl = new Window[ desktops.count() + clients.count()];
210 // TODO this is still not completely in the map order
211 for (ClientList::ConstIterator it = desktops.constBegin(); it != desktops.constEnd(); ++it)
212 cl[pos++] = (*it)->window();
213 for (ClientList::ConstIterator it = clients.constBegin(); it != clients.constEnd(); ++it)
214 cl[pos++] = (*it)->window();
215 rootInfo()->setClientList(cl, pos);
216 delete [] cl;
217 }
218
219 cl = new Window[ stacking_order.count()];
220 pos = 0;
221 for (ToplevelList::ConstIterator it = stacking_order.constBegin(); it != stacking_order.constEnd(); ++it) {
222 if ((*it)->isClient())
223 cl[pos++] = (*it)->window();
224 }
225 rootInfo()->setClientListStacking(cl, pos);
226 delete [] cl;
227
228 // Make the cached stacking order invalid here, in case we need the new stacking order before we get
229 // the matching event, due to X being asynchronous.
230 x_stacking_dirty = true;
231}
232
233/*!
234 Returns topmost visible client. Windows on the dock, the desktop
235 or of any other special kind are excluded. Also if the window
236 doesn't accept focus it's excluded.
237 */
238// TODO misleading name for this method, too many slightly different ways to use it
239Client* Workspace::topClientOnDesktop(int desktop, int screen, bool unconstrained, bool only_normal) const
240{
241// TODO Q_ASSERT( block_stacking_updates == 0 );
242 ToplevelList list;
243 if (!unconstrained)
244 list = stacking_order;
245 else
246 list = unconstrained_stacking_order;
247 for (int i = list.size() - 1;
248 i >= 0;
249 --i) {
250 Client *c = qobject_cast<Client*>(list.at(i));
251 if (!c) {
252 continue;
253 }
254 if (c->isOnDesktop(desktop) && c->isShown(false) && c->isOnCurrentActivity()) {
255 if (screen != -1 && c->screen() != screen)
256 continue;
257 if (!only_normal)
258 return c;
259 if (c->wantsTabFocus() && !c->isSpecialWindow())
260 return c;
261 }
262 }
263 return 0;
264}
265
266Client* Workspace::findDesktop(bool topmost, int desktop) const
267{
268// TODO Q_ASSERT( block_stacking_updates == 0 );
269 if (topmost) {
270 for (int i = stacking_order.size() - 1; i >= 0; i--) {
271 Client *c = qobject_cast<Client*>(stacking_order.at(i));
272 if (c && c->isOnDesktop(desktop) && c->isDesktop()
273 && c->isShown(true))
274 return c;
275 }
276 } else { // bottom-most
277 foreach (Toplevel * c, stacking_order) {
278 Client *client = qobject_cast<Client*>(c);
279 if (client && c->isOnDesktop(desktop) && c->isDesktop()
280 && client->isShown(true))
281 return client;
282 }
283 }
284 return NULL;
285}
286
287void Workspace::raiseOrLowerClient(Client *c)
288{
289 if (!c) return;
290 Client* topmost = NULL;
291// TODO Q_ASSERT( block_stacking_updates == 0 );
292 if (most_recently_raised && stacking_order.contains(most_recently_raised) &&
293 most_recently_raised->isShown(true) && c->isOnCurrentDesktop())
294 topmost = most_recently_raised;
295 else
296 topmost = topClientOnDesktop(c->isOnAllDesktops() ? VirtualDesktopManager::self()->current() : c->desktop(),
297 options->isSeparateScreenFocus() ? c->screen() : -1);
298
299 if (c == topmost)
300 lowerClient(c);
301 else
302 raiseClient(c);
303}
304
305
306void Workspace::lowerClient(Client* c, bool nogroup)
307{
308 if (!c)
309 return;
310
311 c->cancelAutoRaise();
312
313 StackingUpdatesBlocker blocker(this);
314
315 unconstrained_stacking_order.removeAll(c);
316 unconstrained_stacking_order.prepend(c);
317 if (!nogroup && c->isTransient()) {
318 // lower also all windows in the group, in their reversed stacking order
319 ClientList wins = ensureStackingOrder(c->group()->members());
320 for (int i = wins.size() - 1;
321 i >= 0;
322 --i) {
323 if (wins[ i ] != c)
324 lowerClient(wins[ i ], true);
325 }
326 }
327
328 if (c == most_recently_raised)
329 most_recently_raised = 0;
330}
331
332void Workspace::lowerClientWithinApplication(Client* c)
333{
334 if (!c)
335 return;
336
337 c->cancelAutoRaise();
338
339 StackingUpdatesBlocker blocker(this);
340
341 unconstrained_stacking_order.removeAll(c);
342 bool lowered = false;
343 // first try to put it below the bottom-most window of the application
344 for (ToplevelList::Iterator it = unconstrained_stacking_order.begin();
345 it != unconstrained_stacking_order.end();
346 ++it) {
347 Client *client = qobject_cast<Client*>(*it);
348 if (!client) {
349 continue;
350 }
351 if (Client::belongToSameApplication(client, c)) {
352 unconstrained_stacking_order.insert(it, c);
353 lowered = true;
354 break;
355 }
356 }
357 if (!lowered)
358 unconstrained_stacking_order.prepend(c);
359 // ignore mainwindows
360}
361
362void Workspace::raiseClient(Client* c, bool nogroup)
363{
364 if (!c)
365 return;
366
367 c->cancelAutoRaise();
368
369 StackingUpdatesBlocker blocker(this);
370
371 if (!nogroup && c->isTransient()) {
372 ClientList transients;
373 Client *transient_parent = c;
374 while ((transient_parent = transient_parent->transientFor()))
375 transients << transient_parent;
376 foreach (transient_parent, transients)
377 raiseClient(transient_parent, true);
378 }
379
380 unconstrained_stacking_order.removeAll(c);
381 unconstrained_stacking_order.append(c);
382
383 if (!c->isSpecialWindow()) {
384 most_recently_raised = c;
385 pending_take_activity = NULL;
386 }
387}
388
389void Workspace::raiseClientWithinApplication(Client* c)
390{
391 if (!c)
392 return;
393
394 c->cancelAutoRaise();
395
396 StackingUpdatesBlocker blocker(this);
397 // ignore mainwindows
398
399 // first try to put it above the top-most window of the application
400 for (int i = unconstrained_stacking_order.size() - 1; i > -1 ; --i) {
401 Client *other = qobject_cast<Client*>(unconstrained_stacking_order.at(i));
402 if (!other) {
403 continue;
404 }
405 if (other == c) // don't lower it just because it asked to be raised
406 return;
407 if (Client::belongToSameApplication(other, c)) {
408 unconstrained_stacking_order.removeAll(c);
409 unconstrained_stacking_order.insert(unconstrained_stacking_order.indexOf(other) + 1, c); // insert after the found one
410 break;
411 }
412 }
413}
414
415void Workspace::raiseClientRequest(KWin::Client *c, NET::RequestSource src, xcb_timestamp_t timestamp)
416{
417 if (src == NET::FromTool || allowFullClientRaising(c, timestamp))
418 raiseClient(c);
419 else {
420 raiseClientWithinApplication(c);
421 c->demandAttention();
422 }
423}
424
425void Workspace::lowerClientRequest(KWin::Client *c, NET::RequestSource src, xcb_timestamp_t /*timestamp*/)
426{
427 // If the client has support for all this focus stealing prevention stuff,
428 // do only lowering within the application, as that's the more logical
429 // variant of lowering when application requests it.
430 // No demanding of attention here of course.
431 if (src == NET::FromTool || !c->hasUserTimeSupport())
432 lowerClient(c);
433 else
434 lowerClientWithinApplication(c);
435}
436
437
438void Workspace::restack(Client* c, Client* under)
439{
440 assert(unconstrained_stacking_order.contains(under));
441 if (!Client::belongToSameApplication(under, c)) {
442 // put in the stacking order below _all_ windows belonging to the active application
443 for (int i = 0; i < unconstrained_stacking_order.size(); ++i) {
444 Client *other = qobject_cast<Client*>(unconstrained_stacking_order.at(i));
445 if (other && other->layer() == c->layer() && Client::belongToSameApplication(under, other)) {
446 under = (c == other) ? 0 : other;
447 break;
448 }
449 }
450 }
451 if (under) {
452 unconstrained_stacking_order.removeAll(c);
453 unconstrained_stacking_order.insert(unconstrained_stacking_order.indexOf(under), c);
454 }
455
456 assert(unconstrained_stacking_order.contains(c));
457 FocusChain::self()->moveAfterClient(c, under);
458 updateStackingOrder();
459}
460
461void Workspace::restackClientUnderActive(Client* c)
462{
463 if (!active_client || active_client == c || active_client->layer() != c->layer()) {
464 raiseClient(c);
465 return;
466 }
467 restack(c, active_client);
468}
469
470void Workspace::restoreSessionStackingOrder(Client* c)
471{
472 if (c->sessionStackingOrder() < 0)
473 return;
474 StackingUpdatesBlocker blocker(this);
475 unconstrained_stacking_order.removeAll(c);
476 for (ToplevelList::Iterator it = unconstrained_stacking_order.begin(); // from bottom
477 it != unconstrained_stacking_order.end();
478 ++it) {
479 Client *current = qobject_cast<Client*>(*it);
480 if (!current) {
481 continue;
482 }
483 if (current->sessionStackingOrder() > c->sessionStackingOrder()) {
484 unconstrained_stacking_order.insert(it, c);
485 return;
486 }
487 }
488 unconstrained_stacking_order.append(c);
489}
490
491/*!
492 Returns a stacking order based upon \a list that fulfills certain contained.
493 */
494ToplevelList Workspace::constrainedStackingOrder()
495{
496 ToplevelList layer[ NumLayers ];
497
498#if 0
499 kDebug(1212) << "stacking1:";
500 for (ClientList::ConstIterator it = unconstrained_stacking_order.begin();
501 it != unconstrained_stacking_order.end();
502 ++it)
503 kDebug(1212) << (void*)(*it) << *it << ":" << (*it)->layer();
504#endif
505 // build the order from layers
506 QVector< QMap<Group*, Layer> > minimum_layer(screens()->count());
507 for (ToplevelList::ConstIterator it = unconstrained_stacking_order.constBegin(),
508 end = unconstrained_stacking_order.constEnd(); it != end; ++it) {
509 Layer l = (*it)->layer();
510
511 const int screen = (*it)->screen();
512 Client *c = qobject_cast<Client*>(*it);
513 QMap< Group*, Layer >::iterator mLayer = minimum_layer[screen].find(c ? c->group() : NULL);
514 if (mLayer != minimum_layer[screen].end()) {
515 // If a window is raised above some other window in the same window group
516 // which is in the ActiveLayer (i.e. it's fulscreened), make sure it stays
517 // above that window (see #95731).
518 if (*mLayer == ActiveLayer && (l == NormalLayer || l == AboveLayer))
519 l = ActiveLayer;
520 *mLayer = l;
521 } else if (c) {
522 minimum_layer[screen].insertMulti(c->group(), l);
523 }
524 layer[ l ].append(*it);
525 }
526 ToplevelList stacking;
527 for (Layer lay = FirstLayer;
528 lay < NumLayers;
529 ++lay)
530 stacking += layer[ lay ];
531#if 0
532 kDebug(1212) << "stacking2:";
533 for (ClientList::ConstIterator it = stacking.begin();
534 it != stacking.end();
535 ++it)
536 kDebug(1212) << (void*)(*it) << *it << ":" << (*it)->layer();
537#endif
538 // now keep transients above their mainwindows
539 // TODO this could(?) use some optimization
540 for (int i = stacking.size() - 1;
541 i >= 0;
542 ) {
543 Client *current = qobject_cast<Client*>(stacking[i]);
544 if (!current || !current->isTransient()) {
545 --i;
546 continue;
547 }
548 int i2 = -1;
549 if (current->groupTransient()) {
550 if (current->group()->members().count() > 0) {
551 // find topmost client this one is transient for
552 for (i2 = stacking.size() - 1;
553 i2 >= 0;
554 --i2) {
555 if (stacking[ i2 ] == stacking[ i ]) {
556 i2 = -1; // don't reorder, already the topmost in the group
557 break;
558 }
559 Client *c2 = qobject_cast<Client*>(stacking[ i2 ]);
560 if (!c2) {
561 continue;
562 }
563 if (c2->hasTransient(current, true)
564 && keepTransientAbove(c2, current))
565 break;
566 }
567 } // else i2 remains pointing at -1
568 } else {
569 for (i2 = stacking.size() - 1;
570 i2 >= 0;
571 --i2) {
572 Client *c2 = qobject_cast<Client*>(stacking[ i2 ]);
573 if (!c2) {
574 continue;
575 }
576 if (c2 == current) {
577 i2 = -1; // don't reorder, already on top of its mainwindow
578 break;
579 }
580 if (c2 == current->transientFor()
581 && keepTransientAbove(c2, current))
582 break;
583 }
584 }
585 if (i2 == -1) {
586 --i;
587 continue;
588 }
589 stacking.removeAt(i);
590 --i; // move onto the next item (for next for () iteration)
591 --i2; // adjust index of the mainwindow after the remove above
592 if (!current->transients().isEmpty()) // this one now can be possibly above its transients,
593 i = i2; // so go again higher in the stack order and possibly move those transients again
594 ++i2; // insert after (on top of) the mainwindow, it's ok if it2 is now stacking.end()
595 stacking.insert(i2, current);
596 }
597#if 0
598 kDebug(1212) << "stacking3:";
599 for (ClientList::ConstIterator it = stacking.begin();
600 it != stacking.end();
601 ++it)
602 kDebug(1212) << (void*)(*it) << *it << ":" << (*it)->layer();
603 kDebug(1212) << "\n\n";
604#endif
605 return stacking;
606}
607
608void Workspace::blockStackingUpdates(bool block)
609{
610 if (block) {
611 if (block_stacking_updates == 0)
612 blocked_propagating_new_clients = false;
613 ++block_stacking_updates;
614 } else // !block
615 if (--block_stacking_updates == 0) {
616 updateStackingOrder(blocked_propagating_new_clients);
617 if (effects)
618 static_cast<EffectsHandlerImpl*>(effects)->checkInputWindowStacking();
619 }
620}
621
622// Ensure list is in stacking order
623ClientList Workspace::ensureStackingOrder(const ClientList& list) const
624{
625// TODO Q_ASSERT( block_stacking_updates == 0 );
626 if (list.count() < 2)
627 return list;
628 // TODO is this worth optimizing?
629 ClientList result = list;
630 for (ToplevelList::ConstIterator it = stacking_order.constBegin();
631 it != stacking_order.constEnd();
632 ++it) {
633 Client *c = qobject_cast<Client*>(*it);
634 if (!c) {
635 continue;
636 }
637 if (result.removeAll(c) != 0)
638 result.append(c);
639 }
640 return result;
641}
642
643// check whether a transient should be actually kept above its mainwindow
644// there may be some special cases where this rule shouldn't be enfored
645bool Workspace::keepTransientAbove(const Client* mainwindow, const Client* transient)
646{
647 // #93832 - don't keep splashscreens above dialogs
648 if (transient->isSplash() && mainwindow->isDialog())
649 return false;
650 // This is rather a hack for #76026. Don't keep non-modal dialogs above
651 // the mainwindow, but only if they're group transient (since only such dialogs
652 // have taskbar entry in Kicker). A proper way of doing this (both kwin and kicker)
653 // needs to be found.
654 if (transient->isDialog() && !transient->isModal() && transient->groupTransient())
655 return false;
656 // #63223 - don't keep transients above docks, because the dock is kept high,
657 // and e.g. dialogs for them would be too high too
658 if (mainwindow->isDock())
659 return false;
660 return true;
661}
662
663// Returns all windows in their stacking order on the root window.
664ToplevelList Workspace::xStackingOrder() const
665{
666 if (!x_stacking_dirty)
667 return x_stacking;
668 x_stacking_dirty = false;
669 x_stacking.clear();
670 Window dummy;
671 Window* windows = NULL;
672 unsigned int count = 0;
673 XQueryTree(display(), rootWindow(), &dummy, &dummy, &windows, &count);
674 // use our own stacking order, not the X one, as they may differ
675 foreach (Toplevel * c, stacking_order)
676 x_stacking.append(c);
677 for (unsigned int i = 0;
678 i < count;
679 ++i) {
680 if (Unmanaged* c = findUnmanaged(WindowMatchPredicate(windows[ i ])))
681 x_stacking.append(c);
682 }
683 if (windows != NULL)
684 XFree(windows);
685 if (m_compositor) {
686 const_cast< Workspace* >(this)->m_compositor->checkUnredirect();
687 }
688 return x_stacking;
689}
690
691//*******************************
692// Client
693//*******************************
694
695void Client::restackWindow(xcb_window_t above, int detail, NET::RequestSource src, xcb_timestamp_t timestamp, bool send_event)
696{
697 Client *other = 0;
698 if (detail == XCB_STACK_MODE_OPPOSITE) {
699 other = workspace()->findClient(WindowMatchPredicate(above));
700 if (!other) {
701 workspace()->raiseOrLowerClient(this);
702 return;
703 }
704 ToplevelList::const_iterator it = workspace()->stackingOrder().constBegin(),
705 end = workspace()->stackingOrder().constEnd();
706 while (it != end) {
707 if (*it == this) {
708 detail = XCB_STACK_MODE_ABOVE;
709 break;
710 } else if (*it == other) {
711 detail = XCB_STACK_MODE_BELOW;
712 break;
713 }
714 ++it;
715 }
716 }
717 else if (detail == XCB_STACK_MODE_TOP_IF) {
718 other = workspace()->findClient(WindowMatchPredicate(above));
719 if (other && other->geometry().intersects(geometry()))
720 workspace()->raiseClientRequest(this, src, timestamp);
721 return;
722 }
723 else if (detail == XCB_STACK_MODE_BOTTOM_IF) {
724 other = workspace()->findClient(WindowMatchPredicate(above));
725 if (other && other->geometry().intersects(geometry()))
726 workspace()->lowerClientRequest(this, src, timestamp);
727 return;
728 }
729
730 if (!other)
731 other = workspace()->findClient(WindowMatchPredicate(above));
732
733 if (other && detail == XCB_STACK_MODE_ABOVE) {
734 ToplevelList::const_iterator it = workspace()->stackingOrder().constEnd(),
735 begin = workspace()->stackingOrder().constBegin();
736 while (--it != begin) {
737
738 if (*it == other) { // the other one is top on stack
739 it = begin; // invalidate
740 src = NET::FromTool; // force
741 break;
742 }
743 Client *c = qobject_cast<Client*>(*it);
744
745 if (!c || !( (*it)->isNormalWindow() && c->isShown(true) &&
746 (*it)->isOnCurrentDesktop() && (*it)->isOnCurrentActivity() && (*it)->isOnScreen(screen()) ))
747 continue; // irrelevant clients
748
749 if (*(it - 1) == other)
750 break; // "it" is the one above the target one, stack below "it"
751 }
752
753 if (it != begin && (*(it - 1) == other))
754 other = qobject_cast<Client*>(*it);
755 else
756 other = 0;
757 }
758
759 if (other)
760 workspace()->restack(this, other);
761 else if (detail == XCB_STACK_MODE_BELOW)
762 workspace()->lowerClientRequest(this, src, timestamp);
763 else if (detail == XCB_STACK_MODE_ABOVE)
764 workspace()->raiseClientRequest(this, src, timestamp);
765
766 if (send_event)
767 sendSyntheticConfigureNotify();
768}
769
770void Client::setKeepAbove(bool b)
771{
772 b = rules()->checkKeepAbove(b);
773 if (b && !rules()->checkKeepBelow(false))
774 setKeepBelow(false);
775 if (b == keepAbove()) {
776 // force hint change if different
777 if (bool(info->state() & NET::KeepAbove) != keepAbove())
778 info->setState(keepAbove() ? NET::KeepAbove : 0, NET::KeepAbove);
779 return;
780 }
781 keep_above = b;
782 info->setState(keepAbove() ? NET::KeepAbove : 0, NET::KeepAbove);
783 workspace()->updateClientLayer(this);
784 updateWindowRules(Rules::Above);
785
786 // Update states of all other windows in this group
787 if (tabGroup())
788 tabGroup()->updateStates(this, TabGroup::Layer);
789 emit keepAboveChanged(keep_above);
790}
791
792void Client::setKeepBelow(bool b)
793{
794 b = rules()->checkKeepBelow(b);
795 if (b && !rules()->checkKeepAbove(false))
796 setKeepAbove(false);
797 if (b == keepBelow()) {
798 // force hint change if different
799 if (bool(info->state() & NET::KeepBelow) != keepBelow())
800 info->setState(keepBelow() ? NET::KeepBelow : 0, NET::KeepBelow);
801 return;
802 }
803 keep_below = b;
804 info->setState(keepBelow() ? NET::KeepBelow : 0, NET::KeepBelow);
805 workspace()->updateClientLayer(this);
806 updateWindowRules(Rules::Below);
807
808 // Update states of all other windows in this group
809 if (tabGroup())
810 tabGroup()->updateStates(this, TabGroup::Layer);
811 emit keepBelowChanged(keep_below);
812}
813
814Layer Client::layer() const
815{
816 if (in_layer == UnknownLayer)
817 const_cast< Client* >(this)->in_layer = belongsToLayer();
818 return in_layer;
819}
820
821Layer Client::belongsToLayer() const
822{
823 if (isDesktop())
824 return DesktopLayer;
825 if (isSplash()) // no damn annoying splashscreens
826 return NormalLayer; // getting in the way of everything else
827 if (isDock()) {
828 // slight hack for the 'allow window to cover panel' Kicker setting
829 // don't move keepbelow docks below normal window, but only to the same
830 // layer, so that both may be raised to cover the other
831 if (keepBelow())
832 return NormalLayer;
833 if (keepAbove()) // slight hack for the autohiding panels
834 return AboveLayer;
835 return DockLayer;
836 }
837 if (keepBelow())
838 return BelowLayer;
839 if (isActiveFullScreen())
840 return ActiveLayer;
841 if (keepAbove())
842 return AboveLayer;
843 return NormalLayer;
844}
845
846void Client::updateLayer()
847{
848 if (layer() == belongsToLayer())
849 return;
850 StackingUpdatesBlocker blocker(workspace());
851 invalidateLayer(); // invalidate, will be updated when doing restacking
852 for (ClientList::ConstIterator it = transients().constBegin(),
853 end = transients().constEnd(); it != end; ++it)
854 (*it)->updateLayer();
855}
856
857bool rec_checkTransientOnTop(const ClientList &transients, const Client *topmost)
858{
859 foreach (const Client *transient, transients) {
860 if (transient == topmost || rec_checkTransientOnTop(transient->transients(), topmost)) {
861 return true;
862 }
863 }
864 return false;
865}
866
867bool Client::isActiveFullScreen() const
868{
869 if (!isFullScreen())
870 return false;
871
872 const Client* ac = workspace()->mostRecentlyActivatedClient(); // instead of activeClient() - avoids flicker
873 // according to NETWM spec implementation notes suggests
874 // "focused windows having state _NET_WM_STATE_FULLSCREEN" to be on the highest layer.
875 // we'll also take the screen into account
876 return ac && (ac == this || this->group() == ac->group() || ac->screen() != screen());
877}
878
879} // namespace
880