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