1 /*      $Id$
2 
3         This program is free software; you can redistribute it and/or modify
4         it under the terms of the GNU General Public License as published by
5         the Free Software Foundation; either version 2, or (at your option)
6         any later version.
7 
8         This program is distributed in the hope that it will be useful,
9         but WITHOUT ANY WARRANTY; without even the implied warranty of
10         MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11         GNU General Public License for more details.
12 
13         You should have received a copy of the GNU General Public License
14         along with this program; if not, write to the Free Software
15         Foundation, Inc., Inc., 51 Franklin Street, Fifth Floor, Boston,
16         MA 02110-1301, USA.
17 
18 
19         xfwm4    - (c) 2002-2011 Olivier Fourdan
20 
21  */
22 
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 
27 #include <X11/Xlib.h>
28 #include <X11/Xutil.h>
29 #include <X11/Xatom.h>
30 #include <glib.h>
31 #include <libxfce4util/libxfce4util.h>
32 
33 #include "display.h"
34 #include "screen.h"
35 #include "mywindow.h"
36 #include "client.h"
37 #include "stacking.h"
38 #include "netwm.h"
39 #include "transients.h"
40 #include "frame.h"
41 #include "focus.h"
42 
43 static guint raise_timeout = 0;
44 
45 void
clientApplyStackList(ScreenInfo * screen_info)46 clientApplyStackList (ScreenInfo *screen_info)
47 {
48     Window *xwinstack;
49     guint nwindows;
50     gint i;
51 
52     DBG ("applying stack list");
53     nwindows = g_list_length (screen_info->windows_stack);
54 
55     i = 0;
56     xwinstack = g_new0 (Window, nwindows + 4);
57     xwinstack[i++] = MYWINDOW_XWINDOW (screen_info->sidewalk[0]);
58     xwinstack[i++] = MYWINDOW_XWINDOW (screen_info->sidewalk[1]);
59     xwinstack[i++] = MYWINDOW_XWINDOW (screen_info->sidewalk[2]);
60     xwinstack[i++] = MYWINDOW_XWINDOW (screen_info->sidewalk[3]);
61 
62     if (nwindows)
63     {
64         GList *list;
65         Client *c = NULL;
66 
67         for (list = g_list_last(screen_info->windows_stack); list; list = g_list_previous (list))
68         {
69             c = (Client *) list->data;
70             xwinstack[i++] = c->frame;
71             DBG ("  [%i] \"%s\" (0x%lx)", i, c->name, c->window);
72         }
73     }
74 
75     myDisplayErrorTrapPush (screen_info->display_info);
76     XRestackWindows (myScreenGetXDisplay (screen_info), xwinstack, (int) nwindows + 4);
77     myDisplayErrorTrapPopIgnored (screen_info->display_info);
78 
79     g_free (xwinstack);
80 }
81 
82 Client *
clientGetLowestTransient(Client * c)83 clientGetLowestTransient (Client * c)
84 {
85     ScreenInfo *screen_info;
86     Client *lowest_transient, *c2;
87     GList *list;
88 
89     g_return_val_if_fail (c != NULL, NULL);
90 
91     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
92 
93     lowest_transient = NULL;
94     screen_info = c->screen_info;
95     for (list = screen_info->windows_stack; list; list = g_list_next (list))
96     {
97         c2 = (Client *) list->data;
98         if ((c2 != c) && clientIsTransientFor (c2, c))
99         {
100             lowest_transient = c2;
101             break;
102         }
103     }
104     return lowest_transient;
105 }
106 
107 Client *
clientGetHighestTransientOrModalFor(Client * c)108 clientGetHighestTransientOrModalFor (Client * c)
109 {
110     ScreenInfo *screen_info;
111     Client *highest_transient;
112     Client *c2;
113     GList *list;
114 
115     g_return_val_if_fail (c != NULL, NULL);
116 
117     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
118 
119     screen_info = c->screen_info;
120     highest_transient = NULL;
121     for (list = screen_info->windows_stack; list; list = g_list_next (list))
122     {
123         c2 = (Client *) list->data;
124         if (c2)
125         {
126             if (clientIsTransientOrModalFor (c, c2))
127             {
128                 highest_transient = c2;
129             }
130         }
131     }
132 
133     return highest_transient;
134 }
135 
136 Client *
clientGetTopMostForGroup(Client * c)137 clientGetTopMostForGroup (Client * c)
138 {
139     ScreenInfo *screen_info;
140     Client *top_most;
141     Client *c2;
142     GList *list;
143 
144     g_return_val_if_fail (c != NULL, NULL);
145 
146     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
147 
148     screen_info = c->screen_info;
149     top_most = NULL;
150     for (list = screen_info->windows_stack; list; list = g_list_next (list))
151     {
152         c2 = (Client *) list->data;
153         if (c2 != c)
154         {
155             if (clientSameGroup (c, c2))
156             {
157                 top_most = c2;
158             }
159         }
160     }
161 
162     return top_most;
163 }
164 
165 gboolean
clientIsTopMost(Client * c)166 clientIsTopMost (Client *c)
167 {
168     ScreenInfo *screen_info;
169     GList *list, *l2;
170     Client *c2;
171 
172     g_return_val_if_fail (c != NULL, FALSE);
173 
174     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
175 
176     screen_info = c->screen_info;
177 
178     list = g_list_find (screen_info->windows_stack, (gconstpointer) c);
179     if (list)
180     {
181         l2 = g_list_next (list);
182         while (l2)
183         {
184             c2 = (Client *) l2->data;
185             if (FLAG_TEST (c2->xfwm_flags, XFWM_FLAG_VISIBLE) && (c2->win_layer == c->win_layer))
186             {
187                 return FALSE;
188             }
189             l2 = g_list_next (l2);
190         }
191     }
192     return TRUE;
193 }
194 
195 Client *
clientGetNextTopMost(ScreenInfo * screen_info,guint layer,Client * exclude)196 clientGetNextTopMost (ScreenInfo *screen_info, guint layer, Client * exclude)
197 {
198     Client *top, *c;
199     GList *list;
200 
201     TRACE ("layer %u", layer);
202 
203     top = NULL;
204     for (list = screen_info->windows_stack; list; list = g_list_next (list))
205     {
206         c = (Client *) list->data;
207         TRACE ("*** stack window \"%s\" (0x%lx), layer %i", c->name, c->window, (int) c->win_layer);
208         if (!exclude || (c != exclude))
209         {
210             if (c->win_layer > layer)
211             {
212                 top = c;
213                 break;
214             }
215         }
216     }
217 
218     return top;
219 }
220 
221 Client *
clientGetBottomMost(ScreenInfo * screen_info,guint layer,Client * exclude)222 clientGetBottomMost (ScreenInfo *screen_info, guint layer, Client * exclude)
223 {
224     Client *bot, *c;
225     GList *list;
226 
227     TRACE ("layer %u", layer);
228 
229     bot = NULL;
230     for (list = screen_info->windows_stack; list; list = g_list_next (list))
231     {
232         c = (Client *) list->data;
233         if (c)
234         {
235             TRACE ("*** stack window \"%s\" (0x%lx), layer %i", c->name,
236                 c->window, (int) c->win_layer);
237             if (!exclude || (c != exclude))
238             {
239                 if (c->win_layer < layer)
240                 {
241                     bot = c;
242                 }
243                 else if (c->win_layer >= layer)
244                 {
245                     break;
246                 }
247             }
248         }
249     }
250     return bot;
251 }
252 
253 /*
254  * This function does the same as XQueryPointer but w/out the race
255  * conditions caused by querying the X server
256  */
257 Client *
clientAtPosition(ScreenInfo * screen_info,int x,int y,GList * exclude_list)258 clientAtPosition (ScreenInfo *screen_info, int x, int y, GList * exclude_list)
259 {
260     GList *list;
261     Client *c, *c2;
262 
263     TRACE ("(%i,%i)", x, y);
264 
265     c = NULL;
266     for (list = g_list_last (screen_info->windows_stack); list; list = g_list_previous (list))
267     {
268         c2 = (Client *) list->data;
269         if ((frameX (c2) <= x) && (frameX (c2) + frameWidth (c2) >= x)
270             && (frameY (c2) <= y) && (frameY (c2) + frameHeight (c2) >= y))
271         {
272             if (clientSelectMask (c2, NULL, SEARCH_INCLUDE_SKIP_PAGER | SEARCH_INCLUDE_SKIP_TASKBAR, WINDOW_REGULAR_FOCUSABLE)
273                 && !g_list_find (exclude_list, (gconstpointer) c2))
274             {
275                 c = c2;
276                 break;
277             }
278         }
279     }
280 
281     return c;
282 }
283 
284 static void
clientRaiseInternal(Client * c,Client * client_sibling)285 clientRaiseInternal (Client * c, Client * client_sibling)
286 {
287     ScreenInfo *screen_info;
288     Client *c2, *c3;
289     GList *l1, *l2;
290     GList *transients;
291     GList *windows_stack_copy;
292     GList *sibling;
293 
294     screen_info = c->screen_info;
295     transients = NULL;
296 
297     /* Copy the existing window stack temporarily as reference */
298     windows_stack_copy = g_list_copy (screen_info->windows_stack);
299     screen_info->windows_stack = g_list_remove (screen_info->windows_stack, (gconstpointer) c);
300     sibling = NULL;
301 
302     if (client_sibling)
303     {
304         /* If there is one, look for its place in the list */
305         sibling = g_list_find (screen_info->windows_stack, (gconstpointer) client_sibling);
306         /* Place the raised window just before it */
307         screen_info->windows_stack = g_list_insert_before (screen_info->windows_stack, sibling, c);
308     }
309     else
310     {
311         /* There will be no window on top of the raised window, so place it at the end of list */
312         screen_info->windows_stack = g_list_append (screen_info->windows_stack, c);
313     }
314     /* Now, look for transients, transients of transients, etc. */
315     for (l1 = windows_stack_copy; l1; l1 = g_list_next (l1))
316     {
317         c2 = (Client *) l1->data;
318         if (c2)
319         {
320             if ((c2 != c) && clientIsTransientOrModalFor (c2, c) && (c2->win_layer <= c->win_layer))
321             {
322                 transients = g_list_append (transients, c2);
323                 if (sibling)
324                 {
325                     /* Make sure client_sibling is not c2 otherwise we create a circular linked list */
326                     if (client_sibling != c2)
327                     {
328                         /* Place the transient window just before sibling */
329                         screen_info->windows_stack = g_list_remove (screen_info->windows_stack, (gconstpointer) c2);
330                         screen_info->windows_stack = g_list_insert_before (screen_info->windows_stack, sibling, c2);
331                     }
332                 }
333                 else
334                 {
335                     /* There will be no window on top of the transient window, so place it at the end of list */
336                     screen_info->windows_stack = g_list_remove (screen_info->windows_stack, (gconstpointer) c2);
337                     screen_info->windows_stack = g_list_append (screen_info->windows_stack, c2);
338                 }
339             }
340             else
341             {
342                 for (l2 = transients; l2; l2 = g_list_next (l2))
343                 {
344                     c3 = (Client *) l2->data;
345                     if ((c3 != c2) && clientIsTransientOrModalFor (c2, c3))
346                     {
347                         transients = g_list_append (transients, c2);
348                         if (sibling)
349                         {
350                             /* Again, make sure client_sibling is not c2 to avoid a circular linked list */
351                             if (client_sibling != c2)
352                             {
353                                 /* Place the transient window just before sibling */
354                                 screen_info->windows_stack = g_list_remove (screen_info->windows_stack, (gconstpointer) c2);
355                                 screen_info->windows_stack = g_list_insert_before (screen_info->windows_stack, sibling, c2);
356                             }
357                         }
358                         else
359                         {
360                             /* There will be no window on top of the transient window, so place it at the end of list */
361                             screen_info->windows_stack = g_list_remove (screen_info->windows_stack, (gconstpointer) c2);
362                             screen_info->windows_stack = g_list_append (screen_info->windows_stack, c2);
363                         }
364                         break;
365                     }
366                 }
367             }
368         }
369     }
370 
371     if (transients)
372     {
373         g_list_free (transients);
374     }
375 
376     if (windows_stack_copy)
377     {
378         g_list_free (windows_stack_copy);
379     }
380 }
381 
382 void
clientRaise(Client * c,Window wsibling)383 clientRaise (Client * c, Window wsibling)
384 {
385     ScreenInfo *screen_info;
386     DisplayInfo *display_info;
387     Client *ancestor;
388     Client *client_sibling;
389     Client *c2;
390     GList *sibling;
391     GList *above_sibling;
392 
393     g_return_if_fail (c != NULL);
394 
395     TRACE ("client \"%s\" (0x%lx) above (0x%lx)", c->name, c->window, wsibling);
396 
397     if (!FLAG_TEST (c->xfwm_flags, XFWM_FLAG_MANAGED))
398     {
399         return;
400     }
401 
402     screen_info = c->screen_info;
403     display_info = screen_info->display_info;
404 
405     if (c == screen_info->last_raise)
406     {
407         TRACE ("client \"%s\" (0x%lx) already raised", c->name, c->window);
408         return;
409     }
410 
411     if (g_list_length (screen_info->windows_stack) < 2)
412     {
413         return;
414     }
415 
416     /* Search for the window that will be just on top of the raised window  */
417     client_sibling = NULL;
418     if (wsibling)
419     {
420         c2 = myDisplayGetClientFromWindow (display_info, wsibling, SEARCH_FRAME | SEARCH_WINDOW);
421         if (c2)
422         {
423             sibling = g_list_find (screen_info->windows_stack, (gconstpointer) c2);
424             if (sibling)
425             {
426                 above_sibling = g_list_next (sibling);
427                 if (above_sibling)
428                 {
429                     client_sibling = (Client *) above_sibling->data;
430                     /* Do not place window under higher layers though */
431                     if ((client_sibling) && (client_sibling->win_layer < c->win_layer))
432                     {
433                         client_sibling = NULL;
434                     }
435                 }
436             }
437         }
438     }
439 
440     if (!client_sibling)
441     {
442         client_sibling = clientGetNextTopMost (screen_info, c->win_layer, c);
443     }
444 
445     ancestor = clientGetTransientFor(c);
446     clientRaiseInternal (ancestor, client_sibling);
447     if (ancestor != c)
448     {
449         clientRaiseInternal (c, client_sibling);
450     }
451 
452     /* Now, screen_info->windows_stack contains the correct window stack
453        We still need to tell the X Server to reflect the changes
454      */
455     clientApplyStackList (screen_info);
456     clientSetNetClientList (screen_info, display_info->atoms[NET_CLIENT_LIST_STACKING], screen_info->windows_stack);
457     screen_info->last_raise = c;
458 }
459 
460 void
clientLower(Client * c,Window wsibling)461 clientLower (Client * c, Window wsibling)
462 {
463     ScreenInfo *screen_info;
464     DisplayInfo *display_info;
465     Client *c2, *client_sibling;
466     GList *sibling;
467     GList *list;
468     gint position;
469 
470     g_return_if_fail (c != NULL);
471 
472     TRACE ("client \"%s\" (0x%lx) below (0x%lx)", c->name, c->window, wsibling);
473 
474     if (!FLAG_TEST (c->xfwm_flags, XFWM_FLAG_MANAGED))
475     {
476         return;
477     }
478 
479     screen_info = c->screen_info;
480     display_info = screen_info->display_info;
481     client_sibling = NULL;
482     sibling = NULL;
483     c2 = NULL;
484 
485     if (g_list_length (screen_info->windows_stack) < 2)
486     {
487         return;
488     }
489 
490     if (clientIsTransientOrModalForGroup (c))
491     {
492         client_sibling = clientGetTopMostForGroup (c);
493     }
494     else if (clientIsTransient (c))
495     {
496         client_sibling = clientGetTransient (c);
497     }
498     else if (wsibling)
499     {
500         c2 = myDisplayGetClientFromWindow (display_info, wsibling, SEARCH_FRAME | SEARCH_WINDOW);
501         if (c2)
502         {
503             sibling = g_list_find (screen_info->windows_stack, (gconstpointer) c2);
504             if (sibling)
505             {
506                 list = g_list_previous (sibling);
507                 if (list)
508                 {
509                     client_sibling = (Client *) list->data;
510                     /* Do not place window above lower layers though */
511                     if ((client_sibling) && (client_sibling->win_layer > c->win_layer))
512                     {
513                         client_sibling = NULL;
514                     }
515                 }
516             }
517         }
518     }
519 
520     if ((!client_sibling) ||
521         (client_sibling && (client_sibling->win_layer < c->win_layer)))
522     {
523         client_sibling = clientGetBottomMost (screen_info, c->win_layer, c);
524     }
525 
526     if (client_sibling != c)
527     {
528         screen_info->windows_stack = g_list_remove (screen_info->windows_stack, (gconstpointer) c);
529         /* Paranoid check to avoid circular linked list */
530         if (client_sibling)
531         {
532             sibling = g_list_find (screen_info->windows_stack, (gconstpointer) client_sibling);
533             position = g_list_position (screen_info->windows_stack, sibling) + 1;
534 
535             screen_info->windows_stack = g_list_insert (screen_info->windows_stack, c, position);
536             TRACE ("lowest client is \"%s\" (0x%lx) at position %i",
537                     client_sibling->name, client_sibling->window, position);
538         }
539         else
540         {
541             screen_info->windows_stack = g_list_prepend (screen_info->windows_stack, c);
542         }
543     }
544 
545     /* Now, screen_info->windows_stack contains the correct window stack
546        We still need to tell the X Server to reflect the changes
547      */
548     clientApplyStackList (screen_info);
549     clientSetNetClientList (screen_info, display_info->atoms[NET_CLIENT_LIST_STACKING], screen_info->windows_stack);
550     clientPassFocus (screen_info, c, NULL);
551     if (screen_info->last_raise == c)
552     {
553         screen_info->last_raise = NULL;
554     }
555 }
556 
557 gboolean
clientAdjustFullscreenLayer(Client * c,gboolean set)558 clientAdjustFullscreenLayer (Client *c, gboolean set)
559 {
560     g_return_val_if_fail (c, FALSE);
561 
562     TRACE ("%s fullscreen layer for  \"%s\" (0x%lx)", set ? "setting" : "unsetting", c->name, c->window);
563 
564     if (set)
565     {
566         if (FLAG_TEST(c->flags, CLIENT_FLAG_FULLSCREEN))
567         {
568             clientSetLayer (c, WIN_LAYER_FULLSCREEN);
569             return TRUE;
570         }
571     }
572     else if (c->win_layer == WIN_LAYER_FULLSCREEN)
573     {
574         if (FLAG_TEST(c->flags, CLIENT_FLAG_FULLSCREEN))
575         {
576             TRACE ("Moving \"%s\" (0x%lx) to initial layer %d", c->name, c->window, c->pre_fullscreen_layer);
577             clientSetLayer (c, c->pre_fullscreen_layer);
578             return TRUE;
579         }
580     }
581     return FALSE;
582 }
583 
584 void
clientAddToList(Client * c)585 clientAddToList (Client * c)
586 {
587     ScreenInfo *screen_info;
588     DisplayInfo *display_info;
589 
590     g_return_if_fail (c != NULL);
591     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
592 
593     screen_info = c->screen_info;
594     display_info = screen_info->display_info;
595     myDisplayAddClient (display_info, c);
596 
597     screen_info->client_count++;
598     if (screen_info->clients)
599     {
600         c->prev = screen_info->clients->prev;
601         c->next = screen_info->clients;
602         screen_info->clients->prev->next = c;
603         screen_info->clients->prev = c;
604     }
605     else
606     {
607         screen_info->clients = c;
608         c->next = c;
609         c->prev = c;
610     }
611 
612     screen_info->windows = g_list_append (screen_info->windows, c);
613     screen_info->windows_stack = g_list_append (screen_info->windows_stack, c);
614 
615     clientSetNetClientList (screen_info, display_info->atoms[NET_CLIENT_LIST], screen_info->windows);
616 
617     FLAG_SET (c->xfwm_flags, XFWM_FLAG_MANAGED);
618 }
619 
620 void
clientRemoveFromList(Client * c)621 clientRemoveFromList (Client * c)
622 {
623     ScreenInfo *screen_info;
624     DisplayInfo *display_info;
625 
626     g_return_if_fail (c != NULL);
627     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
628 
629     FLAG_UNSET (c->xfwm_flags, XFWM_FLAG_MANAGED);
630 
631     screen_info = c->screen_info;
632     display_info = screen_info->display_info;
633     myDisplayRemoveClient (display_info, c);
634 
635     g_assert (screen_info->client_count > 0);
636     screen_info->client_count--;
637     if (screen_info->client_count == 0)
638     {
639         screen_info->clients = NULL;
640     }
641     else
642     {
643         c->next->prev = c->prev;
644         c->prev->next = c->next;
645         if (c == screen_info->clients)
646         {
647             screen_info->clients = screen_info->clients->next;
648         }
649     }
650 
651     screen_info->windows = g_list_remove (screen_info->windows, c);
652     screen_info->windows_stack = g_list_remove (screen_info->windows_stack, c);
653 
654     clientSetNetClientList (screen_info, display_info->atoms[NET_CLIENT_LIST], screen_info->windows);
655     clientSetNetClientList (screen_info, display_info->atoms[NET_CLIENT_LIST_STACKING], screen_info->windows_stack);
656 
657     FLAG_UNSET (c->xfwm_flags, XFWM_FLAG_MANAGED);
658 }
659 
660 GList *
clientGetStackList(ScreenInfo * screen_info)661 clientGetStackList (ScreenInfo *screen_info)
662 {
663     g_return_val_if_fail (screen_info, NULL);
664 
665     if (screen_info->windows_stack)
666     {
667         return g_list_copy (screen_info->windows_stack);
668     }
669     return NULL;
670 }
671 
672 void
clientSetLastRaise(Client * c)673 clientSetLastRaise (Client *c)
674 {
675     ScreenInfo *screen_info;
676 
677     g_return_if_fail (c != NULL);
678 
679     screen_info = c->screen_info;
680     screen_info->last_raise = c;
681 }
682 
683 Client *
clientGetLastRaise(ScreenInfo * screen_info)684 clientGetLastRaise (ScreenInfo *screen_info)
685 {
686     g_return_val_if_fail (screen_info, NULL);
687     return (screen_info->last_raise);
688 }
689 
690 void
clientClearLastRaise(ScreenInfo * screen_info)691 clientClearLastRaise (ScreenInfo *screen_info)
692 {
693     g_return_if_fail (screen_info);
694     screen_info->last_raise = NULL;
695 }
696 
697 static gboolean
delayed_raise_cb(gpointer data)698 delayed_raise_cb (gpointer data)
699 {
700     Client *c;
701 
702     clientClearDelayedRaise ();
703     c = clientGetFocus ();
704 
705     if (c)
706     {
707         TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
708         clientRaise (c, None);
709         clientSetLastRaise (c);
710     }
711     return (FALSE);
712 }
713 
714 void
clientClearDelayedRaise(void)715 clientClearDelayedRaise (void)
716 {
717     if (raise_timeout)
718     {
719         g_source_remove (raise_timeout);
720         raise_timeout = 0;
721     }
722 }
723 
724 void
clientResetDelayedRaise(ScreenInfo * screen_info)725 clientResetDelayedRaise (ScreenInfo *screen_info)
726 {
727     if (raise_timeout)
728     {
729         g_source_remove (raise_timeout);
730     }
731     raise_timeout = g_timeout_add_full (G_PRIORITY_DEFAULT,
732                                         screen_info->params->raise_delay,
733                                         delayed_raise_cb,
734                                         NULL, NULL);
735 }
736 
737