2
* Copyright © 2011 Canonical Ltd.
4
* Permission to use, copy, modify, distribute, and sell this software
5
* and its documentation for any purpose is hereby granted without
6
* fee, provided that the above copyright notice appear in all copies
7
* and that both that copyright notice and this permission notice
8
* appear in supporting documentation, and that the name of
9
* Canonical Ltd. not be used in advertising or publicity pertaining to
10
* distribution of the software without specific, written prior permission.
11
* Canonical Ltd. makes no representations about the suitability of this
12
* software for any purpose. It is provided "as is" without express or
15
* CANONICAL, LTD. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
16
* INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
17
* NO EVENT SHALL CANONICAL, LTD. BE LIABLE FOR ANY SPECIAL, INDIRECT OR
18
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
19
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
20
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
21
* WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23
* Authored by: Sam Spilsbury <sam.spilsbury@canonical.com>
26
#include "privatestackdebugger.h"
27
#include "privatewindow.h"
32
StackDebugger * gStackDebugger = NULL;
36
StackDebugger::Default ()
38
return gStackDebugger;
42
StackDebugger::SetDefault (StackDebugger *dbg)
45
delete gStackDebugger;
50
StackDebugger::StackDebugger (Display *dpy, Window root, boost::function <eventList ()> evProc) :
52
mServerChildren (NULL),
53
mWindowsChanged (false),
54
mServerWindowsChanged (false),
57
getEventsProc (evProc)
61
StackDebugger::~StackDebugger ()
65
XFree (mServerChildren);
66
mServerChildren = NULL;
72
StackDebugger::timedOut ()
74
return mTimeoutRequired;
78
StackDebugger::removeServerWindow (Window id)
80
/* Find the toplevel window in the list and remove it */
81
for (CompWindowList::iterator it = mLastServerWindows.begin ();
82
it != mLastServerWindows.end ();
85
if ((*it)->id () == id)
87
mLastServerWindows.erase (it);
94
StackDebugger::overrideRedirectRestack (Window toplevel, Window sibling)
96
CompWindow *tl = screen->findWindow (toplevel);
98
removeServerWindow (toplevel);
100
/* Find the sibling of this window and insert above it or at
101
* the bottom if the sibling is 0 */
105
for (CompWindowList::iterator it = mLastServerWindows.begin ();
106
it != mLastServerWindows.end ();
109
if (sibling == (*it)->id () ||
110
sibling == (*it)->frame ())
112
mLastServerWindows.insert (++it, tl);
118
mLastServerWindows.push_front (tl);
121
StackDebugger::eventList
122
StackDebugger::loadStack (CompWindowList &serverWindows, bool wait)
124
Window rootRet, parentRet;
128
XFree (mServerChildren);
132
XQueryTree (mDpy, mRoot, &rootRet, &parentRet,
133
&mServerChildren, &mServerNChildren);
135
events = getEventsProc ();
139
/* It is possible that X might not be keeping up with us, so
140
* we should give it about 300 ms in case the stacks are out of sync
141
* in order to deliver any more events that might be pending */
143
mTimeoutRequired = false;
144
mLastServerWindows = serverWindows;
146
if (mServerNChildren != serverWindows.size () && wait)
148
eventList moreEvents;
153
pfd.fd = ConnectionNumber (mDpy);
157
moreEvents = getEventsProc ();
159
foreach (XEvent e, moreEvents)
160
events.push_back (e);
162
mTimeoutRequired = true;
165
mDestroyedFrames.clear ();
167
XUngrabServer (mDpy);
174
StackDebugger::addDestroyedFrame (Window f)
176
mDestroyedFrames.push_back (f);
180
StackDebugger::removeDestroyedFrame (Window f)
182
mDestroyedFrames.remove (f);
186
StackDebugger::cmpStack (CompWindowList &windows,
187
CompWindowList &serverWindows,
190
std::vector <Window> serverSideWindows;
191
CompWindowList::reverse_iterator lrrit = windows.rbegin ();
192
CompWindowList::reverse_iterator lsrit = mLastServerWindows.rbegin ();
196
for (unsigned int n = 0; n < mServerNChildren; n++)
198
if (std::find (mDestroyedFrames.begin (),
199
mDestroyedFrames.end (), mServerChildren[n])
200
== mDestroyedFrames.end ())
201
serverSideWindows.push_back (mServerChildren[n]);
205
compLogMessage ("core", CompLogLevelDebug, "sent | recv | server |");
207
for (;(lrrit != windows.rend () ||
208
lsrit != mLastServerWindows.rend () ||
209
i != serverSideWindows.size ());)
215
if (lrrit != windows.rend ())
216
lrXid = (*lrrit)->priv->frame ? (*lrrit)->priv->frame : (*lrrit)->id ();
218
if (lsrit != mLastServerWindows.rend ())
219
lsXid = (*lsrit)->priv->frame ? (*lsrit)->priv->frame : (*lsrit)->id ();
221
if (i != serverSideWindows.size ())
222
sXid = serverSideWindows[serverSideWindows.size () - (i + 1)];
225
compLogMessage ("core", CompLogLevelDebug, "id 0x%x id 0x%x id 0x%x %s",
226
(unsigned int) lsXid, (unsigned int) lrXid,
227
(unsigned int) sXid, (lrXid != sXid) ? " /!\\ " : "");
232
if (lrrit != windows.rend ())
235
if (lsrit != mLastServerWindows.rend())
238
if (i != serverSideWindows.size ())
245
/* Checks the sanity of the list of windows last sent to the server.
247
* There are a few stacking "layers" here. From top to bottom:
248
* - 1) Docks stacked above toplevel windows which are stacked
249
* above fullscreen windows
250
* - 2) "Keep above" toplevel windows above fullscreen windows
251
* where a toplevel is in focus
252
* - 3) Toplevel windows in focus above fullscreen windows
253
* - 4) Fullscreen windows
255
* - 6) Keep above windows
256
* - 7) Toplevel windows
257
* - 8) Docks which are marked "Keep Below"
258
* - 9) "Keep Below" windows
259
* - 10) Desktop windows
261
* There are also a few rules which apply here:
262
* - 1) Dock windows should always be above normal windows
263
* except if marked keep below on any layer.
264
* - 2) Dock windows should ONLY be on one layer at a time,
265
* eg if they are on layer 1 then there cannot
266
* also be dock windows on layer 5 (except in the
267
* case of below dock windows on layer 8)
268
* - 3) Fullscreen windows must always be above docks when in
269
* focus, no matter if there is another window with "Keep Above"
270
* - 4) Focused windows take priority over fullscreen windows and
271
* docks must always be above them (see rule 1)
273
* As we pass through each layer, this function flags each one from
274
* lowest being the most bits set to highest being the least bits
275
* set. If a window violates this it raises a warning */
277
#define DOCKS_ABOVE_TOPLEVELS_ABOVE_FULLSCREEN 0xffffffff >> 1
278
#define KEEP_ABOVE_TOPLEVELS_ABOVE_FULLSCREEN 0xffffffff >> 2
279
#define TOPLEVELS_ABOVE_FULLSCREEN 0xffffffff >> 3
280
#define FULLSCREEN 0xffffffff >> 4
281
#define DOCKS 0xffffffff >> 5
282
#define KEEP_ABOVE 0xffffffff >> 6
283
#define TOPLEVELS 0xffffffff >> 7
284
#define DOCKS_BELOW 0xffffffff >> 8
285
#define KEEP_BELOW 0xffffffff >> 9
286
#define DESKTOP 0xffffffff >> 10
290
bool setCurrentLayer (Window requestingWindow, int layer, int ¤t)
293
/* Only allow steps down */
294
if ((current & layer) != layer)
297
compLogMessage ("stackdebugger", CompLogLevelWarn, "0x%x requested invalid layer 0x%x",
298
requestingWindow, layer, current);
308
StackDebugger::checkSanity (CompWindowList &serverWindows, bool verbose)
310
int current = 0xffffffff;
311
int oldCurrent = current;
315
compLogMessage ("stackdebugger", CompLogLevelDebug, "processing new stack --------");
317
/* go backwards down the stack */
318
for (CompWindowList::reverse_iterator rit = serverWindows.rbegin ();
319
rit != serverWindows.rend (); rit++)
321
CompWindow *w = (*rit);
323
/* Override redirect windows set all kinds
324
* of crazy stuff and are required to stack
325
* themselves so skip those */
326
if (w->overrideRedirect ())
329
/* ignore non-override redirect unmanaged windows */
333
/* ignore any windows that just got created */
337
/* determine the current layer */
338
if (w->type () == CompWindowTypeDockMask)
340
if ((current & DOCKS_ABOVE_TOPLEVELS_ABOVE_FULLSCREEN) ==
341
DOCKS_ABOVE_TOPLEVELS_ABOVE_FULLSCREEN)
343
bool fullscreenWindow = false;
345
/* search down the stack to check if there is a fullscreen
346
* window, otherwise we are not on the fullscreen layer */
347
for (CompWindow *rw = w->serverPrev; rw; rw = rw->serverPrev)
349
if (rw->type () & CompWindowTypeFullscreenMask)
351
fullscreenWindow = true;
356
/* if there is no fullscreen window, change the layer */
357
if (!fullscreenWindow)
358
err |= setCurrentLayer (w->id (), DOCKS, current);
360
err |= setCurrentLayer (w->id (), DOCKS_ABOVE_TOPLEVELS_ABOVE_FULLSCREEN, current);
362
else if (w->state () & CompWindowStateBelowMask)
363
err |= setCurrentLayer (w->id (), DOCKS_BELOW, current);
365
err |= setCurrentLayer (w->id (), DOCKS, current);
367
else if (w->type () == CompWindowTypeFullscreenMask)
369
err |= setCurrentLayer (w->id (), FULLSCREEN, current);
371
else if (w->type () == CompWindowTypeDesktopMask)
373
err |= setCurrentLayer (w->id (), DESKTOP, current);
375
/* everything else that is not a fullscreen window or a desktop */
378
if (w->state () & CompWindowStateAboveMask)
380
if ((current & KEEP_ABOVE_TOPLEVELS_ABOVE_FULLSCREEN) ==
381
KEEP_ABOVE_TOPLEVELS_ABOVE_FULLSCREEN)
383
bool fullscreenWindow = false;
385
/* search down the stack to check if there is a fullscreen
386
* window, otherwise we are not on the fullscreen layer */
387
for (CompWindow *rw = w->serverPrev; rw; rw = rw->serverPrev)
389
if (rw->type () == CompWindowTypeFullscreenMask)
391
fullscreenWindow = true;
396
if (!fullscreenWindow)
397
err |= setCurrentLayer (w->id (), KEEP_ABOVE, current);
399
err |= setCurrentLayer (w->id (), KEEP_ABOVE_TOPLEVELS_ABOVE_FULLSCREEN, current);
402
err |= setCurrentLayer (w->id (), KEEP_ABOVE, current);
404
else if (w->state () & CompWindowStateBelowMask)
405
err |= setCurrentLayer (w->id (), KEEP_BELOW, current);
408
if ((current & TOPLEVELS_ABOVE_FULLSCREEN) ==
409
TOPLEVELS_ABOVE_FULLSCREEN)
411
bool fullscreenWindow = false;
413
/* search down the stack to check if there is a fullscreen
414
* window, otherwise we are not on the fullscreen layer */
415
for (CompWindow *rw = w->serverPrev; rw; rw = rw->serverPrev)
417
if (rw->type () == CompWindowTypeFullscreenMask)
419
fullscreenWindow = true;
424
if (!fullscreenWindow)
425
err |= setCurrentLayer (w->id (), TOPLEVELS, current);
427
err |= setCurrentLayer (w->id (), TOPLEVELS_ABOVE_FULLSCREEN, current);
430
err |= setCurrentLayer (w->id (), TOPLEVELS, current);
434
if ((current & DOCKS_ABOVE_TOPLEVELS_ABOVE_FULLSCREEN) ==
435
DOCKS_ABOVE_TOPLEVELS_ABOVE_FULLSCREEN)
437
if (verbose && current != oldCurrent)
438
compLogMessage ("stackdebugger", CompLogLevelDebug, "on layer DOCKS_ABOVE_TOPLEVELS_ABOVE_FULLSCREEN");
440
else if ((current & KEEP_ABOVE_TOPLEVELS_ABOVE_FULLSCREEN) ==
441
KEEP_ABOVE_TOPLEVELS_ABOVE_FULLSCREEN)
443
if (verbose && current != oldCurrent)
444
compLogMessage ("stackdebugger", CompLogLevelDebug, "on layer KEEP_ABOVE_TOPLEVELS_ABOVE_FULLSCREEN");
446
else if ((current & TOPLEVELS_ABOVE_FULLSCREEN) == TOPLEVELS_ABOVE_FULLSCREEN)
448
if (verbose && current != oldCurrent)
449
compLogMessage ("stackdebugger", CompLogLevelDebug, "on layer TOPLEVELS_ABOVE_FULLSCREEN");
451
else if ((current & FULLSCREEN) == FULLSCREEN)
453
if (verbose && current != oldCurrent)
454
compLogMessage ("stackdebugger", CompLogLevelDebug, "on layer FULLSCREEN");
456
else if ((current & DOCKS) == DOCKS)
458
if (verbose && current != oldCurrent)
459
compLogMessage ("stackdebugger", CompLogLevelDebug, "on layer DOCKS");
461
else if ((current & KEEP_ABOVE) == KEEP_ABOVE)
463
if (verbose && current != oldCurrent)
464
compLogMessage ("stackdebugger", CompLogLevelDebug, "on layer KEEP_ABOVE");
466
else if ((current & TOPLEVELS) == TOPLEVELS)
468
if (verbose && current != oldCurrent)
469
compLogMessage ("stackdebugger", CompLogLevelDebug, "on layer TOPLEVELS");
471
else if ((current & DOCKS_BELOW) == DOCKS_BELOW)
473
if (verbose && current != oldCurrent)
474
compLogMessage ("stackdebugger", CompLogLevelDebug, "on layer DOCKS_BELOW");
476
else if ((current & KEEP_BELOW) == KEEP_BELOW)
478
if (verbose && current != oldCurrent)
479
compLogMessage ("stackdebugger", CompLogLevelDebug, "on layer KEEP_BELOW");
481
else if ((current & DESKTOP) == DESKTOP)
483
if (verbose && current != oldCurrent)
484
compLogMessage ("stackdebugger", CompLogLevelDebug, "on layer DESKTOP");
487
oldCurrent = current;