1 /*
2 * Copyright �� 2011 Canonical Ltd.
3 *
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
13 * implied warranty.
14 *
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.
22 *
23 * Authored by: Sam Spilsbury <sam.spilsbury@canonical.com>
24 */
25
26 #include "privatestackdebugger.h"
27 #include "privatescreen.h"
28 #include "privatewindow.h"
29 #include <poll.h>
30
31 namespace
32 {
33 StackDebugger * gStackDebugger = NULL;
34 }
35
36 StackDebugger *
37 StackDebugger::Default ()
38 {
39 return gStackDebugger;
40 }
41
42 void
43 StackDebugger::SetDefault (StackDebugger *dbg)
44 {
45 if (gStackDebugger)
46 delete gStackDebugger;
47
48 gStackDebugger = dbg;
49 }
50
51 StackDebugger::StackDebugger (Display *dpy, Window root, FetchXEventInterface *fetchXEvent) :
52 mServerNChildren (0),
53 mServerChildren (NULL),
54 mWindowsChanged (false),
55 mServerWindowsChanged (false),
56 mRoot (root),
57 mDpy (dpy),
58 mFetchXEvent (fetchXEvent),
59 mTimeoutRequired (false)
60 {
61 }
62
63 StackDebugger::~StackDebugger ()
64 {
65 if (mServerChildren)
66 {
67 XFree (mServerChildren);
68 mServerChildren = NULL;
69 mServerNChildren = 0;
70 }
71 }
72
73 bool
74 StackDebugger::timedOut ()
75 {
76 return mTimeoutRequired;
77 }
78
79 void
80 StackDebugger::removeServerWindow (Window id)
81 {
82 /* Find the toplevel window in the list and remove it */
83 for (CompWindowList::iterator it = mLastServerWindows.begin ();
84 it != mLastServerWindows.end ();
85 ++it)
86 {
87 if ((*it)->id () == id)
88 {
89 mLastServerWindows.erase (it);
90 break;
91 }
92 }
93 }
94
95 void
96 StackDebugger::overrideRedirectRestack (Window toplevel, Window sibling)
97 {
98 CompWindow *tl = screen->findWindow (toplevel);
99
100 removeServerWindow (toplevel);
101
102 /* Find the sibling of this window and insert above it or at
103 * the bottom if the sibling is 0 */
104
105 if (sibling)
106 {
107 for (CompWindowList::iterator it = mLastServerWindows.begin ();
108 it != mLastServerWindows.end ();
109 ++it)
110 {
111 if (sibling == (*it)->id () ||
112 sibling == (*it)->frame ())
113 {
114 mLastServerWindows.insert (++it, tl);
115 break;
116 }
117 }
118 }
119 else
120 mLastServerWindows.push_front (tl);
121 }
122
123 bool
124 StackDebugger::getNextEvent (XEvent &ev)
125 {
126 if (mEvents.empty ())
127 return false;
128
129 ev = mEvents.front ();
130
131 mEvents.pop_front ();
132
133 return true;
134 }
135
136 void
137 StackDebugger::loadStack (CompWindowList &serverWindows, bool wait)
138 {
139 Window rootRet, parentRet;
140
Condition "this->mServerChildren", taking true branch
141 if (mServerChildren)
142 XFree (mServerChildren);
143
144 XSync (mDpy, FALSE);
145 XGrabServer (mDpy);
146 XQueryTree (mDpy, mRoot, &rootRet, &parentRet,
147 &mServerChildren, &mServerNChildren);
148
149 unsigned int n = XEventsQueued (mDpy, QueuedAfterFlush);
150 mEvents.clear ();
151 mEvents.resize (n);
152 std::list <XEvent>::iterator it = mEvents.begin ();
153
Condition "it != this->mEvents.end()", taking false branch
154 while (it != mEvents.end ())
155 {
156 mFetchXEvent->getNextXEvent ((*it));
157 ++it;
Reached end of loop
158 }
159
160 XSync (mDpy, FALSE);
161
162 /* It is possible that X might not be keeping up with us, so
163 * we should give it about 300 ms in case the stacks are out of sync
164 * in order to deliver any more events that might be pending */
165
166 mTimeoutRequired = false;
167 mLastServerWindows = serverWindows;
168
Condition "this->mServerNChildren != serverWindows->size()", taking true branch
Condition "wait", taking true branch
169 if (mServerNChildren != serverWindows.size () && wait)
170 {
171 struct pollfd pfd;
172
173 pfd.events = POLLIN;
174 pfd.revents = 0;
175 pfd.fd = ConnectionNumber (mDpy);
176
CID 12417 - CHECKED_RETURN
Calling function "poll(&pfd, 1UL, 300)" without checking return value. This library function may fail and return an error code.
No check of the return value of "poll(&pfd, 1UL, 300)".
177 poll (&pfd, 1, 300);
178
179 XEvent e;
180
181 while (mFetchXEvent->getNextXEvent (e))
182 mEvents.push_back (e);
183
184 mTimeoutRequired = true;
185 }
186
187 mDestroyedFrames.clear ();
188
189 XUngrabServer (mDpy);
190 XSync (mDpy, FALSE);
191 }
192
193 void
194 StackDebugger::addDestroyedFrame (Window f)
195 {
196 mDestroyedFrames.push_back (f);
197 }
198
199 void
200 StackDebugger::removeDestroyedFrame (Window f)
201 {
202 mDestroyedFrames.remove (f);
203 }
204
205 bool
206 StackDebugger::cmpStack (CompWindowList &windows,
207 CompWindowList &serverWindows,
208 bool verbose)
209 {
210 std::vector <Window> serverSideWindows;
211 CompWindowList::reverse_iterator lrrit = windows.rbegin ();
212 CompWindowList::reverse_iterator lsrit = mLastServerWindows.rbegin ();
213 unsigned int i = 0;
214 bool err = false;
215
216 for (unsigned int n = 0; n < mServerNChildren; n++)
217 {
218 if (std::find (mDestroyedFrames.begin (),
219 mDestroyedFrames.end (), mServerChildren[n])
220 == mDestroyedFrames.end ())
221 serverSideWindows.push_back (mServerChildren[n]);
222 }
223
224 if (verbose)
225 compLogMessage ("core", CompLogLevelDebug, "sent | recv | server |");
226
227 for (;(lrrit != windows.rend () ||
228 lsrit != mLastServerWindows.rend () ||
229 i != serverSideWindows.size ());)
230 {
231 Window lrXid = 0;
232 Window lsXid = 0;
233 Window sXid = 0;
234
235 if (lrrit != windows.rend ())
236 lrXid = (*lrrit)->priv->frame ? (*lrrit)->priv->frame : (*lrrit)->id ();
237
238 if (lsrit != mLastServerWindows.rend ())
239 lsXid = (*lsrit)->priv->frame ? (*lsrit)->priv->frame : (*lsrit)->id ();
240
241 if (i != serverSideWindows.size ())
242 sXid = serverSideWindows[serverSideWindows.size () - (i + 1)];
243
244 if (verbose)
245 compLogMessage ("core", CompLogLevelDebug, "id 0x%x id 0x%x id 0x%x %s",
246 (unsigned int) lsXid, (unsigned int) lrXid,
247 (unsigned int) sXid, (lrXid != sXid) ? " /!\\ " : "");
248
249 if (lrXid != sXid)
250 err = true;
251
252 if (lrrit != windows.rend ())
253 ++lrrit;
254
255 if (lsrit != mLastServerWindows.rend())
256 ++lsrit;
257
258 if (i != serverSideWindows.size ())
259 i++;
260 }
261
262 return err;
263 }
264
265 /* Checks the sanity of the list of windows last sent to the server.
266 *
267 * There are a few stacking "layers" here. From top to bottom:
268 * - 1) Docks stacked above toplevel windows which are stacked
269 * above fullscreen windows
270 * - 2) "Keep above" toplevel windows above fullscreen windows
271 * where a toplevel is in focus
272 * - 3) Toplevel windows in focus above fullscreen windows
273 * - 4) Fullscreen windows
274 * - 5) Dock windows
275 * - 6) Keep above windows
276 * - 7) Toplevel windows
277 * - 8) Docks which are marked "Keep Below"
278 * - 9) "Keep Below" windows
279 * - 10) Desktop windows
280 *
281 * There are also a few rules which apply here:
282 * - 1) Dock windows should always be above normal windows
283 * except if marked keep below on any layer.
284 * - 2) Dock windows should ONLY be on one layer at a time,
285 * eg if they are on layer 1 then there cannot
286 * also be dock windows on layer 5 (except in the
287 * case of below dock windows on layer 8)
288 * - 3) Fullscreen windows must always be above docks when in
289 * focus, no matter if there is another window with "Keep Above"
290 * - 4) Focused windows take priority over fullscreen windows and
291 * docks must always be above them (see rule 1)
292 *
293 * As we pass through each layer, this function flags each one from
294 * lowest being the most bits set to highest being the least bits
295 * set. If a window violates this it raises a warning */
296
297 #define DOCKS_ABOVE_TOPLEVELS_ABOVE_FULLSCREEN 0xffffffff >> 1
298 #define KEEP_ABOVE_TOPLEVELS_ABOVE_FULLSCREEN 0xffffffff >> 2
299 #define TOPLEVELS_ABOVE_FULLSCREEN 0xffffffff >> 3
300 #define FULLSCREEN 0xffffffff >> 4
301 #define DOCKS 0xffffffff >> 5
302 #define KEEP_ABOVE 0xffffffff >> 6
303 #define TOPLEVELS 0xffffffff >> 7
304 #define DOCKS_BELOW 0xffffffff >> 8
305 #define KEEP_BELOW 0xffffffff >> 9
306 #define DESKTOP 0xffffffff >> 10
307
308 namespace
309 {
310 bool setCurrentLayer (Window requestingWindow, int layer, int ¤t)
311 {
312 bool ret = false;
313 /* Only allow steps down */
314 if ((current & layer) != layer)
315 {
316 ret = true;
317 compLogMessage ("stackdebugger", CompLogLevelWarn, "0x%x requested invalid layer 0x%x",
318 requestingWindow, layer, current);
319 }
320
321 current = layer;
322
323 return ret;
324 }
325 }
326
327 bool
328 StackDebugger::checkSanity (CompWindowList &serverWindows, bool verbose)
329 {
330 int current = 0xffffffff;
331 int oldCurrent = current;
332 bool err = false;
333
334 if (verbose)
335 compLogMessage ("stackdebugger", CompLogLevelDebug, "processing new stack --------");
336
337 /* go backwards down the stack */
338 for (CompWindowList::reverse_iterator rit = serverWindows.rbegin ();
339 rit != serverWindows.rend (); ++rit)
340 {
341 CompWindow *w = (*rit);
342
343 /* Override redirect windows set all kinds
344 * of crazy stuff and are required to stack
345 * themselves so skip those */
346 if (w->overrideRedirect ())
347 continue;
348
349 /* ignore non-override redirect unmanaged windows */
350 if (!w->managed ())
351 continue;
352
353 /* ignore any windows that just got created */
354 if (!w->mapNum ())
355 continue;
356
357 /* determine the current layer */
358 if (w->type () == CompWindowTypeDockMask)
359 {
360 if ((current & DOCKS_ABOVE_TOPLEVELS_ABOVE_FULLSCREEN) ==
361 DOCKS_ABOVE_TOPLEVELS_ABOVE_FULLSCREEN)
362 {
363 bool fullscreenWindow = false;
364
365 /* search down the stack to check if there is a fullscreen
366 * window, otherwise we are not on the fullscreen layer */
367 for (CompWindow *rw = w->prev; rw; rw = rw->prev)
368 {
369 if (rw->type () & CompWindowTypeFullscreenMask)
370 {
371 fullscreenWindow = true;
372 break;
373 }
374 }
375
376 /* if there is no fullscreen window, change the layer */
377 if (!fullscreenWindow)
378 err |= setCurrentLayer (w->id (), DOCKS, current);
379 else
380 err |= setCurrentLayer (w->id (), DOCKS_ABOVE_TOPLEVELS_ABOVE_FULLSCREEN, current);
381 }
382 else if (w->state () & CompWindowStateBelowMask)
383 err |= setCurrentLayer (w->id (), DOCKS_BELOW, current);
384 else
385 err |= setCurrentLayer (w->id (), DOCKS, current);
386 }
387 else if (w->type () == CompWindowTypeFullscreenMask)
388 {
389 err |= setCurrentLayer (w->id (), FULLSCREEN, current);
390 }
391 else if (w->type () == CompWindowTypeDesktopMask)
392 {
393 err |= setCurrentLayer (w->id (), DESKTOP, current);
394 }
395 /* everything else that is not a fullscreen window or a desktop */
396 else
397 {
398 if (w->state () & CompWindowStateAboveMask)
399 {
400 if ((current & KEEP_ABOVE_TOPLEVELS_ABOVE_FULLSCREEN) ==
401 KEEP_ABOVE_TOPLEVELS_ABOVE_FULLSCREEN)
402 {
403 bool fullscreenWindow = false;
404
405 /* search down the stack to check if there is a fullscreen
406 * window, otherwise we are not on the fullscreen layer */
407 for (CompWindow *rw = w->prev; rw; rw = rw->prev)
408 {
409 if (rw->type () & CompWindowTypeFullscreenMask)
410 {
411 fullscreenWindow = true;
412 break;
413 }
414 }
415
416 if (!fullscreenWindow)
417 err |= setCurrentLayer (w->id (), KEEP_ABOVE, current);
418 else
419 err |= setCurrentLayer (w->id (), KEEP_ABOVE_TOPLEVELS_ABOVE_FULLSCREEN, current);
420 }
421 else
422 err |= setCurrentLayer (w->id (), KEEP_ABOVE, current);
423 }
424 else if (w->state () & CompWindowStateBelowMask)
425 err |= setCurrentLayer (w->id (), KEEP_BELOW, current);
426 else
427 {
428 if ((current & TOPLEVELS_ABOVE_FULLSCREEN) ==
429 TOPLEVELS_ABOVE_FULLSCREEN)
430 {
431 bool fullscreenWindow = false;
432
433 /* search down the stack to check if there is a fullscreen
434 * window, otherwise we are not on the fullscreen layer */
435 for (CompWindow *rw = w->prev; rw; rw = rw->prev)
436 {
437 if (rw->type () & CompWindowTypeFullscreenMask)
438 {
439 fullscreenWindow = true;
440 break;
441 }
442 }
443
444 if (!fullscreenWindow)
445 err |= setCurrentLayer (w->id (), TOPLEVELS, current);
446 else
447 err |= setCurrentLayer (w->id (), TOPLEVELS_ABOVE_FULLSCREEN, current);
448 }
449 else
450 err |= setCurrentLayer (w->id (), TOPLEVELS, current);
451 }
452 }
453
454 if ((current & DOCKS_ABOVE_TOPLEVELS_ABOVE_FULLSCREEN) ==
455 DOCKS_ABOVE_TOPLEVELS_ABOVE_FULLSCREEN)
456 {
457 if (verbose && current != oldCurrent)
458 compLogMessage ("stackdebugger", CompLogLevelDebug, "on layer DOCKS_ABOVE_TOPLEVELS_ABOVE_FULLSCREEN");
459 }
460 else if ((current & KEEP_ABOVE_TOPLEVELS_ABOVE_FULLSCREEN) ==
461 KEEP_ABOVE_TOPLEVELS_ABOVE_FULLSCREEN)
462 {
463 if (verbose && current != oldCurrent)
464 compLogMessage ("stackdebugger", CompLogLevelDebug, "on layer KEEP_ABOVE_TOPLEVELS_ABOVE_FULLSCREEN");
465 }
466 else if ((current & TOPLEVELS_ABOVE_FULLSCREEN) == TOPLEVELS_ABOVE_FULLSCREEN)
467 {
468 if (verbose && current != oldCurrent)
469 compLogMessage ("stackdebugger", CompLogLevelDebug, "on layer TOPLEVELS_ABOVE_FULLSCREEN");
470 }
471 else if ((current & FULLSCREEN) == FULLSCREEN)
472 {
473 if (verbose && current != oldCurrent)
474 compLogMessage ("stackdebugger", CompLogLevelDebug, "on layer FULLSCREEN");
475 }
476 else if ((current & DOCKS) == DOCKS)
477 {
478 if (verbose && current != oldCurrent)
479 compLogMessage ("stackdebugger", CompLogLevelDebug, "on layer DOCKS");
480 }
481 else if ((current & KEEP_ABOVE) == KEEP_ABOVE)
482 {
483 if (verbose && current != oldCurrent)
484 compLogMessage ("stackdebugger", CompLogLevelDebug, "on layer KEEP_ABOVE");
485 }
486 else if ((current & TOPLEVELS) == TOPLEVELS)
487 {
488 if (verbose && current != oldCurrent)
489 compLogMessage ("stackdebugger", CompLogLevelDebug, "on layer TOPLEVELS");
490 }
491 else if ((current & DOCKS_BELOW) == DOCKS_BELOW)
492 {
493 if (verbose && current != oldCurrent)
494 compLogMessage ("stackdebugger", CompLogLevelDebug, "on layer DOCKS_BELOW");
495 }
496 else if ((current & KEEP_BELOW) == KEEP_BELOW)
497 {
498 if (verbose && current != oldCurrent)
499 compLogMessage ("stackdebugger", CompLogLevelDebug, "on layer KEEP_BELOW");
500 }
501 else if ((current & DESKTOP) == DESKTOP)
502 {
503 if (verbose && current != oldCurrent)
504 compLogMessage ("stackdebugger", CompLogLevelDebug, "on layer DESKTOP");
505 }
506
507 oldCurrent = current;
508 }
509
510 return err;
511 }