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 &current)
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 }