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         oroborus - (c) 2001 Ken Lynch
20         xfwm4    - (c) 2002-2020 Olivier Fourdan
21 
22  */
23 
24 #ifdef HAVE_CONFIG_H
25 #include "config.h"
26 #endif
27 
28 #include <sys/types.h>
29 #include <signal.h>
30 #include <unistd.h>
31 #include <errno.h>
32 
33 #include <X11/X.h>
34 #include <X11/Xlib.h>
35 #include <X11/Xutil.h>
36 #include <X11/Xatom.h>
37 #include <X11/extensions/shape.h>
38 
39 #include <glib.h>
40 #include <gdk/gdk.h>
41 #include <gdk/gdkx.h>
42 #include <gtk/gtk.h>
43 #include <libxfce4util/libxfce4util.h>
44 
45 #include <common/xfwm-common.h>
46 
47 #include "client.h"
48 #include "compositor.h"
49 #include "focus.h"
50 #include "frame.h"
51 #include "hints.h"
52 #include "icons.h"
53 #include "misc.h"
54 #include "moveresize.h"
55 #include "mypixmap.h"
56 #include "mywindow.h"
57 #include "netwm.h"
58 #include "placement.h"
59 #include "screen.h"
60 #include "session.h"
61 #include "settings.h"
62 #include "stacking.h"
63 #include "startup_notification.h"
64 #include "transients.h"
65 #include "workspaces.h"
66 #include "xsync.h"
67 #include "event_filter.h"
68 
69 /* Event mask definition */
70 
71 #define POINTER_EVENT_MASK \
72     ButtonPressMask|\
73     ButtonReleaseMask
74 
75 #define FRAME_EVENT_MASK \
76     SubstructureNotifyMask|\
77     SubstructureRedirectMask|\
78     PointerMotionMask|\
79     ButtonMotionMask|\
80     FocusChangeMask|\
81     EnterWindowMask|\
82     PropertyChangeMask
83 
84 #define CLIENT_EVENT_MASK \
85     StructureNotifyMask|\
86     FocusChangeMask|\
87     PropertyChangeMask
88 
89 #define BUTTON_EVENT_MASK \
90     EnterWindowMask|\
91     LeaveWindowMask
92 
93 /* Useful macros */
94 #define START_ICONIC(c) \
95     ((c->wmhints) && \
96      (c->wmhints->initial_state == IconicState) && \
97      !clientIsTransientOrModal (c))
98 
99 #define OPACITY_SET_STEP        (guint) 0x16000000
100 #define OPACITY_SET_MIN         (guint) 0x40000000
101 
102 typedef struct _ButtonPressData ButtonPressData;
103 struct _ButtonPressData
104 {
105     int b;
106     Client *c;
107 };
108 
109 /* Forward decl */
110 static void
111 clientUpdateIconPix (Client *c);
112 static gboolean
113 clientNewMaxSize (Client *c, XWindowChanges *wc, GdkRectangle *);
114 
115 Display *
clientGetXDisplay(Client * c)116 clientGetXDisplay (Client *c)
117 {
118     g_return_val_if_fail (c, NULL);
119 
120     return myScreenGetXDisplay (c->screen_info);
121 }
122 
123 void
clientInstallColormaps(Client * c)124 clientInstallColormaps (Client *c)
125 {
126     ScreenInfo *screen_info;
127     DisplayInfo *display_info;
128     XWindowAttributes attr;
129     gboolean installed;
130     int i;
131 
132     g_return_if_fail (c != NULL);
133     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
134 
135     screen_info = c->screen_info;
136     display_info = screen_info->display_info;
137 
138     myDisplayErrorTrapPush (display_info);
139 
140     installed = FALSE;
141     if (c->ncmap)
142     {
143         for (i = c->ncmap - 1; i >= 0; i--)
144         {
145             XGetWindowAttributes (display_info->dpy, c->cmap_windows[i], &attr);
146             XInstallColormap (display_info->dpy, attr.colormap);
147             if (c->cmap_windows[i] == c->window)
148             {
149                 installed = TRUE;
150             }
151         }
152     }
153     if ((!installed) && (c->cmap))
154     {
155         XInstallColormap (display_info->dpy, c->cmap);
156     }
157 
158     myDisplayErrorTrapPopIgnored (display_info);
159 }
160 
161 void
clientUpdateColormaps(Client * c)162 clientUpdateColormaps (Client *c)
163 {
164     ScreenInfo *screen_info;
165     DisplayInfo *display_info;
166     int result, status;
167 
168     g_return_if_fail (c != NULL);
169     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
170 
171     if (c->ncmap)
172     {
173         XFree (c->cmap_windows);
174         c->ncmap = 0;
175     }
176 
177     screen_info = c->screen_info;
178     display_info = screen_info->display_info;
179 
180     myDisplayErrorTrapPush (display_info);
181     status = XGetWMColormapWindows (display_info->dpy, c->window,
182                                     &c->cmap_windows, &c->ncmap);
183     result = myDisplayErrorTrapPop (display_info);
184     if ((result != Success) || !status)
185     {
186         c->cmap_windows = NULL;
187         c->ncmap = 0;
188     }
189 }
190 
191 static gchar*
clientCreateTitleName(Client * c,gchar * name,gchar * hostname)192 clientCreateTitleName (Client *c, gchar *name, gchar *hostname)
193 {
194     ScreenInfo *screen_info;
195     DisplayInfo *display_info;
196     gchar *title;
197 
198     g_return_val_if_fail (c != NULL, NULL);
199     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
200 
201     screen_info = c->screen_info;
202     display_info = screen_info->display_info;
203 
204     if (strlen (hostname) && (display_info->hostname) && (g_ascii_strcasecmp (display_info->hostname, hostname)))
205     {
206         /* TRANSLATORS: "(on %s)" is like "running on" the name of the other host */
207         title = g_strdup_printf (_("%s (on %s)"), name, hostname);
208     }
209     else
210     {
211         title = g_strdup (name);
212     }
213 
214     return title;
215 }
216 
217 void
clientUpdateName(Client * c)218 clientUpdateName (Client *c)
219 {
220     ScreenInfo *screen_info;
221     DisplayInfo *display_info;
222     gchar *hostname;
223     gchar *wm_name;
224     gchar *name;
225     gboolean refresh;
226 
227     g_return_if_fail (c != NULL);
228     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
229 
230     screen_info = c->screen_info;
231     display_info = screen_info->display_info;
232 
233     getWindowName (display_info, c->window, &wm_name);
234     getWindowHostname (display_info, c->window, &hostname);
235     refresh = FALSE;
236 
237     /* Update hostname too, as it's used when terminating a client */
238     if (hostname)
239     {
240         if (c->hostname)
241         {
242             g_free (c->hostname);
243         }
244         c->hostname = hostname;
245     }
246 
247     if (wm_name)
248     {
249         name = clientCreateTitleName (c, wm_name, hostname);
250         g_free (wm_name);
251         if (c->name)
252         {
253             if (strcmp (name, c->name))
254             {
255                 refresh = TRUE;
256                 FLAG_SET (c->flags, CLIENT_FLAG_NAME_CHANGED);
257             }
258             g_free (c->name);
259         }
260         c->name = name;
261     }
262 
263     if (refresh)
264     {
265         frameQueueDraw (c, TRUE);
266     }
267 }
268 
269 static void
clientRecomputeMaximizeSize(Client * c)270 clientRecomputeMaximizeSize (Client *c)
271 {
272     unsigned long maximization_flags = 0L;
273 
274     g_return_if_fail (c != NULL);
275     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
276 
277     /* Recompute size and position of maximized windows */
278     maximization_flags = c->flags & CLIENT_FLAG_MAXIMIZED;
279 
280     /* Force an update by clearing the internal flags */
281     FLAG_UNSET (c->flags, CLIENT_FLAG_MAXIMIZED);
282     clientToggleMaximized (c, maximization_flags, FALSE);
283 }
284 
285 void
clientUpdateAllFrames(ScreenInfo * screen_info,int mask)286 clientUpdateAllFrames (ScreenInfo *screen_info, int mask)
287 {
288     Client *c;
289     guint i;
290 
291     g_return_if_fail (screen_info != NULL);
292     TRACE ("entering");
293 
294     for (c = screen_info->clients, i = 0; i < screen_info->client_count; c = c->next, i++)
295     {
296         unsigned short configure_flags = 0;
297 
298         if (mask & UPDATE_BUTTON_GRABS)
299         {
300             clientUngrabButtons (c);
301             clientGrabButtons (c);
302             clientGrabMouseButton (c);
303         }
304         if (mask & UPDATE_CACHE)
305         {
306             clientUpdateIconPix (c);
307         }
308         if (mask & UPDATE_GRAVITY)
309         {
310             clientCoordGravitate (c, c->gravity, REMOVE, &c->x, &c->y);
311             clientCoordGravitate (c, c->gravity, APPLY, &c->x, &c->y);
312             setNetFrameExtents (screen_info->display_info,
313                                 c->window,
314                                 frameTop (c),
315                                 frameLeft (c),
316                                 frameRight (c),
317                                 frameBottom (c));
318             configure_flags |= CFG_FORCE_REDRAW;
319             mask &= ~UPDATE_FRAME;
320         }
321         if (mask & UPDATE_MAXIMIZE)
322         {
323             /* Recompute size and position of maximized windows */
324             if (FLAG_TEST (c->flags, CLIENT_FLAG_MAXIMIZED))
325             {
326                 clientRecomputeMaximizeSize (c);
327 
328                 configure_flags |= CFG_FORCE_REDRAW;
329                 mask &= ~UPDATE_FRAME;
330             }
331         }
332         if (configure_flags != 0L)
333         {
334             clientReconfigure (c, configure_flags);
335         }
336         if (mask & UPDATE_FRAME)
337         {
338             frameQueueDraw (c, TRUE);
339         }
340 
341     }
342 }
343 
344 void
clientGrabButtons(Client * c)345 clientGrabButtons (Client *c)
346 {
347     ScreenInfo *screen_info;
348 
349     g_return_if_fail (c != NULL);
350     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
351 
352     screen_info = c->screen_info;
353     if (screen_info->params->easy_click)
354     {
355         grabButton (c->screen_info->display_info->devices, clientGetXDisplay (c),
356                     AnyButton, screen_info->params->easy_click, c->window);
357     }
358 }
359 
360 void
clientUngrabButtons(Client * c)361 clientUngrabButtons (Client *c)
362 {
363     g_return_if_fail (c != NULL);
364     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
365     xfwm_device_ungrab_button (c->screen_info->display_info->devices, clientGetXDisplay (c),
366                                AnyButton, AnyModifier, c->window);
367 }
368 
369 static gboolean
urgent_cb(gpointer data)370 urgent_cb (gpointer data)
371 {
372     Client *c;
373     ScreenInfo *screen_info;
374 
375     c = (Client *) data;
376     g_return_val_if_fail (c != NULL, FALSE);
377     TRACE ("iteration %i", c->blink_iterations);
378 
379     screen_info = c->screen_info;
380     if (c != clientGetFocus ())
381     {
382         /*
383          * If we do not blink on urgency, check if the window was last
384          * drawn focused and redraw it unfocused.
385          * This is for th case when the tuser changes the settings
386          * in between two redraws.
387          */
388         if (!screen_info->params->urgent_blink)
389         {
390             if (FLAG_TEST (c->xfwm_flags, XFWM_FLAG_SEEN_ACTIVE))
391             {
392                 FLAG_TOGGLE (c->xfwm_flags, XFWM_FLAG_SEEN_ACTIVE);
393                 frameQueueDraw (c, FALSE);
394             }
395 
396             if (c->blink_iterations)
397             {
398                 c->blink_iterations = 0;
399             }
400             return TRUE;
401         }
402         /*
403          * If we blink on urgency, check if we've not reach the number
404          * of iterations and if not, simply change the status and redraw
405          */
406         if (c->blink_iterations < (2 * MAX_BLINK_ITERATIONS))
407         {
408             c->blink_iterations++;
409             FLAG_TOGGLE (c->xfwm_flags, XFWM_FLAG_SEEN_ACTIVE);
410             frameQueueDraw (c, FALSE);
411             return TRUE;
412         }
413         /*
414          * If we reached the max number of iterations, check if we
415          * repeat. If repeat_urgent_blink is set, redraw the frame and
416          * restart counting from 1
417          */
418         if (screen_info->params->repeat_urgent_blink)
419         {
420             FLAG_TOGGLE (c->xfwm_flags, XFWM_FLAG_SEEN_ACTIVE);
421             frameQueueDraw (c, FALSE);
422             c->blink_iterations = 1;
423             return TRUE;
424         }
425     }
426     else if (c->blink_iterations)
427     {
428         c->blink_iterations = 0;
429     }
430     return (TRUE);
431 }
432 
433 void
clientUpdateUrgency(Client * c)434 clientUpdateUrgency (Client *c)
435 {
436     g_return_if_fail (c != NULL);
437     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
438 
439     FLAG_UNSET (c->xfwm_flags, XFWM_FLAG_SEEN_ACTIVE);
440     if (c->blink_timeout_id)
441     {
442         g_source_remove (c->blink_timeout_id);
443         frameQueueDraw (c, FALSE);
444     }
445     FLAG_UNSET (c->wm_flags, WM_FLAG_URGENT);
446 
447     c->blink_timeout_id = 0;
448     c->blink_iterations = 0;
449     if ((c->wmhints) && (c->wmhints->flags & XUrgencyHint))
450     {
451         FLAG_SET (c->wm_flags, WM_FLAG_URGENT);
452         if (FLAG_TEST (c->xfwm_flags, XFWM_FLAG_VISIBLE))
453         {
454             c->blink_timeout_id =
455                 g_timeout_add_full (G_PRIORITY_DEFAULT,
456                                     CLIENT_BLINK_TIMEOUT,
457                                     urgent_cb, c, NULL);
458         }
459     }
460     if (FLAG_TEST (c->xfwm_flags, XFWM_FLAG_SEEN_ACTIVE)
461         && !FLAG_TEST (c->wm_flags, WM_FLAG_URGENT)
462         && (c != clientGetFocus ()))
463     {
464         FLAG_UNSET (c->xfwm_flags, XFWM_FLAG_SEEN_ACTIVE);
465         frameQueueDraw (c, FALSE);
466     }
467 }
468 
469 /* ICCCM Section 4 - Client to Window Manager Communication
470  *
471  * The win_gravity may be any of the values specified for WINGRAVITY in the core
472  * protocol  except  for  Unmap  :  NorthWest (1),  North  (2),  NorthEast  (3),
473  * West (4), Center (5), East (6), SouthWest (7), South (8),  and SouthEast (9).
474  * It specifies how  and whether the client  window wants to be shifted to  make
475  * room for the window manager frame.
476  *
477  * If the win_gravity is Static , the window manager frame is positioned so that
478  * the  inside  border of  the  client  window  inside the frame  is in the same
479  * position  on the screen  as it was when the client  requested  the transition
480  * from Withdrawn state.  Other values of win_gravity specify a window reference
481  * point.  For NorthWest ,  NorthEast ,  SouthWest , and SouthEast the reference
482  * point  is  the  specified outer  corner of the window  (on the outside border
483  * edge).  For North , South , East , and West the reference point is the center
484  * of the specified  outer edge of the window border.   For Center the reference
485  * point is the center of the window.  The reference point of the window manager
486  * frame is placed  at the location on the screen  where the reference point  of
487  * the client window was when the client requested the transition from Withdrawn
488  * state.
489  */
490 
491 void
clientCoordGravitate(Client * c,int gravity,int mode,int * x,int * y)492 clientCoordGravitate (Client *c, int gravity, int mode, int *x, int *y)
493 {
494     int dx, dy;
495 
496     g_return_if_fail (c != NULL);
497     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
498 
499     switch (gravity)
500     {
501         case CenterGravity:
502             dx = (frameLeft (c) - frameRight (c) + 1) / 2;
503             dy = (frameTop (c) - frameBottom (c) + 1) / 2;
504             break;
505         case NorthGravity:
506             dx = (frameLeft (c) - frameRight (c) + 1) / 2;
507             dy = frameTop (c);
508             break;
509         case SouthGravity:
510             dx = (frameLeft (c) - frameRight (c) + 1) / 2;
511             dy = - frameBottom (c);
512             break;
513         case EastGravity:
514             dx = - frameRight (c);
515             dy = (frameTop (c) - frameBottom (c) + 1) / 2;
516             break;
517         case WestGravity:
518             dx = frameLeft (c);
519             dy = (frameTop (c) - frameBottom (c) + 1) / 2;
520             break;
521         case NorthWestGravity:
522             dx = frameLeft (c);
523             dy = frameTop (c);
524             break;
525         case NorthEastGravity:
526             dx = - frameRight (c);
527             dy = frameTop (c);
528             break;
529         case SouthWestGravity:
530             dx = frameLeft (c);
531             dy = - frameBottom (c);
532             break;
533         case SouthEastGravity:
534             dx = - frameRight (c);
535             dy = - frameBottom (c);
536             break;
537         case StaticGravity:
538         default:
539             dx = 0;
540             dy = 0;
541             break;
542     }
543     *x = *x + (dx * mode);
544     *y = *y + (dy * mode);
545 }
546 
547 void
clientAdjustCoordGravity(Client * c,int gravity,XWindowChanges * wc,unsigned long * mask)548 clientAdjustCoordGravity (Client *c, int gravity, XWindowChanges *wc, unsigned long *mask)
549 {
550     int tx, ty, dw, dh;
551 
552     g_return_if_fail (c != NULL);
553     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
554 
555     tx = wc->x;
556     ty = wc->y;
557 
558     if (*mask & (CWX | CWY))
559     {
560         clientCoordGravitate (c, gravity, APPLY, &tx, &ty);
561     }
562 
563     if (*mask & CWWidth)
564     {
565         wc->width = clientCheckWidth (c, wc->width, TRUE);
566     }
567 
568     if (*mask & CWHeight)
569     {
570         wc->height = clientCheckHeight (c, wc->height, TRUE);
571     }
572 
573     switch (gravity)
574     {
575         case CenterGravity:
576             dw = (c->width - wc->width) / 2;
577             dh = (c->height - wc->height) / 2;
578             break;
579         case NorthGravity:
580             dw = (c->width - wc->width) / 2;
581             dh = 0;
582             break;
583         case SouthGravity:
584             dw = (c->width  - wc->width) / 2;
585             dh = (c->height - wc->height);
586             break;
587         case EastGravity:
588             dw = (c->width  - wc->width);
589             dh = (c->height - wc->height) / 2;
590             break;
591         case WestGravity:
592             dw = 0;
593             dh = (c->height - wc->height) / 2;
594             break;
595         case NorthWestGravity:
596             dw = 0;
597             dh = 0;
598             break;
599         case NorthEastGravity:
600             dw = (c->width - wc->width);
601             dh = 0;
602             break;
603         case SouthWestGravity:
604             dw = 0;
605             dh = (c->height - wc->height);
606             break;
607         case SouthEastGravity:
608             dw = (c->width  - wc->width);
609             dh = (c->height - wc->height);
610             break;
611         case StaticGravity:
612         default:
613             dw = 0;
614             dh = 0;
615             break;
616     }
617 
618     if (*mask & CWX)
619     {
620         wc->x = tx;
621     }
622     else if (*mask & CWWidth)
623     {
624         wc->x = c->x + dw;
625         *mask |= CWX;
626     }
627 
628     if (*mask & CWY)
629     {
630         wc->y = ty;
631     }
632     else if (*mask & CWHeight)
633     {
634         wc->y = c->y + dh;
635         *mask |= CWY;
636     }
637 }
638 
639 static void
clientConfigureWindows(Client * c,XWindowChanges * wc,unsigned long mask,unsigned short flags)640 clientConfigureWindows (Client *c, XWindowChanges * wc, unsigned long mask, unsigned short flags)
641 {
642     unsigned long change_mask_frame, change_mask_client;
643     XWindowChanges change_values;
644     DisplayInfo *display_info;
645     ScreenInfo *screen_info;
646 
647     screen_info = c->screen_info;
648     display_info = screen_info->display_info;
649 
650     change_mask_frame = mask & (CWX | CWY | CWWidth | CWHeight);
651     change_mask_client = mask & (CWWidth | CWHeight);
652 
653     if ((mask & (CWWidth | CWHeight)) || (flags & CFG_FORCE_REDRAW))
654     {
655         frameDraw (c, (flags & CFG_FORCE_REDRAW));
656     }
657 
658     if (flags & CFG_FORCE_REDRAW)
659     {
660         change_mask_client |= (CWX | CWY);
661     }
662 
663     myDisplayErrorTrapPush (display_info);
664     if (change_mask_frame & (CWX | CWY | CWWidth | CWHeight))
665     {
666         change_values.x = frameX (c);
667         change_values.y = frameY (c);
668         change_values.width = frameWidth (c);
669         change_values.height = frameHeight (c);
670         XConfigureWindow (display_info->dpy, c->frame, change_mask_frame, &change_values);
671     }
672 
673     if (change_mask_client & (CWX | CWY | CWWidth | CWHeight))
674     {
675         change_values.x = frameLeft (c);
676         change_values.y = frameTop (c);
677         change_values.width = c->width;
678         change_values.height = c->height;
679         XConfigureWindow (display_info->dpy, c->window, change_mask_client, &change_values);
680     }
681     myDisplayErrorTrapPopIgnored (display_info);
682 }
683 
684 void
clientSendConfigureNotify(Client * c)685 clientSendConfigureNotify (Client *c)
686 {
687     XConfigureEvent ce;
688     DisplayInfo *display_info;
689     ScreenInfo *screen_info;
690 
691     g_return_if_fail (c != NULL);
692     g_return_if_fail (c->window != None);
693 
694     screen_info = c->screen_info;
695     display_info = screen_info->display_info;
696 
697     DBG ("Sending ConfigureNotify");
698     ce.type = ConfigureNotify;
699     ce.display = display_info->dpy;
700     ce.send_event = TRUE;
701     ce.event = c->window;
702     ce.window = c->window;
703     ce.x = c->x;
704     ce.y = c->y;
705     ce.width = c->width;
706     ce.height = c->height;
707     ce.border_width = 0;
708     ce.above = None;
709     ce.override_redirect = FALSE;
710 
711     myDisplayErrorTrapPush (display_info);
712     XSendEvent (display_info->dpy, c->window, TRUE,
713                 StructureNotifyMask, (XEvent *) & ce);
714     myDisplayErrorTrapPopIgnored (display_info);
715 }
716 
717 void
clientConfigure(Client * c,XWindowChanges * wc,unsigned long mask,unsigned short flags)718 clientConfigure (Client *c, XWindowChanges * wc, unsigned long mask, unsigned short flags)
719 {
720     int px, py, pwidth, pheight;
721     gboolean win_moved, win_resized;
722 
723     g_return_if_fail (c != NULL);
724     g_return_if_fail (c->window != None);
725 
726     TRACE ("client \"%s\" (0x%lx) %s, type %u", c->name, c->window,
727            flags & CFG_CONSTRAINED ? "constrained" : "not contrained", c->type);
728 
729     px = c->x;
730     py = c->y;
731     pwidth = c->width;
732     pheight = c->height;
733 
734     if (mask & CWX)
735     {
736         if (!FLAG_TEST (c->xfwm_flags, XFWM_FLAG_MOVING_RESIZING))
737         {
738             c->x = wc->x;
739         }
740     }
741     if (mask & CWY)
742     {
743         if (!FLAG_TEST (c->xfwm_flags, XFWM_FLAG_MOVING_RESIZING))
744         {
745             c->y = wc->y;
746         }
747     }
748     if (mask & CWWidth)
749     {
750         c->width = clientCheckWidth (c, wc->width, flags & CFG_REQUEST);
751     }
752     if (mask & CWHeight)
753     {
754         c->height = clientCheckHeight (c, wc->height, flags & CFG_REQUEST);
755     }
756     if (mask & CWBorderWidth)
757     {
758         c->border_width = wc->border_width;
759     }
760     if (mask & CWStackMode)
761     {
762         switch (wc->stack_mode)
763         {
764             /*
765              * Limitation: we don't support neither
766              * TopIf, BottomIf nor Opposite ...
767              */
768             case Above:
769                 TRACE ("above");
770                 if (mask & CWSibling)
771                 {
772                     clientRaise (c, wc->sibling);
773                 }
774                 else
775                 {
776                     clientRaise (c, None);
777                 }
778                 break;
779             case Below:
780                 TRACE ("below");
781                 if (mask & CWSibling)
782                 {
783                     clientLower (c, wc->sibling);
784                 }
785                 else
786                 {
787                     clientLower (c, None);
788                 }
789 
790                 break;
791             case Opposite:
792             case TopIf:
793             case BottomIf:
794             default:
795                 break;
796         }
797     }
798     mask &= ~(CWStackMode | CWSibling);
799 
800     /* Keep control over what the application does. */
801     if (((flags & (CFG_CONSTRAINED | CFG_REQUEST)) == (CFG_CONSTRAINED | CFG_REQUEST))
802          && CONSTRAINED_WINDOW (c))
803     {
804         clientConstrainPos (c, flags & CFG_KEEP_VISIBLE);
805 
806         if (c->x != px)
807         {
808             mask |= CWX;
809         }
810         else
811         {
812             mask &= ~CWX;
813         }
814 
815         if (c->y != py)
816         {
817             mask |= CWY;
818         }
819         else
820         {
821             mask &= ~CWY;
822         }
823 
824         if (c->width != pwidth)
825         {
826             mask |= CWWidth;
827         }
828         else
829         {
830             mask &= ~CWWidth;
831         }
832 
833         if (c->height != pheight)
834         {
835             mask |= CWHeight;
836         }
837         else
838         {
839             mask &= ~CWHeight;
840         }
841     }
842 
843     clientConfigureWindows (c, wc, mask, flags);
844     /*
845 
846       We reparent the client window. According to the ICCCM spec, the
847       WM must send a senthetic event when the window is moved and not resized.
848 
849       But, since we reparent the window, we must also send a synthetic
850       configure event when the window is moved and resized.
851 
852       See this thread for the rational:
853       http://www.mail-archive.com/wm-spec-list@gnome.org/msg00379.html
854 
855       And specifically this post from Carsten Haitzler:
856       http://www.mail-archive.com/wm-spec-list@gnome.org/msg00382.html
857 
858      */
859     win_moved = (c->x != c->applied_geometry.x ||
860                  c->y != c->applied_geometry.y);
861     win_resized = (c->width != c->applied_geometry.width ||
862                    c->height != c->applied_geometry.height);
863 
864     if ((win_moved) || (flags & (CFG_NOTIFY | CFG_FORCE_REDRAW)) ||
865         ((flags & CFG_REQUEST) && !(win_moved || win_resized)))
866     {
867         clientSendConfigureNotify (c);
868     }
869 
870     c->applied_geometry.x = c->x;
871     c->applied_geometry.y = c->y;
872     c->applied_geometry.width = c->width;
873     c->applied_geometry.height = c->height;
874 }
875 
876 void
clientReconfigure(Client * c,unsigned short flags)877 clientReconfigure (Client *c, unsigned short flags)
878 {
879     XWindowChanges wc;
880 
881     g_return_if_fail (c != NULL);
882     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
883 
884     wc.x = c->x;
885     wc.y = c->y;
886     wc.width = c->width;
887     wc.height = c->height;
888     clientConfigure (c, &wc, CWX | CWY | CWWidth | CWHeight, flags);
889 }
890 
891 void
clientMoveResizeWindow(Client * c,XWindowChanges * wc,unsigned long mask)892 clientMoveResizeWindow (Client *c, XWindowChanges * wc, unsigned long mask)
893 {
894     ScreenInfo *screen_info;
895     DisplayInfo *display_info;
896     unsigned short flags;
897 
898     g_return_if_fail (c != NULL);
899     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
900 
901     screen_info = c->screen_info;
902     display_info = screen_info->display_info;
903     if (c->type == WINDOW_DESKTOP)
904     {
905         /* Ignore stacking request for DESKTOP windows */
906         mask &= ~(CWSibling | CWStackMode);
907     }
908     if (FLAG_TEST (c->flags, CLIENT_FLAG_FULLSCREEN)
909         || (FLAG_TEST_ALL (c->flags, CLIENT_FLAG_MAXIMIZED)
910             && (c->screen_info->params->borderless_maximize)))
911     {
912         /* Not allowed in fullscreen mode */
913         mask &= ~(CWX | CWY | CWWidth | CWHeight);
914     }
915     /* clean up buggy requests that set all flags */
916     if ((mask & CWX) && (wc->x == c->x))
917     {
918         mask &= ~CWX;
919     }
920     if ((mask & CWY) && (wc->y == c->y))
921     {
922         mask &= ~CWY;
923     }
924     if ((mask & CWWidth) && (wc->width == c->width))
925     {
926         mask &= ~CWWidth;
927     }
928     if ((mask & CWHeight) && (wc->height == c->height))
929     {
930         mask &= ~CWHeight;
931     }
932 
933     /* Still a move/resize after cleanup? */
934     flags = CFG_REQUEST;
935     if (mask & (CWX | CWY | CWWidth | CWHeight))
936     {
937         /* Clear any previously saved pos flag from screen resize */
938         FLAG_UNSET (c->xfwm_flags, XFWM_FLAG_SAVED_POS);
939 
940         if (FLAG_TEST (c->flags, CLIENT_FLAG_MAXIMIZED))
941         {
942             clientRemoveMaximizeFlag (c);
943             flags |= CFG_FORCE_REDRAW;
944         }
945 
946         flags |= CFG_CONSTRAINED;
947     }
948     if ((mask & (CWWidth | CWHeight)) && !(mask & (CWX | CWY)))
949     {
950         /*
951          * The client is resizing its window, but did not specify a
952          * position, make sure the window remains fully visible in that
953          *case so that the user does not have to relocate the window
954          */
955         flags |= CFG_KEEP_VISIBLE;
956     }
957     /*
958      * Let's say that if the client performs a XRaiseWindow, we show the window if focus
959      * stealing prevention is not activated, otherwise we just set the "demands attention"
960      * flag...
961      */
962     if ((mask & CWStackMode) && (wc->stack_mode == Above) && (wc->sibling == None) && !(c->type & WINDOW_TYPE_DONT_FOCUS))
963     {
964         Client *last_raised;
965 
966         last_raised = clientGetLastRaise (screen_info);
967         if (last_raised && (c != last_raised))
968         {
969             if ((screen_info->params->prevent_focus_stealing) && (screen_info->params->activate_action == ACTIVATE_ACTION_NONE))
970             {
971                 mask &= ~(CWSibling | CWStackMode);
972                 TRACE ("setting WM_STATE_DEMANDS_ATTENTION flag on \"%s\" (0x%lx)", c->name, c->window);
973                 FLAG_SET (c->flags, CLIENT_FLAG_DEMANDS_ATTENTION);
974                 clientSetNetState (c);
975             }
976             else
977             {
978                 clientActivate (c, getXServerTime (display_info), TRUE);
979             }
980         }
981     }
982     /* And finally, configure the window */
983     clientConfigure (c, wc, mask, flags);
984 }
985 
986 void
clientGetMWMHints(Client * c)987 clientGetMWMHints (Client *c)
988 {
989     ScreenInfo *screen_info;
990     DisplayInfo *display_info;
991 
992     g_return_if_fail (c != NULL);
993     g_return_if_fail (c->window != None);
994     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
995 
996     screen_info = c->screen_info;
997     display_info = screen_info->display_info;
998 
999     if (c->mwm_hints)
1000     {
1001         g_free (c->mwm_hints);
1002     }
1003     c->mwm_hints = getMotifHints (display_info, c->window);
1004 }
1005 
1006 void
clientApplyMWMHints(Client * c,gboolean update)1007 clientApplyMWMHints (Client *c, gboolean update)
1008 {
1009     ScreenInfo *screen_info;
1010     DisplayInfo *display_info;
1011     XWindowChanges wc;
1012 
1013     g_return_if_fail (c != NULL);
1014     g_return_if_fail (c->window != None);
1015     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
1016 
1017     screen_info = c->screen_info;
1018     display_info = screen_info->display_info;
1019 
1020     if (c->mwm_hints)
1021     {
1022         if ((c->mwm_hints->flags & MWM_HINTS_DECORATIONS))
1023         {
1024             if (!FLAG_TEST (c->flags, CLIENT_FLAG_HAS_SHAPE))
1025             {
1026                 if (c->mwm_hints->decorations & MWM_DECOR_ALL)
1027                 {
1028                     FLAG_SET (c->xfwm_flags, XFWM_FLAG_HAS_BORDER | XFWM_FLAG_HAS_MENU);
1029                 }
1030                 else
1031                 {
1032                     FLAG_UNSET (c->xfwm_flags, XFWM_FLAG_HAS_BORDER | XFWM_FLAG_HAS_MENU);
1033 
1034                     if (c->mwm_hints-> decorations & (MWM_DECOR_TITLE | MWM_DECOR_BORDER))
1035                     {
1036                         FLAG_SET (c->xfwm_flags,  XFWM_FLAG_HAS_BORDER);
1037                     }
1038                     if (c->mwm_hints->decorations & MWM_DECOR_MENU)
1039                     {
1040                         FLAG_SET (c->xfwm_flags, XFWM_FLAG_HAS_MENU);
1041                     }
1042                     if (c->mwm_hints->decorations & MWM_DECOR_MINIMIZE)
1043                     {
1044                         FLAG_SET (c->xfwm_flags, XFWM_FLAG_HAS_HIDE);
1045                     }
1046                     if (c->mwm_hints->decorations & MWM_DECOR_MAXIMIZE)
1047                     {
1048                         FLAG_SET (c->xfwm_flags, XFWM_FLAG_HAS_MAXIMIZE);
1049                     }
1050                 }
1051             }
1052         }
1053         /* The following is from Metacity : */
1054         if (c->mwm_hints->flags & MWM_HINTS_FUNCTIONS)
1055         {
1056             if (!(c->mwm_hints->functions & MWM_FUNC_ALL))
1057             {
1058                 FLAG_UNSET (c->xfwm_flags,
1059                     XFWM_FLAG_HAS_CLOSE | XFWM_FLAG_HAS_HIDE |
1060                     XFWM_FLAG_HAS_MAXIMIZE | XFWM_FLAG_HAS_MOVE |
1061                     XFWM_FLAG_HAS_RESIZE);
1062             }
1063             else
1064             {
1065                 FLAG_SET (c->xfwm_flags,
1066                     XFWM_FLAG_HAS_CLOSE | XFWM_FLAG_HAS_HIDE |
1067                     XFWM_FLAG_HAS_MAXIMIZE | XFWM_FLAG_HAS_MOVE |
1068                     XFWM_FLAG_HAS_RESIZE);
1069             }
1070 
1071             if (c->mwm_hints->functions & MWM_FUNC_CLOSE)
1072             {
1073                 FLAG_TOGGLE (c->xfwm_flags, XFWM_FLAG_HAS_CLOSE);
1074             }
1075             if (c->mwm_hints->functions & MWM_FUNC_MINIMIZE)
1076             {
1077                 FLAG_TOGGLE (c->xfwm_flags, XFWM_FLAG_HAS_HIDE);
1078             }
1079             if (c->mwm_hints->functions & MWM_FUNC_MAXIMIZE)
1080             {
1081                 FLAG_TOGGLE (c->xfwm_flags, XFWM_FLAG_HAS_MAXIMIZE);
1082             }
1083             if (c->mwm_hints->functions & MWM_FUNC_RESIZE)
1084             {
1085                 FLAG_TOGGLE (c->xfwm_flags, XFWM_FLAG_HAS_RESIZE);
1086             }
1087             if (c->mwm_hints->functions & MWM_FUNC_MOVE)
1088             {
1089                 FLAG_TOGGLE (c->xfwm_flags, XFWM_FLAG_HAS_MOVE);
1090             }
1091         }
1092     }
1093 
1094     if (update)
1095     {
1096         wc.x = c->x;
1097         wc.y = c->y;
1098         wc.width = c->width;
1099         wc.height = c->height;
1100 
1101         if (FLAG_TEST (c->flags, CLIENT_FLAG_FULLSCREEN))
1102         {
1103             clientUpdateFullscreenSize (c);
1104         }
1105         /* If client is maximized, we need to update its coordonates and size as well */
1106         else if (FLAG_TEST (c->flags, CLIENT_FLAG_MAXIMIZED))
1107         {
1108             GdkRectangle rect;
1109             myScreenFindMonitorAtPoint (screen_info,
1110                                         frameX (c) + (frameWidth (c) / 2),
1111                                         frameY (c) + (frameHeight (c) / 2), &rect);
1112             clientNewMaxSize (c, &wc, &rect);
1113         }
1114 
1115         clientConfigure (c, &wc, CWX | CWY | CWWidth | CWHeight, CFG_FORCE_REDRAW);
1116 
1117         /* MWM hints can add or remove decorations, update NET_FRAME_EXTENTS accordingly */
1118         setNetFrameExtents (display_info,
1119                             c->window,
1120                             frameTop (c),
1121                             frameLeft (c),
1122                             frameRight (c),
1123                             frameBottom (c));
1124     }
1125 }
1126 
1127 void
clientGetWMNormalHints(Client * c,gboolean update)1128 clientGetWMNormalHints (Client *c, gboolean update)
1129 {
1130     ScreenInfo *screen_info;
1131     DisplayInfo *display_info;
1132     XWindowChanges wc;
1133     unsigned long previous_value;
1134     long dummy;
1135     int result, status;
1136 
1137     g_return_if_fail (c != NULL);
1138     g_return_if_fail (c->window != None);
1139     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
1140 
1141     if (!c->size)
1142     {
1143         c->size = XAllocSizeHints ();
1144     }
1145     g_assert (c->size);
1146 
1147     screen_info = c->screen_info;
1148     display_info = screen_info->display_info;
1149 
1150     dummy = 0;
1151     myDisplayErrorTrapPush (display_info);
1152     status = XGetWMNormalHints (display_info->dpy, c->window, c->size, &dummy);
1153     result = myDisplayErrorTrapPop (display_info);
1154 
1155     if ((result != Success) || !status)
1156     {
1157         c->size->flags = 0;
1158     }
1159 
1160     /* Set/update gravity */
1161     c->gravity = c->size->flags & PWinGravity ? c->size->win_gravity : NorthWestGravity;
1162 
1163     previous_value = FLAG_TEST (c->xfwm_flags, XFWM_FLAG_IS_RESIZABLE);
1164     FLAG_UNSET (c->xfwm_flags, XFWM_FLAG_IS_RESIZABLE);
1165 
1166     wc.x = c->x;
1167     wc.y = c->y;
1168     wc.width = c->width;
1169     wc.height = c->height;
1170 
1171     if (!(c->size->flags & PMaxSize))
1172     {
1173         c->size->max_width = G_MAXINT;
1174         c->size->max_height = G_MAXINT;
1175         c->size->flags |= PMaxSize;
1176     }
1177 
1178     if (!(c->size->flags & PBaseSize))
1179     {
1180         c->size->base_width = 0;
1181         c->size->base_height = 0;
1182     }
1183 
1184     if (!(c->size->flags & PMinSize))
1185     {
1186         if ((c->size->flags & PBaseSize))
1187         {
1188             c->size->min_width = c->size->base_width;
1189             c->size->min_height = c->size->base_height;
1190         }
1191         else
1192         {
1193             c->size->min_width = 1;
1194             c->size->min_height = 1;
1195         }
1196         c->size->flags |= PMinSize;
1197     }
1198 
1199     if (c->size->flags & PResizeInc)
1200     {
1201         if (c->size->width_inc < 1)
1202         {
1203             c->size->width_inc = 1;
1204         }
1205         if (c->size->height_inc < 1)
1206         {
1207             c->size->height_inc = 1;
1208         }
1209     }
1210     else
1211     {
1212         c->size->width_inc = 1;
1213         c->size->height_inc = 1;
1214     }
1215 
1216     if (c->size->flags & PAspect)
1217     {
1218         if (c->size->min_aspect.x < 1)
1219         {
1220             c->size->min_aspect.x = 1;
1221         }
1222         if (c->size->min_aspect.y < 1)
1223         {
1224             c->size->min_aspect.y = 1;
1225         }
1226         if (c->size->max_aspect.x < 1)
1227         {
1228             c->size->max_aspect.x = 1;
1229         }
1230         if (c->size->max_aspect.y < 1)
1231         {
1232             c->size->max_aspect.y = 1;
1233         }
1234     }
1235     else
1236     {
1237         c->size->min_aspect.x = 1;
1238         c->size->min_aspect.y = 1;
1239         c->size->max_aspect.x = G_MAXINT;
1240         c->size->max_aspect.y = G_MAXINT;
1241     }
1242 
1243     if (c->size->min_width < 1)
1244     {
1245         c->size->min_width = 1;
1246     }
1247     if (c->size->min_height < 1)
1248     {
1249         c->size->min_height = 1;
1250     }
1251     if (c->size->max_width < 1)
1252     {
1253         c->size->max_width = 1;
1254     }
1255     if (c->size->max_height < 1)
1256     {
1257         c->size->max_height = 1;
1258     }
1259     if (wc.width > c->size->max_width)
1260     {
1261         wc.width = c->size->max_width;
1262     }
1263     if (wc.height > c->size->max_height)
1264     {
1265         wc.height = c->size->max_height;
1266     }
1267     if (wc.width < c->size->min_width)
1268     {
1269         wc.width = c->size->min_width;
1270     }
1271     if (wc.height < c->size->min_height)
1272     {
1273         wc.height = c->size->min_height;
1274     }
1275 
1276     if ((c->size->min_width < c->size->max_width) ||
1277         (c->size->min_height < c->size->max_height))
1278     {
1279         FLAG_SET (c->xfwm_flags, XFWM_FLAG_IS_RESIZABLE);
1280     }
1281 
1282     if (update)
1283     {
1284         if ((c->width != wc.width) || (c->height != wc.height))
1285         {
1286             if (FLAG_TEST (c->flags, CLIENT_FLAG_MAXIMIZED))
1287             {
1288                 clientRemoveMaximizeFlag (c);
1289             }
1290             clientConfigure (c, &wc, CWX | CWY | CWWidth | CWHeight, CFG_CONSTRAINED | CFG_FORCE_REDRAW);
1291         }
1292         else if (FLAG_TEST (c->xfwm_flags, XFWM_FLAG_IS_RESIZABLE) != previous_value)
1293         {
1294             frameQueueDraw (c, FALSE);
1295         }
1296     }
1297     else
1298     {
1299         c->width = wc.width;
1300         c->height = wc.height;
1301     }
1302 }
1303 
1304 void
clientGetWMProtocols(Client * c)1305 clientGetWMProtocols (Client *c)
1306 {
1307     ScreenInfo *screen_info;
1308     DisplayInfo *display_info;
1309     unsigned int wm_protocols_flags;
1310 
1311     g_return_if_fail (c != NULL);
1312     g_return_if_fail (c->window != None);
1313     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
1314 
1315     screen_info = c->screen_info;
1316     display_info = screen_info->display_info;
1317 
1318     wm_protocols_flags = getWMProtocols (display_info, c->window);
1319     FLAG_SET (c->wm_flags,
1320         (wm_protocols_flags & WM_PROTOCOLS_DELETE_WINDOW) ?
1321         WM_FLAG_DELETE : 0);
1322     FLAG_SET (c->wm_flags,
1323         (wm_protocols_flags & WM_PROTOCOLS_TAKE_FOCUS) ?
1324         WM_FLAG_TAKEFOCUS : 0);
1325     /* KDE extension */
1326     FLAG_SET (c->wm_flags,
1327         (wm_protocols_flags & WM_PROTOCOLS_CONTEXT_HELP) ?
1328         WM_FLAG_CONTEXT_HELP : 0);
1329     /* Ping */
1330     FLAG_SET (c->wm_flags,
1331         (wm_protocols_flags & WM_PROTOCOLS_PING) ?
1332         WM_FLAG_PING : 0);
1333 }
1334 
1335 static void
clientFree(Client * c)1336 clientFree (Client *c)
1337 {
1338     g_return_if_fail (c != NULL);
1339     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
1340 
1341     clientClearFocus (c);
1342     if (clientGetLastRaise (c->screen_info) == c)
1343     {
1344         clientClearLastRaise (c->screen_info);
1345     }
1346     if (clientGetDelayedFocus () == c)
1347     {
1348         clientClearDelayedFocus ();
1349     }
1350     if (c->blink_timeout_id)
1351     {
1352         g_source_remove (c->blink_timeout_id);
1353     }
1354     if (c->icon_timeout_id)
1355     {
1356         g_source_remove (c->icon_timeout_id);
1357     }
1358     if (c->frame_timeout_id)
1359     {
1360         g_source_remove (c->frame_timeout_id);
1361     }
1362     if (c->ping_timeout_id)
1363     {
1364         clientRemoveNetWMPing (c);
1365     }
1366 #ifdef HAVE_XSYNC
1367     if (c->xsync_alarm != None)
1368     {
1369         clientDestroyXSyncAlarm (c);
1370     }
1371     if (c->xsync_timeout_id)
1372     {
1373         g_source_remove (c->xsync_timeout_id);
1374     }
1375 #endif /* HAVE_XSYNC */
1376 #ifdef HAVE_LIBSTARTUP_NOTIFICATION
1377     if (c->startup_id)
1378     {
1379         g_free (c->startup_id);
1380     }
1381 #endif /* HAVE_LIBSTARTUP_NOTIFICATION */
1382     if (c->name)
1383     {
1384         g_free (c->name);
1385     }
1386     if (c->hostname)
1387     {
1388         g_free (c->hostname);
1389     }
1390     if (c->size)
1391     {
1392         XFree (c->size);
1393     }
1394     if (c->wmhints)
1395     {
1396         XFree (c->wmhints);
1397     }
1398     if (c->mwm_hints)
1399     {
1400         g_free (c->mwm_hints);
1401     }
1402     if ((c->ncmap > 0) && (c->cmap_windows))
1403     {
1404         XFree (c->cmap_windows);
1405     }
1406     if (c->class.res_name)
1407     {
1408         XFree (c->class.res_name);
1409     }
1410     if (c->class.res_class)
1411     {
1412         XFree (c->class.res_class);
1413     }
1414     if (c->dialog_pid)
1415     {
1416         kill (c->dialog_pid, SIGKILL);
1417     }
1418     if (c->dialog_fd >= 0)
1419     {
1420         close (c->dialog_fd);
1421     }
1422 
1423     g_free (c);
1424 }
1425 
1426 static void
clientApplyInitialState(Client * c)1427 clientApplyInitialState (Client *c)
1428 {
1429     g_return_if_fail (c != NULL);
1430     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
1431 
1432     /* We check that afterwards to make sure all states are now known */
1433     if (FLAG_TEST (c->flags, CLIENT_FLAG_MAXIMIZED))
1434     {
1435         if (FLAG_TEST (c->xfwm_flags, XFWM_FLAG_HAS_MAXIMIZE))
1436         {
1437             unsigned long mode = 0L;
1438 
1439             TRACE ("applying client's initial state: maximized");
1440             mode = c->flags & CLIENT_FLAG_MAXIMIZED;
1441 
1442             /* Unset fullscreen mode so that clientToggleMaximized() really change the state */
1443             FLAG_UNSET (c->flags, CLIENT_FLAG_MAXIMIZED);
1444             clientToggleMaximized (c, mode, FALSE);
1445         }
1446     }
1447     if (FLAG_TEST (c->flags, CLIENT_FLAG_FULLSCREEN))
1448     {
1449         TRACE ("applying client's initial state: fullscreen");
1450         clientUpdateFullscreenState (c);
1451     }
1452     if (FLAG_TEST_AND_NOT (c->flags, CLIENT_FLAG_ABOVE, CLIENT_FLAG_BELOW))
1453     {
1454         TRACE ("applying client's initial state: above");
1455         clientUpdateLayerState (c);
1456     }
1457     if (FLAG_TEST_AND_NOT (c->flags, CLIENT_FLAG_BELOW, CLIENT_FLAG_ABOVE))
1458     {
1459         TRACE ("applying client's initial state: below");
1460         clientUpdateLayerState (c);
1461     }
1462     if (FLAG_TEST (c->flags, CLIENT_FLAG_STICKY) &&
1463         FLAG_TEST (c->xfwm_flags, XFWM_FLAG_HAS_STICK))
1464     {
1465         TRACE ("applying client's initial state: sticky");
1466         clientStick (c, TRUE);
1467     }
1468     if (FLAG_TEST (c->flags, CLIENT_FLAG_SHADED))
1469     {
1470         TRACE ("applying client's initial state: shaded");
1471         clientShade (c);
1472     }
1473 }
1474 
1475 static gboolean
clientCheckShape(Client * c)1476 clientCheckShape (Client *c)
1477 {
1478     ScreenInfo *screen_info;
1479     DisplayInfo *display_info;
1480     int xws, yws, xbs, ybs;
1481     unsigned wws, hws, wbs, hbs;
1482     int boundingShaped, clipShaped;
1483 
1484     g_return_val_if_fail (c != NULL, FALSE);
1485 
1486     screen_info = c->screen_info;
1487     display_info = screen_info->display_info;
1488     boundingShaped = 0;
1489 
1490     if (display_info->have_shape)
1491     {
1492         myDisplayErrorTrapPush (display_info);
1493         XShapeQueryExtents (display_info->dpy, c->window, &boundingShaped, &xws, &yws, &wws,
1494                             &hws, &clipShaped, &xbs, &ybs, &wbs, &hbs);
1495         myDisplayErrorTrapPopIgnored (display_info);
1496         return (boundingShaped != 0);
1497     }
1498     return FALSE;
1499 }
1500 
1501 static void
clientUpdateIconPix(Client * c)1502 clientUpdateIconPix (Client *c)
1503 {
1504     ScreenInfo *screen_info;
1505     gint size;
1506     GdkPixbuf *icon;
1507     int i;
1508 
1509     g_return_if_fail (c != NULL);
1510     g_return_if_fail (c->window != None);
1511     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
1512 
1513     screen_info = c->screen_info;
1514     for (i = 0; i < STATE_TOGGLED; i++)
1515     {
1516         xfwmPixmapFree (&c->appmenu[i]);
1517     }
1518 
1519     if (xfwmPixmapNone(&screen_info->buttons[MENU_BUTTON][ACTIVE]))
1520     {
1521         /* The current theme has no menu button */
1522         return;
1523     }
1524 
1525     for (i = 0; i < STATE_TOGGLED; i++)
1526     {
1527         if (!xfwmPixmapNone(&screen_info->buttons[MENU_BUTTON][i]))
1528         {
1529             xfwmPixmapDuplicate (&screen_info->buttons[MENU_BUTTON][i], &c->appmenu[i]);
1530         }
1531     }
1532     size = MIN (screen_info->buttons[MENU_BUTTON][ACTIVE].width,
1533                 screen_info->buttons[MENU_BUTTON][ACTIVE].height);
1534 
1535     if (size > 1)
1536     {
1537         icon = getAppIcon (c, size, size);
1538         if (icon)
1539         {
1540             for (i = 0; i < STATE_TOGGLED; i++)
1541             {
1542                 if (!xfwmPixmapNone(&c->appmenu[i]))
1543                 {
1544                     xfwmPixmapRenderGdkPixbuf (&c->appmenu[i], icon);
1545                 }
1546             }
1547             g_object_unref (icon);
1548         }
1549     }
1550 }
1551 
1552 static gboolean
update_icon_idle_cb(gpointer data)1553 update_icon_idle_cb (gpointer data)
1554 {
1555     Client *c;
1556 
1557     c = (Client *) data;
1558     g_return_val_if_fail (c, FALSE);
1559     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
1560 
1561     clientUpdateIconPix (c);
1562     if (FLAG_TEST (c->xfwm_flags, XFWM_FLAG_VISIBLE))
1563     {
1564         frameQueueDraw (c, FALSE);
1565     }
1566     c->icon_timeout_id = 0;
1567 
1568     return (FALSE);
1569 }
1570 
1571 void
clientUpdateIcon(Client * c)1572 clientUpdateIcon (Client *c)
1573 {
1574     g_return_if_fail (c);
1575     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
1576 
1577     if (c->icon_timeout_id == 0)
1578     {
1579         c->icon_timeout_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
1580                                               update_icon_idle_cb, c, NULL);
1581     }
1582 }
1583 
1584 void
clientSaveSizePos(Client * c)1585 clientSaveSizePos (Client *c)
1586 {
1587     g_return_if_fail (c != NULL);
1588 
1589     if (!FLAG_TEST (c->flags, CLIENT_FLAG_RESTORE_SIZE_POS))
1590     {
1591         c->saved_geometry.x = c->x;
1592         c->saved_geometry.width = c->width;
1593         c->saved_geometry.y = c->y;
1594         c->saved_geometry.height = c->height;
1595     }
1596 }
1597 
1598 gboolean
clientRestoreSizePos(Client * c)1599 clientRestoreSizePos (Client *c)
1600 {
1601     g_return_val_if_fail (c != NULL, FALSE);
1602 
1603     if (FLAG_TEST (c->flags, CLIENT_FLAG_RESTORE_SIZE_POS))
1604     {
1605         c->x = c->saved_geometry.x;
1606         c->width = c->saved_geometry.width;
1607         c->y = c->saved_geometry.y;
1608         c->height = c->saved_geometry.height;
1609 
1610         FLAG_UNSET (c->flags, CLIENT_FLAG_RESTORE_SIZE_POS);
1611         return TRUE;
1612     }
1613 
1614     return FALSE;
1615 }
1616 
1617 Client *
clientFrame(DisplayInfo * display_info,Window w,gboolean recapture)1618 clientFrame (DisplayInfo *display_info, Window w, gboolean recapture)
1619 {
1620     ScreenInfo *screen_info;
1621     XWindowAttributes attr;
1622     XSetWindowAttributes attributes;
1623     Client *c = NULL;
1624     gboolean shaped;
1625     gchar *wm_name;
1626     unsigned long valuemask;
1627     int i;
1628 
1629     g_return_val_if_fail (w != None, NULL);
1630     g_return_val_if_fail (display_info != NULL, NULL);
1631     TRACE ("window 0x%lx", w);
1632 
1633     myDisplayGrabServer (display_info);
1634     myDisplayErrorTrapPush (display_info);
1635 
1636     if (!XGetWindowAttributes (display_info->dpy, w, &attr))
1637     {
1638         DBG ("Cannot get window attributes for window (0x%lx)", w);
1639         goto out;
1640     }
1641 
1642     screen_info = myDisplayGetScreenFromRoot (display_info, attr.root);
1643     if (!screen_info)
1644     {
1645         DBG ("Cannot determine screen info from window (0x%lx)", w);
1646         goto out;
1647     }
1648 
1649     if (w == screen_info->xfwm4_win)
1650     {
1651         TRACE ("not managing our own event window");
1652         compositorAddWindow (display_info, w, NULL);
1653         goto out;
1654     }
1655 
1656 #ifdef ENABLE_KDE_SYSTRAY_PROXY
1657     if (checkKdeSystrayWindow (display_info, w))
1658     {
1659         TRACE ("detected KDE systray windows");
1660         if (screen_info->systray != None)
1661         {
1662             sendSystrayReqDock (display_info, w, screen_info->systray);
1663             goto out;
1664         }
1665         TRACE ("no systray found for this screen");
1666     }
1667 #endif /* ENABLE_KDE_SYSTRAY_PROXY */
1668 
1669     if (attr.override_redirect)
1670     {
1671         TRACE ("override redirect window 0x%lx", w);
1672         goto out;
1673     }
1674 
1675     c = g_new0 (Client, 1);
1676     if (!c)
1677     {
1678         TRACE ("cannot allocate memory for the window structure");
1679         goto out;
1680     }
1681 
1682     c->window = w;
1683     c->screen_info = screen_info;
1684     c->serial = screen_info->client_serial++;
1685 
1686     /* Termination dialog */
1687     c->dialog_pid = 0;
1688     c->dialog_fd = -1;
1689 
1690     getWindowName (display_info, c->window, &wm_name);
1691     getWindowHostname (display_info, c->window, &c->hostname);
1692     c->name = clientCreateTitleName (c, wm_name, c->hostname);
1693     g_free (wm_name);
1694 
1695     getTransientFor (display_info, screen_info->xroot, c->window, &c->transient_for);
1696     XChangeSaveSet(display_info->dpy, c->window, SetModeInsert);
1697 
1698     /* Initialize structure */
1699     c->size = NULL;
1700     c->flags = 0L;
1701     c->wm_flags = 0L;
1702     c->xfwm_flags = XFWM_FLAG_INITIAL_VALUES;
1703     c->x = attr.x;
1704     c->y = attr.y;
1705     c->width = attr.width;
1706     c->height = attr.height;
1707 
1708     c->applied_geometry.x = c->x;
1709     c->applied_geometry.y = c->y;
1710     c->applied_geometry.width = c->width;
1711     c->applied_geometry.height = c->height;
1712 
1713 #ifdef HAVE_LIBSTARTUP_NOTIFICATION
1714     c->startup_id = NULL;
1715 #endif /* HAVE_LIBSTARTUP_NOTIFICATION */
1716 
1717     clientGetWMNormalHints (c, FALSE);
1718     clientGetMWMHints (c);
1719     c->size->x = c->x;
1720     c->size->y = c->y;
1721     c->size->width = c->width;
1722     c->size->height = c->height;
1723     c->frame_cache_width = -1;
1724     c->frame_cache_height = -1;
1725     c->border_width = attr.border_width;
1726     c->cmap = attr.colormap;
1727 
1728     shaped = clientCheckShape(c);
1729     if (shaped)
1730     {
1731         FLAG_UNSET (c->xfwm_flags, XFWM_FLAG_HAS_BORDER);
1732         FLAG_SET (c->flags, CLIENT_FLAG_HAS_SHAPE);
1733     }
1734 
1735     if (((c->size->flags & (PMinSize | PMaxSize)) != (PMinSize | PMaxSize))
1736         || (((c->size->flags & (PMinSize | PMaxSize)) ==
1737                 (PMinSize | PMaxSize))
1738             && ((c->size->min_width < c->size->max_width)
1739                 || (c->size->min_height < c->size->max_height))))
1740     {
1741         FLAG_SET (c->xfwm_flags, XFWM_FLAG_IS_RESIZABLE);
1742     }
1743 
1744     for (i = 0; i < BUTTON_COUNT; i++)
1745     {
1746         c->button_status[i] = BUTTON_STATE_NORMAL;
1747     }
1748 
1749     if (!XGetWMColormapWindows (display_info->dpy, c->window, &c->cmap_windows, &c->ncmap))
1750     {
1751         c->ncmap = 0;
1752     }
1753 
1754     c->fullscreen_monitors[0] = 0;
1755     c->fullscreen_monitors[1] = 0;
1756     c->fullscreen_monitors[2] = 0;
1757     c->fullscreen_monitors[3] = 0;
1758 
1759     /* Opacity for compositing manager */
1760     c->opacity = NET_WM_OPAQUE;
1761     getOpacity (display_info, c->window, &c->opacity);
1762     c->opacity_applied = c->opacity;
1763     c->opacity_flags = 0;
1764 
1765     /* Keep count of blinking iterations */
1766     c->blink_iterations = 0;
1767 
1768     if (getOpacityLock (display_info, c->window))
1769     {
1770         FLAG_SET (c->xfwm_flags, XFWM_FLAG_OPACITY_LOCKED);
1771     }
1772 
1773     /* Timout for asynchronous icon update */
1774     c->icon_timeout_id = 0;
1775     /* Timout for asynchronous frame update */
1776     c->frame_timeout_id = 0;
1777     /* Timeout for blinking on urgency */
1778     c->blink_timeout_id = 0;
1779     /* Ping timeout  */
1780     c->ping_timeout_id = 0;
1781     /* Ping timeout  */
1782     c->ping_time = 0;
1783 
1784     c->class.res_name = NULL;
1785     c->class.res_class = NULL;
1786     XGetClassHint (display_info->dpy, w, &c->class);
1787     c->wmhints = XGetWMHints (display_info->dpy, c->window);
1788     c->group_leader = None;
1789     if (c->wmhints)
1790     {
1791         if (c->wmhints->flags & WindowGroupHint)
1792         {
1793             c->group_leader = c->wmhints->window_group;
1794         }
1795     }
1796     c->client_leader = getClientLeader (display_info, c->window);
1797 
1798     TRACE ("\"%s\" (0x%lx) initial map_state = %s",
1799                 c->name, c->window,
1800                 (attr.map_state == IsUnmapped) ?
1801                 "IsUnmapped" :
1802                 (attr.map_state == IsViewable) ?
1803                 "IsViewable" :
1804                 (attr.map_state == IsUnviewable) ?
1805                 "IsUnviewable" :
1806                 "(unknown)");
1807     if (attr.map_state != IsUnmapped)
1808     {
1809         /* Reparent will send us unmap/map events */
1810         FLAG_SET (c->xfwm_flags, XFWM_FLAG_MAP_PENDING);
1811     }
1812     c->ignore_unmap = 0;
1813     c->type = UNSET;
1814     c->type_atom = None;
1815 
1816     FLAG_SET (c->flags, START_ICONIC (c) ? CLIENT_FLAG_ICONIFIED : 0);
1817     FLAG_SET (c->wm_flags, HINTS_ACCEPT_INPUT (c->wmhints) ? WM_FLAG_INPUT : 0);
1818 
1819     clientGetWMProtocols (c);
1820     c->win_layer = WIN_LAYER_NORMAL;
1821     c->pre_fullscreen_layer = c->win_layer;
1822 
1823     /* net_wm_user_time standard */
1824     c->user_time = 0;
1825     c->user_time_win = getNetWMUserTimeWindow(display_info, c->window);
1826     clientAddUserTimeWin (c);
1827     clientGetUserTime (c);
1828 
1829     /* client PID */
1830     c->pid = getWindowPID (display_info, c->window);
1831     TRACE ("client \"%s\" (0x%lx) PID = %i", c->name, c->window, c->pid);
1832 
1833     /* Apply startup notification properties if available */
1834     sn_client_startup_properties (c);
1835 
1836     /* Reload from session */
1837     if (sessionMatchWinToSM (c))
1838     {
1839         FLAG_SET (c->xfwm_flags, XFWM_FLAG_SESSION_MANAGED);
1840     }
1841 
1842     /* Beware, order of calls is important here ! */
1843     clientGetNetState (c);
1844     clientGetNetWmType (c);
1845     clientGetInitialNetWmDesktop (c);
1846     /* workarea will be updated when shown, no need to worry here */
1847     clientGetNetStruts (c);
1848 
1849     /* GTK 3.x stuff */
1850     clientGetGtkFrameExtents(c);
1851     clientGetGtkHideTitlebar(c);
1852 
1853     /* Once we know the type of window, we can initialize window position */
1854     if (!FLAG_TEST (c->xfwm_flags, XFWM_FLAG_SESSION_MANAGED))
1855     {
1856         clientCoordGravitate (c, c->gravity, APPLY, &c->x, &c->y);
1857         if (attr.map_state == IsUnmapped)
1858         {
1859             clientInitPosition (c);
1860         }
1861     }
1862 
1863     /*
1864        Initialize "old" fields once the position is ensured, to avoid
1865        initially maximized or fullscreen windows being placed offscreen
1866        once de-maximized
1867      */
1868     c->saved_geometry.x = c->x;
1869     c->saved_geometry.y = c->y;
1870     c->saved_geometry.width = c->width;
1871     c->saved_geometry.height = c->height;
1872 
1873     c->pre_fullscreen_geometry.x = c->x;
1874     c->pre_fullscreen_geometry.y = c->y;
1875     c->pre_fullscreen_geometry.width = c->width;
1876     c->pre_fullscreen_geometry.height = c->height;
1877 
1878     /*
1879        We must call clientApplyInitialState() after having placed the
1880        window so that the inital position values are correctly set if the
1881        inital state is maximize or fullscreen
1882      */
1883     clientApplyInitialState (c);
1884 
1885     valuemask = CWEventMask|CWBitGravity|CWWinGravity;
1886     attributes.event_mask = (FRAME_EVENT_MASK | POINTER_EVENT_MASK);
1887     attributes.win_gravity = StaticGravity;
1888     attributes.bit_gravity = StaticGravity;
1889 
1890 #ifdef HAVE_RENDER
1891     if ((attr.depth == 32) && (display_info->have_render))
1892     {
1893         c->visual = attr.visual;
1894         c->depth  = attr.depth;
1895 
1896         attributes.colormap = attr.colormap;
1897         attributes.background_pixmap = None;
1898         attributes.border_pixel = 0;
1899         attributes.background_pixel = 0;
1900 
1901         valuemask |= CWColormap|CWBackPixmap|CWBackPixel|CWBorderPixel;
1902     }
1903     else
1904     {
1905         /* Default depth/visual */
1906         c->visual = screen_info->visual;
1907         c->depth  = screen_info->depth;
1908     }
1909 #else  /* HAVE_RENDER */
1910     /* We don't support multiple depth/visual w/out render */
1911     c->visual = screen_info->visual;
1912     c->depth  = screen_info->depth;
1913 #endif /* HAVE_RENDER */
1914 
1915     c->frame =
1916         XCreateWindow (display_info->dpy, screen_info->xroot, 0, 0, 1, 1, 0,
1917         c->depth, InputOutput, c->visual, valuemask, &attributes);
1918 #ifdef HAVE_XI2
1919     xfwm_device_configure_xi2_event_mask (display_info->devices, display_info->dpy,
1920                                           c->frame, attributes.event_mask);
1921 #endif
1922 
1923     XSelectInput (display_info->dpy, c->window, NoEventMask);
1924     XSetWindowBorderWidth (display_info->dpy, c->window, 0);
1925     if (FLAG_TEST (c->flags, CLIENT_FLAG_SHADED))
1926     {
1927         XUnmapWindow (display_info->dpy, c->window);
1928     }
1929     XReparentWindow (display_info->dpy, c->window, c->frame, frameLeft (c), frameTop (c));
1930     valuemask = CWEventMask|CWWinGravity;
1931 
1932     /* Force win_gravity to NorthWest. Any other gravity has the
1933      * window move relative to its parent when the parent resizes.
1934      *
1935      * There are many bug reports related to libreoffice using
1936      * StaticGravity, including:
1937      *
1938      * http://www.linuxquestions.org/questions/linux-desktop-74/strange-libreoffice-problem-on-debian-with-xfce-4175469847/
1939      * https://bugs.launchpad.net/ubuntu/+source/nvidia-graphics-drivers/+bug/889212
1940      * https://bbs.archlinux.org/viewtopic.php?id=133137
1941      * http://forums.debian.net/viewtopic.php?f=6&t=105757
1942      */
1943     attributes.event_mask = (CLIENT_EVENT_MASK);
1944     attributes.win_gravity = NorthWestGravity;
1945     XChangeWindowAttributes (display_info->dpy, c->window, valuemask, &attributes);
1946     if (display_info->have_shape)
1947     {
1948         XShapeSelectInput (display_info->dpy, c->window, ShapeNotifyMask);
1949     }
1950 
1951     clientAddToList (c);
1952     clientGrabButtons(c);
1953 
1954     /* Initialize per client menu button pixmap */
1955 
1956     for (i = 0; i < STATE_TOGGLED; i++)
1957     {
1958         xfwmPixmapInit (screen_info, &c->appmenu[i]);
1959     }
1960 
1961     for (i = 0; i < SIDE_COUNT; i++)
1962     {
1963         if (i == SIDE_TOP)
1964             continue;  /* Keep SIDE_TOP for later */
1965 
1966         xfwmWindowCreate (screen_info, c->visual, c->depth, c->frame,
1967             &c->sides[i], NoEventMask,
1968             myDisplayGetCursorResize(screen_info->display_info, CORNER_COUNT + i));
1969     }
1970 
1971     for (i = 0; i < CORNER_COUNT; i++)
1972     {
1973         xfwmWindowCreate (screen_info, c->visual, c->depth, c->frame,
1974             &c->corners[i], NoEventMask,
1975             myDisplayGetCursorResize(screen_info->display_info, i));
1976     }
1977 
1978     xfwmWindowCreate (screen_info, c->visual, c->depth, c->frame,
1979         &c->title, NoEventMask, None);
1980 
1981     /*create the top side window AFTER the title window since they overlap
1982        and the top side window should be on top */
1983 
1984     xfwmWindowCreate (screen_info, c->visual, c->depth, c->frame,
1985         &c->sides[SIDE_TOP], NoEventMask,
1986         myDisplayGetCursorResize(screen_info->display_info, CORNER_COUNT + SIDE_TOP));
1987 
1988     for (i = 0; i < BUTTON_COUNT; i++)
1989     {
1990         xfwmWindowCreate (screen_info, c->visual, c->depth, c->frame,
1991             &c->buttons[i], BUTTON_EVENT_MASK, None);
1992     }
1993     clientUpdateIconPix (c);
1994 
1995     /* Put the window on top to avoid XShape, that speeds up hw accelerated
1996        GL apps dramatically */
1997     XRaiseWindow (display_info->dpy, c->window);
1998 
1999     TRACE ("now calling configure for the new window \"%s\" (0x%lx)", c->name, c->window);
2000     clientReconfigure (c, CFG_NOTIFY | CFG_FORCE_REDRAW);
2001 
2002     /* Notify the compositor about this new window */
2003     compositorAddWindow (display_info, c->frame, c);
2004 
2005     if (!FLAG_TEST (c->flags, CLIENT_FLAG_ICONIFIED))
2006     {
2007         if ((c->win_workspace == screen_info->current_ws) ||
2008             FLAG_TEST(c->flags, CLIENT_FLAG_STICKY))
2009         {
2010             if (recapture)
2011             {
2012                 clientRaise (c, None);
2013                 clientShow (c, TRUE);
2014                 clientSortRing(c);
2015             }
2016             else
2017             {
2018                 clientFocusNew(c);
2019             }
2020         }
2021         else
2022         {
2023             clientRaise (c, None);
2024             clientShow (c, TRUE);
2025             clientInitFocusFlag (c);
2026         }
2027     }
2028     else
2029     {
2030         clientRaise (c, None);
2031         setWMState (display_info, c->window, IconicState);
2032         clientSetNetActions (c);
2033     }
2034     clientUpdateOpacity (c);
2035     clientGrabMouseButton (c);
2036     setNetFrameExtents (display_info, c->window, frameTop (c), frameLeft (c),
2037                                                  frameRight (c), frameBottom (c));
2038     clientSetNetState (c);
2039 
2040 #ifdef HAVE_XSYNC
2041     c->xsync_counter = None;
2042     c->xsync_alarm = None;
2043     c->xsync_timeout_id = 0;
2044     if (display_info->have_xsync)
2045     {
2046         clientGetXSyncCounter (c);
2047     }
2048     if (c->xsync_counter)
2049     {
2050         clientCreateXSyncAlarm (c);
2051     }
2052 #endif /* HAVE_XSYNC */
2053 
2054 
2055     DBG ("client \"%s\" (0x%lx) is now managed", c->name, c->window);
2056     DBG ("client_count=%d", screen_info->client_count);
2057 
2058 out:
2059     /* Window is reparented now, so we can safely release the grab
2060      * on the server
2061      */
2062     myDisplayErrorTrapPopIgnored (display_info);
2063     myDisplayUngrabServer (display_info);
2064 
2065     return c;
2066 }
2067 
2068 void
clientUnframe(Client * c,gboolean remap)2069 clientUnframe (Client *c, gboolean remap)
2070 {
2071     ScreenInfo *screen_info;
2072     DisplayInfo *display_info;
2073     XEvent ev;
2074     int i;
2075     gboolean reparented;
2076 
2077     TRACE ("client \"%s\" (0x%lx) [%s]",
2078             c->name, c->window, remap ? "remap" : "no remap");
2079 
2080     g_return_if_fail (c != NULL);
2081 
2082     screen_info = c->screen_info;
2083     display_info = screen_info->display_info;
2084 
2085     clientRemoveFromList (c);
2086     compositorSetClient (display_info, c->frame, NULL);
2087 
2088     myDisplayGrabServer (display_info);
2089     myDisplayErrorTrapPush (display_info);
2090 
2091     clientRemoveUserTimeWin (c);
2092     clientUngrabButtons (c);
2093     XUnmapWindow (display_info->dpy, c->frame);
2094     clientCoordGravitate (c, c->gravity, REMOVE, &c->x, &c->y);
2095     XSelectInput (display_info->dpy, c->window, NoEventMask);
2096     XChangeSaveSet(display_info->dpy, c->window, SetModeDelete);
2097 
2098     reparented = XCheckTypedWindowEvent (display_info->dpy, c->window, ReparentNotify, &ev);
2099 
2100     if (remap || !reparented)
2101     {
2102         XReparentWindow (display_info->dpy, c->window, c->screen_info->xroot, c->x, c->y);
2103         XSetWindowBorderWidth (display_info->dpy, c->window, c->border_width);
2104         if (remap)
2105         {
2106             compositorAddWindow (display_info, c->window, NULL);
2107             XMapWindow (display_info->dpy, c->window);
2108         }
2109         else
2110         {
2111             XUnmapWindow (display_info->dpy, c->window);
2112             setWMState (display_info, c->window, WithdrawnState);
2113         }
2114     }
2115 
2116     if (!remap)
2117     {
2118         XDeleteProperty (display_info->dpy, c->window,
2119                          display_info->atoms[NET_WM_STATE]);
2120         XDeleteProperty (display_info->dpy, c->window,
2121                          display_info->atoms[NET_WM_DESKTOP]);
2122         XDeleteProperty (display_info->dpy, c->window,
2123                          display_info->atoms[NET_WM_ALLOWED_ACTIONS]);
2124     }
2125 
2126     xfwmWindowDelete (&c->title);
2127 
2128     for (i = 0; i < SIDE_COUNT; i++)
2129     {
2130         xfwmWindowDelete (&c->sides[i]);
2131     }
2132     for (i = 0; i < CORNER_COUNT; i++)
2133     {
2134         xfwmWindowDelete (&c->corners[i]);
2135     }
2136     for (i = 0; i < STATE_TOGGLED; i++)
2137     {
2138         xfwmPixmapFree (&c->appmenu[i]);
2139     }
2140     for (i = 0; i < BUTTON_COUNT; i++)
2141     {
2142         xfwmWindowDelete (&c->buttons[i]);
2143     }
2144     if (FLAG_TEST (c->flags, CLIENT_FLAG_HAS_STRUT))
2145     {
2146         workspaceUpdateArea (c->screen_info);
2147     }
2148     XDestroyWindow (display_info->dpy, c->frame);
2149 
2150     myDisplayErrorTrapPopIgnored (display_info);
2151     myDisplayUngrabServer (display_info);
2152     clientFree (c);
2153 }
2154 
2155 void
clientFrameAll(ScreenInfo * screen_info)2156 clientFrameAll (ScreenInfo *screen_info)
2157 {
2158     DisplayInfo *display_info;
2159     XWindowAttributes attr;
2160     xfwmWindow shield;
2161     Window w1, w2, *wins;
2162     unsigned int count, i;
2163 
2164     TRACE ("entering");
2165 
2166     display_info = screen_info->display_info;
2167     clientSetFocus (screen_info, NULL, myDisplayGetCurrentTime (display_info), NO_FOCUS_FLAG);
2168     xfwmWindowTemp (screen_info,
2169                     NULL, 0,
2170                     screen_info->xroot,
2171                     &shield,
2172                     0, 0,
2173                     screen_info->width,
2174                     screen_info->height,
2175                     EnterWindowMask,
2176                     FALSE);
2177 
2178     XSync (display_info->dpy, FALSE);
2179     myDisplayGrabServer (display_info);
2180     XQueryTree (display_info->dpy, screen_info->xroot, &w1, &w2, &wins, &count);
2181     for (i = 0; i < count; i++)
2182     {
2183         XGetWindowAttributes (display_info->dpy, wins[i], &attr);
2184         if ((attr.map_state == IsViewable) && (attr.root == screen_info->xroot))
2185         {
2186             Client *c = clientFrame (display_info, wins[i], TRUE);
2187             if ((c) && ((screen_info->params->raise_on_click) || (screen_info->params->click_to_focus)))
2188             {
2189                 clientGrabMouseButton (c);
2190             }
2191         }
2192         else
2193         {
2194              compositorAddWindow (display_info, wins[i], NULL);
2195         }
2196     }
2197     if (wins)
2198     {
2199         XFree (wins);
2200     }
2201     clientFocusTop (screen_info, WIN_LAYER_FULLSCREEN, myDisplayGetCurrentTime (display_info));
2202     xfwmWindowDelete (&shield);
2203     myDisplayUngrabServer (display_info);
2204     XSync (display_info->dpy, FALSE);
2205 }
2206 
2207 void
clientUnframeAll(ScreenInfo * screen_info)2208 clientUnframeAll (ScreenInfo *screen_info)
2209 {
2210     DisplayInfo *display_info;
2211     Client *c;
2212     Window w1, w2, *wins;
2213     unsigned int count, i;
2214 
2215     TRACE ("entering");
2216 
2217     display_info = screen_info->display_info;
2218     clientSetFocus (screen_info, NULL, myDisplayGetCurrentTime (display_info), FOCUS_IGNORE_MODAL);
2219     XSync (display_info->dpy, FALSE);
2220     myDisplayGrabServer (display_info);
2221     XQueryTree (display_info->dpy, screen_info->xroot, &w1, &w2, &wins, &count);
2222     for (i = 0; i < count; i++)
2223     {
2224         c = myScreenGetClientFromWindow (screen_info, wins[i], SEARCH_FRAME);
2225         if (c)
2226         {
2227             clientUnframe (c, TRUE);
2228         }
2229     }
2230     myDisplayUngrabServer (display_info);
2231     XSync(display_info->dpy, FALSE);
2232     if (wins)
2233     {
2234         XFree (wins);
2235     }
2236 }
2237 
2238 Client *
clientGetFromWindow(Client * c,Window w,unsigned short mode)2239 clientGetFromWindow (Client *c, Window w, unsigned short mode)
2240 {
2241     int b;
2242 
2243     g_return_val_if_fail (w != None, NULL);
2244     g_return_val_if_fail (c != NULL, NULL);
2245     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
2246 
2247     if (mode & SEARCH_WINDOW)
2248     {
2249         if (c->window == w)
2250         {
2251             TRACE ("found \"%s\" (mode WINDOW)", c->name);
2252             return (c);
2253         }
2254     }
2255 
2256     if (mode & SEARCH_FRAME)
2257     {
2258         if (c->frame == w)
2259         {
2260             TRACE ("found \"%s\" (mode FRAME)", c->name);
2261             return (c);
2262         }
2263     }
2264 
2265     if (mode & SEARCH_WIN_USER_TIME)
2266     {
2267         if (c->user_time_win == w)
2268         {
2269             TRACE ("found \"%s\" (mode WIN_USER_TIME)", c->name);
2270             return (c);
2271         }
2272     }
2273 
2274     if (mode & SEARCH_BUTTON)
2275     {
2276         for (b = 0; b < BUTTON_COUNT; b++)
2277         {
2278             if (MYWINDOW_XWINDOW(c->buttons[b]) == w)
2279             {
2280                 TRACE ("found \"%s\" (mode BUTTON)", c->name);
2281                 return (c);
2282             }
2283         }
2284     }
2285 
2286     TRACE ("no client found");
2287 
2288     return NULL;
2289 }
2290 
2291 static void
clientSetWorkspaceSingle(Client * c,guint ws)2292 clientSetWorkspaceSingle (Client *c, guint ws)
2293 {
2294     ScreenInfo *screen_info;
2295     DisplayInfo *display_info;
2296 
2297     g_return_if_fail (c != NULL);
2298     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
2299 
2300     screen_info = c->screen_info;
2301     display_info = screen_info->display_info;
2302 
2303     if (ws > screen_info->workspace_count - 1)
2304     {
2305         ws = screen_info->workspace_count - 1;
2306         TRACE ("value off limits, using %i instead", ws);
2307     }
2308 
2309     if (c->win_workspace != ws)
2310     {
2311         TRACE ("setting client \"%s\" (0x%lx) to current_ws %d", c->name, c->window, ws);
2312         c->win_workspace = ws;
2313         if (FLAG_TEST (c->flags, CLIENT_FLAG_STICKY))
2314         {
2315             setHint (display_info, c->window, NET_WM_DESKTOP, (unsigned long) ALL_WORKSPACES);
2316         }
2317         else
2318         {
2319             setHint (display_info, c->window, NET_WM_DESKTOP, (unsigned long) ws);
2320         }
2321     }
2322     FLAG_SET (c->xfwm_flags, XFWM_FLAG_WORKSPACE_SET);
2323 }
2324 
2325 void
clientSetWorkspace(Client * c,guint ws,gboolean manage_mapping)2326 clientSetWorkspace (Client *c, guint ws, gboolean manage_mapping)
2327 {
2328     Client *c2;
2329     GList *list_of_windows;
2330     GList *list;
2331     guint previous_ws;
2332 
2333     g_return_if_fail (c != NULL);
2334     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
2335 
2336     if (ws > c->screen_info->workspace_count - 1)
2337     {
2338         g_warning ("Requested workspace %d does not exist", ws);
2339         return;
2340     }
2341 
2342     list_of_windows = clientListTransientOrModal (c);
2343     for (list = list_of_windows; list; list = g_list_next (list))
2344     {
2345         c2 = (Client *) list->data;
2346 
2347         if (c2->win_workspace != ws)
2348         {
2349             TRACE ("setting client \"%s\" (0x%lx) to current_ws %d", c->name, c->window, ws);
2350 
2351             previous_ws = c2->win_workspace;
2352             clientSetWorkspaceSingle (c2, ws);
2353 
2354             if (manage_mapping && !FLAG_TEST (c2->flags, CLIENT_FLAG_ICONIFIED))
2355             {
2356                 if (previous_ws == c2->screen_info->current_ws)
2357                 {
2358                     clientWithdraw (c2, c2->screen_info->current_ws, FALSE);
2359                 }
2360                 if (FLAG_TEST (c2->flags, CLIENT_FLAG_STICKY) || (ws == c2->screen_info->current_ws))
2361                 {
2362                     clientShow (c2, FALSE);
2363                 }
2364             }
2365         }
2366     }
2367     g_list_free (list_of_windows);
2368 }
2369 
2370 static void
clientShowSingle(Client * c,gboolean deiconify)2371 clientShowSingle (Client *c, gboolean deiconify)
2372 {
2373     ScreenInfo *screen_info;
2374     DisplayInfo *display_info;
2375 
2376     g_return_if_fail (c != NULL);
2377 
2378     if (FLAG_TEST (c->xfwm_flags, XFWM_FLAG_VISIBLE))
2379     {
2380         /* Should we map the window if it is visible? */
2381         return;
2382     }
2383 
2384     screen_info = c->screen_info;
2385     display_info = screen_info->display_info;
2386 
2387     if ((c->win_workspace == screen_info->current_ws) || FLAG_TEST (c->flags, CLIENT_FLAG_STICKY))
2388     {
2389         TRACE ("showing client \"%s\" (0x%lx)", c->name, c->window);
2390         FLAG_SET (c->xfwm_flags, XFWM_FLAG_VISIBLE);
2391         myDisplayErrorTrapPush (display_info);
2392         XMapWindow (display_info->dpy, c->frame);
2393         if (!FLAG_TEST (c->flags, CLIENT_FLAG_SHADED))
2394         {
2395             XMapWindow (display_info->dpy, c->window);
2396         }
2397         myDisplayErrorTrapPopIgnored (display_info);
2398         /* Adjust to urgency state as the window is visible */
2399         clientUpdateUrgency (c);
2400     }
2401     if (deiconify)
2402     {
2403         FLAG_UNSET (c->flags, CLIENT_FLAG_ICONIFIED);
2404         setWMState (display_info, c->window, NormalState);
2405     }
2406     clientSetNetActions (c);
2407     clientSetNetState (c);
2408 }
2409 
2410 void
clientShow(Client * c,gboolean deiconify)2411 clientShow (Client *c, gboolean deiconify)
2412 {
2413     Client *c2;
2414     GList *list_of_windows;
2415     GList *list;
2416 
2417     g_return_if_fail (c != NULL);
2418     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
2419 
2420     list_of_windows = clientListTransientOrModal (c);
2421     for (list = g_list_last (list_of_windows); list; list = g_list_previous (list))
2422     {
2423         c2 = (Client *) list->data;
2424         clientSetWorkspaceSingle (c2, c->win_workspace);
2425         /* Ignore request before if the window is not yet managed */
2426         if (!FLAG_TEST (c2->xfwm_flags, XFWM_FLAG_MANAGED))
2427         {
2428             continue;
2429         }
2430         clientShowSingle (c2, deiconify);
2431     }
2432     g_list_free (list_of_windows);
2433 
2434     /* Update working area as windows have been shown */
2435     workspaceUpdateArea (c->screen_info);
2436 }
2437 
2438 static void
clientWithdrawSingle(Client * c,GList * exclude_list,gboolean iconify)2439 clientWithdrawSingle (Client *c, GList *exclude_list, gboolean iconify)
2440 {
2441     ScreenInfo *screen_info;
2442     DisplayInfo *display_info;
2443 
2444     g_return_if_fail (c != NULL);
2445     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
2446 
2447     screen_info = c->screen_info;
2448     display_info = screen_info->display_info;
2449 
2450     clientPassFocus(c->screen_info, c, exclude_list);
2451     if (FLAG_TEST (c->xfwm_flags, XFWM_FLAG_VISIBLE))
2452     {
2453         FLAG_UNSET (c->xfwm_flags, XFWM_FLAG_VISIBLE);
2454         c->ignore_unmap++;
2455         /* Adjust to urgency state as the window is not visible */
2456         clientUpdateUrgency (c);
2457     }
2458 
2459     myDisplayErrorTrapPush (display_info);
2460     XUnmapWindow (display_info->dpy, c->frame);
2461     XUnmapWindow (display_info->dpy, c->window);
2462     myDisplayErrorTrapPopIgnored (display_info);
2463 
2464     if (iconify)
2465     {
2466         FLAG_SET (c->flags, CLIENT_FLAG_ICONIFIED);
2467         setWMState (display_info, c->window, IconicState);
2468         if (!screen_info->show_desktop && !screen_info->params->cycle_minimized)
2469         {
2470             clientSetLast (c);
2471         }
2472     }
2473     clientSetNetActions (c);
2474     clientSetNetState (c);
2475 }
2476 
2477 void
clientWithdraw(Client * c,guint ws,gboolean iconify)2478 clientWithdraw (Client *c, guint ws, gboolean iconify)
2479 {
2480     Client *c2;
2481     GList *list_of_windows;
2482     GList *list;
2483 
2484     g_return_if_fail (c != NULL);
2485     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
2486 
2487     list_of_windows = clientListTransientOrModal (c);
2488     for (list = list_of_windows; list; list = g_list_next (list))
2489     {
2490         c2 = (Client *) list->data;
2491 
2492         /* Ignore request before if the window is not yet managed */
2493         if (!FLAG_TEST (c2->xfwm_flags, XFWM_FLAG_MANAGED))
2494         {
2495             continue;
2496         }
2497 
2498         if (FLAG_TEST (c2->flags, CLIENT_FLAG_STICKY) && !iconify)
2499         {
2500             continue;
2501         }
2502 
2503         if (clientIsTransientOrModalForGroup (c2))
2504         {
2505             if ((c2 != c) &&
2506                 clientTransientOrModalHasAncestor (c2, c2->win_workspace))
2507             {
2508                 /* Other ancestors for that transient for group are still
2509                  * visible on current workspace, so don't hide it...
2510                  */
2511                 continue;
2512             }
2513             if ((ws != c2->win_workspace) &&
2514                 clientTransientOrModalHasAncestor (c2, ws))
2515             {
2516                 /* ws is used when transitioning between desktops, to avoid
2517                    hiding a transient for group that will be shown again on the new
2518                    workspace (transient for groups can be transients for multiple
2519                    ancesors splitted across workspaces...)
2520                  */
2521                 continue;
2522             }
2523         }
2524         clientWithdrawSingle (c2, list_of_windows, iconify);
2525     }
2526     g_list_free (list_of_windows);
2527 
2528     /* Update working area as windows have been hidden */
2529     workspaceUpdateArea (c->screen_info);
2530 }
2531 
2532 void
clientWithdrawAll(Client * c,guint ws)2533 clientWithdrawAll (Client *c, guint ws)
2534 {
2535     GList *list;
2536     Client *c2;
2537     ScreenInfo *screen_info;
2538 
2539     g_return_if_fail (c != NULL);
2540     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
2541 
2542     screen_info = c->screen_info;
2543     for (list = screen_info->windows_stack; list; list = g_list_next (list))
2544     {
2545         c2 = (Client *) list->data;
2546 
2547         if ((c2 != c)
2548             && CLIENT_CAN_HIDE_WINDOW (c2)
2549             && !clientIsTransientOrModal (c2))
2550         {
2551             if (((!c) && (c2->win_workspace == ws))
2552                  || ((c) && !clientIsTransientOrModalFor (c, c2)
2553                          && (c2->win_workspace == c->win_workspace)))
2554             {
2555                 clientWithdraw (c2, ws, TRUE);
2556             }
2557         }
2558     }
2559 }
2560 
2561 void
clientClearAllShowDesktop(ScreenInfo * screen_info)2562 clientClearAllShowDesktop (ScreenInfo *screen_info)
2563 {
2564     GList *list;
2565 
2566     TRACE ("entering");
2567 
2568     if (screen_info->show_desktop)
2569     {
2570         for (list = screen_info->windows_stack; list; list = g_list_next (list))
2571         {
2572             Client *c = (Client *) list->data;
2573             FLAG_UNSET (c->xfwm_flags, XFWM_FLAG_WAS_SHOWN);
2574         }
2575         screen_info->show_desktop = FALSE;
2576         sendRootMessage (screen_info, NET_SHOWING_DESKTOP, screen_info->show_desktop,
2577                          myDisplayGetCurrentTime (screen_info->display_info));
2578     }
2579 }
2580 
2581 void
clientToggleShowDesktop(ScreenInfo * screen_info)2582 clientToggleShowDesktop (ScreenInfo *screen_info)
2583 {
2584     GList *list;
2585 
2586     TRACE ("entering");
2587 
2588     clientSetFocus (screen_info, NULL,
2589                     myDisplayGetCurrentTime (screen_info->display_info),
2590                     FOCUS_IGNORE_MODAL);
2591     if (screen_info->show_desktop)
2592     {
2593         for (list = screen_info->windows_stack; list; list = g_list_next (list))
2594         {
2595             Client *c = (Client *) list->data;
2596             if ((c->type & WINDOW_REGULAR_FOCUSABLE)
2597                 && !FLAG_TEST (c->flags, CLIENT_FLAG_ICONIFIED | CLIENT_FLAG_SKIP_TASKBAR))
2598             {
2599                 FLAG_SET (c->xfwm_flags, XFWM_FLAG_WAS_SHOWN);
2600                 clientWithdraw (c, c->win_workspace, TRUE);
2601             }
2602         }
2603         clientFocusTop (screen_info, WIN_LAYER_DESKTOP, myDisplayGetCurrentTime (screen_info->display_info));
2604     }
2605     else
2606     {
2607         for (list = g_list_last(screen_info->windows_stack); list; list = g_list_previous (list))
2608         {
2609             Client *c = (Client *) list->data;
2610             if (FLAG_TEST (c->xfwm_flags, XFWM_FLAG_WAS_SHOWN))
2611             {
2612                 clientShow (c, TRUE);
2613             }
2614             FLAG_UNSET (c->xfwm_flags, XFWM_FLAG_WAS_SHOWN);
2615         }
2616         clientFocusTop (screen_info, WIN_LAYER_FULLSCREEN, myDisplayGetCurrentTime (screen_info->display_info));
2617     }
2618 }
2619 
2620 void
clientActivate(Client * c,guint32 timestamp,gboolean source_is_application)2621 clientActivate (Client *c, guint32 timestamp, gboolean source_is_application)
2622 {
2623     ScreenInfo *screen_info;
2624     Client *focused;
2625     Client *ancestor;
2626 
2627     g_return_if_fail (c != NULL);
2628     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
2629 
2630     screen_info = c->screen_info;
2631     ancestor = clientGetTransientFor(c);
2632     focused = clientGetFocus ();
2633 
2634     if ((screen_info->current_ws == c->win_workspace) || (screen_info->params->activate_action != ACTIVATE_ACTION_NONE))
2635     {
2636         if ((focused) && (c != focused))
2637         {
2638             /* We might be able to avoid this if we are about to switch workspace */
2639             clientAdjustFullscreenLayer (focused, FALSE);
2640         }
2641         if (FLAG_TEST (c->xfwm_flags, XFWM_FLAG_WAS_SHOWN))
2642         {
2643             /* We are explicitely activating a window that was shown before show-desktop */
2644             clientClearAllShowDesktop (screen_info);
2645         }
2646         if (screen_info->current_ws != c->win_workspace)
2647         {
2648             if (screen_info->params->activate_action == ACTIVATE_ACTION_BRING)
2649             {
2650                 clientSetWorkspace (c, screen_info->current_ws, TRUE);
2651             }
2652             else
2653             {
2654                 workspaceSwitch (screen_info, c->win_workspace, NULL, FALSE, timestamp);
2655             }
2656         }
2657 
2658         clientShow (ancestor, TRUE);
2659         clientRaise (c, None);
2660 
2661         if (!source_is_application || screen_info->params->click_to_focus || (c->type & WINDOW_TYPE_DONT_FOCUS))
2662         {
2663             /*
2664                It's a bit tricky here, we want to honor the activate request only if:
2665 
2666                - The window use the _NET_ACTIVE_WINDOW protocol and identify itself as a pager,
2667                - Or we use the click to focus model, in that case we focus the window anyway,
2668                - Or the request comes from an application that we would not focus by default,
2669                  such as panels for example
2670              */
2671 
2672             clientSetFocus (screen_info, c, timestamp, NO_FOCUS_FLAG);
2673         }
2674     }
2675     else
2676     {
2677         TRACE ("setting WM_STATE_DEMANDS_ATTENTION flag on \"%s\" (0x%lx)", c->name, c->window);
2678         FLAG_SET (c->flags, CLIENT_FLAG_DEMANDS_ATTENTION);
2679         clientSetNetState (c);
2680     }
2681 }
2682 
2683 void
clientClose(Client * c)2684 clientClose (Client *c)
2685 {
2686     ScreenInfo *screen_info;
2687     DisplayInfo *display_info;
2688     guint32 timestamp;
2689 
2690     g_return_if_fail (c != NULL);
2691     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
2692 
2693     screen_info = c->screen_info;
2694     display_info = screen_info->display_info;
2695     timestamp = myDisplayGetCurrentTime (display_info);
2696     timestamp = myDisplayGetTime (display_info, timestamp);
2697 
2698     if (FLAG_TEST (c->wm_flags, WM_FLAG_DELETE))
2699     {
2700         sendClientMessage (screen_info, c->window, WM_DELETE_WINDOW, timestamp);
2701         if (FLAG_TEST (c->wm_flags, WM_FLAG_PING))
2702         {
2703             clientSendNetWMPing (c, timestamp);
2704         }
2705     }
2706     else
2707     {
2708         clientKill (c);
2709     }
2710 }
2711 
2712 void
clientKill(Client * c)2713 clientKill (Client *c)
2714 {
2715     ScreenInfo *screen_info;
2716     DisplayInfo *display_info;
2717 
2718     g_return_if_fail (c != NULL);
2719     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
2720 
2721     screen_info = c->screen_info;
2722     display_info = screen_info->display_info;
2723 
2724     myDisplayErrorTrapPush (display_info);
2725     XKillClient (display_info->dpy, c->window);
2726     myDisplayErrorTrapPopIgnored (display_info);
2727 }
2728 
2729 void
clientTerminate(Client * c)2730 clientTerminate (Client *c)
2731 {
2732     ScreenInfo *screen_info;
2733     DisplayInfo *display_info;
2734 
2735     g_return_if_fail (c != NULL);
2736     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
2737 
2738     screen_info = c->screen_info;
2739     display_info = screen_info->display_info;
2740 
2741     if ((c->hostname) && (display_info->hostname) && (c->pid > 0))
2742     {
2743         if (!strcmp (display_info->hostname, c->hostname))
2744         {
2745             TRACE ("sending client %s (pid %i) signal SIGKILL\n", c->name, c->pid);
2746 
2747             if (kill (c->pid, SIGKILL) < 0)
2748             {
2749                 g_warning ("Failed to kill client id %d: %s", c->pid, strerror (errno));
2750             }
2751         }
2752     }
2753 
2754     clientKill (c);
2755 }
2756 
2757 void
clientEnterContextMenuState(Client * c)2758 clientEnterContextMenuState (Client *c)
2759 {
2760     ScreenInfo *screen_info;
2761     DisplayInfo *display_info;
2762 
2763     g_return_if_fail (c != NULL);
2764     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
2765 
2766     screen_info = c->screen_info;
2767     display_info = screen_info->display_info;
2768 
2769     if (FLAG_TEST (c->wm_flags, WM_FLAG_CONTEXT_HELP))
2770     {
2771         sendClientMessage (c->screen_info, c->window, NET_WM_CONTEXT_HELP,
2772                            myDisplayGetCurrentTime (display_info));
2773     }
2774 }
2775 
2776 void
clientSetLayer(Client * c,guint l)2777 clientSetLayer (Client *c, guint l)
2778 {
2779     GList *list_of_windows = NULL;
2780     GList *list = NULL;
2781     Client *c2 = NULL;
2782 
2783     g_return_if_fail (c != NULL);
2784     TRACE ("client \"%s\" (0x%lx) on layer %d", c->name, c->window, l);
2785 
2786     list_of_windows = clientListTransientOrModal (c);
2787     for (list = list_of_windows; list; list = g_list_next (list))
2788     {
2789         c2 = (Client *) list->data;
2790         if (c2->win_layer != l)
2791         {
2792             TRACE ("setting client \"%s\" (0x%lx) layer to %d", c2->name,
2793                 c2->window, l);
2794             c2->win_layer = l;
2795         }
2796     }
2797     g_list_free (list_of_windows);
2798 
2799     if (clientGetLastRaise (c->screen_info) == c)
2800     {
2801         clientClearLastRaise (c->screen_info);
2802     }
2803 
2804     c2 = clientGetFocusOrPending ();
2805     if (c2 && (c2 != c) && (c2->win_layer == c->win_layer))
2806     {
2807         TRACE ("placing %s under %s", c->name, c2->name);
2808         clientLower (c, c2->frame);
2809     }
2810     else
2811     {
2812        TRACE ("placing %s on top of its layer %lu", c->name, c->win_layer);
2813        clientRaise (c, None);
2814     }
2815 }
2816 
2817 void
clientShade(Client * c)2818 clientShade (Client *c)
2819 {
2820     XWindowChanges wc;
2821     ScreenInfo *screen_info;
2822     DisplayInfo *display_info;
2823     unsigned long mask;
2824 
2825     g_return_if_fail (c != NULL);
2826     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
2827 
2828     if (!CLIENT_HAS_FRAME(c))
2829     {
2830         TRACE ("cowardly refusing to shade \"%s\" (0x%lx) because it has no title", c->name, c->window);
2831         return;
2832     }
2833     else if (FLAG_TEST (c->flags, CLIENT_FLAG_SHADED))
2834     {
2835         TRACE ("client \"%s\" (0x%lx) is already shaded", c->name, c->window);
2836         return;
2837     }
2838 
2839     screen_info = c->screen_info;
2840     display_info = screen_info->display_info;
2841 
2842     FLAG_SET (c->flags, CLIENT_FLAG_SHADED);
2843     if (FLAG_TEST (c->xfwm_flags, XFWM_FLAG_MANAGED))
2844     {
2845         mask = (CWWidth | CWHeight);
2846         if (clientConstrainPos (c, FALSE))
2847         {
2848             wc.x = c->x;
2849             wc.y = c->y;
2850             mask |= (CWX | CWY);
2851         }
2852 
2853         if (FLAG_TEST (c->xfwm_flags, XFWM_FLAG_VISIBLE))
2854         {
2855             c->ignore_unmap++;
2856         }
2857         /*
2858          * Shading unmaps the client window. We therefore have to transfer focus to its frame
2859          * so that focus doesn't return to root. clientSetFocus() will take care of focusing
2860          * the window frame since the SHADED flag is now set.
2861          */
2862         if (c == clientGetFocus ())
2863         {
2864             clientSetFocus (screen_info, c, myDisplayGetCurrentTime (display_info), FOCUS_FORCE);
2865         }
2866 
2867         myDisplayErrorTrapPush (display_info);
2868         XUnmapWindow (display_info->dpy, c->window);
2869         myDisplayErrorTrapPopIgnored (display_info);
2870 
2871         wc.width = c->width;
2872         wc.height = c->height;
2873         clientConfigure (c, &wc, mask, CFG_FORCE_REDRAW);
2874     }
2875     clientSetNetState (c);
2876 }
2877 
2878 void
clientUnshade(Client * c)2879 clientUnshade (Client *c)
2880 {
2881     XWindowChanges wc;
2882     ScreenInfo *screen_info;
2883     DisplayInfo *display_info;
2884 
2885     g_return_if_fail (c != NULL);
2886     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
2887 
2888     if (!FLAG_TEST (c->flags, CLIENT_FLAG_SHADED))
2889     {
2890         TRACE ("\"%s\" (0x%lx) is not shaded", c->name, c->window);
2891         return;
2892     }
2893 
2894     screen_info = c->screen_info;
2895     display_info = screen_info->display_info;
2896 
2897     FLAG_UNSET (c->flags, CLIENT_FLAG_SHADED);
2898     if (FLAG_TEST (c->xfwm_flags, XFWM_FLAG_MANAGED))
2899     {
2900         if (FLAG_TEST (c->xfwm_flags, XFWM_FLAG_VISIBLE))
2901         {
2902             myDisplayErrorTrapPush (display_info);
2903             XMapWindow (display_info->dpy, c->window);
2904             myDisplayErrorTrapPopIgnored (display_info);
2905         }
2906         /*
2907          * Unshading will show the client window, so we need to focus it when unshading.
2908          */
2909         if (c == clientGetFocus ())
2910         {
2911             clientSetFocus (screen_info, c, myDisplayGetCurrentTime (display_info), FOCUS_FORCE);
2912         }
2913 
2914         wc.width = c->width;
2915         wc.height = c->height;
2916         clientConfigure (c, &wc, CWWidth | CWHeight, CFG_FORCE_REDRAW);
2917     }
2918     clientSetNetState (c);
2919 }
2920 
2921 void
clientToggleShaded(Client * c)2922 clientToggleShaded (Client *c)
2923 {
2924     if (FLAG_TEST (c->flags, CLIENT_FLAG_SHADED))
2925     {
2926         clientUnshade (c);
2927     }
2928     else
2929     {
2930         clientShade (c);
2931     }
2932 }
2933 
2934 void
clientStick(Client * c,gboolean include_transients)2935 clientStick (Client *c, gboolean include_transients)
2936 {
2937     ScreenInfo *screen_info;
2938     DisplayInfo *display_info;
2939     Client *c2;
2940     GList *list_of_windows;
2941     GList *list;
2942 
2943     g_return_if_fail (c != NULL);
2944     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
2945 
2946     screen_info = c->screen_info;
2947     display_info = screen_info->display_info;
2948 
2949     if (include_transients)
2950     {
2951         list_of_windows = clientListTransientOrModal (c);
2952         for (list = list_of_windows; list; list = g_list_next (list))
2953         {
2954             c2 = (Client *) list->data;
2955             TRACE ("sticking client \"%s\" (0x%lx)", c2->name, c2->window);
2956             FLAG_SET (c2->flags, CLIENT_FLAG_STICKY);
2957             setHint (display_info, c2->window, NET_WM_DESKTOP, (unsigned long) ALL_WORKSPACES);
2958             frameQueueDraw (c2, FALSE);
2959         }
2960         g_list_free (list_of_windows);
2961     }
2962     else
2963     {
2964         TRACE ("sticking client \"%s\" (0x%lx)", c->name, c->window);
2965         FLAG_SET (c->flags, CLIENT_FLAG_STICKY);
2966         setHint (display_info, c->window, NET_WM_DESKTOP, (unsigned long) ALL_WORKSPACES);
2967     }
2968     clientSetWorkspace (c, screen_info->current_ws, TRUE);
2969     clientSetNetState (c);
2970 }
2971 
2972 void
clientUnstick(Client * c,gboolean include_transients)2973 clientUnstick (Client *c, gboolean include_transients)
2974 {
2975     ScreenInfo *screen_info;
2976     DisplayInfo *display_info;
2977     Client *c2;
2978     GList *list_of_windows;
2979     GList *list;
2980 
2981     g_return_if_fail (c != NULL);
2982     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
2983 
2984     screen_info = c->screen_info;
2985     display_info = screen_info->display_info;
2986 
2987     if (include_transients)
2988     {
2989         list_of_windows = clientListTransientOrModal (c);
2990         for (list = list_of_windows; list; list = g_list_next (list))
2991         {
2992             c2 = (Client *) list->data;
2993             TRACE ("unsticking client \"%s\" (0x%lx)", c2->name, c2->window);
2994             FLAG_UNSET (c2->flags, CLIENT_FLAG_STICKY);
2995             setHint (display_info, c2->window, NET_WM_DESKTOP, (unsigned long) screen_info->current_ws);
2996             frameQueueDraw (c2, FALSE);
2997         }
2998         g_list_free (list_of_windows);
2999     }
3000     else
3001     {
3002         TRACE ("unsticking client \"%s\" (0x%lx)", c->name, c->window);
3003         FLAG_UNSET (c->flags, CLIENT_FLAG_STICKY);
3004         setHint (display_info, c->window, NET_WM_DESKTOP, (unsigned long) screen_info->current_ws);
3005     }
3006     clientSetWorkspace (c, screen_info->current_ws, TRUE);
3007     clientSetNetState (c);
3008 }
3009 
3010 void
clientToggleSticky(Client * c,gboolean include_transients)3011 clientToggleSticky (Client *c, gboolean include_transients)
3012 {
3013     g_return_if_fail (c != NULL);
3014     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
3015 
3016     if (FLAG_TEST (c->flags, CLIENT_FLAG_STICKY))
3017     {
3018         clientUnstick (c, include_transients);
3019     }
3020     else
3021     {
3022         clientStick (c, include_transients);
3023     }
3024 }
3025 
3026 void
clientUpdateFullscreenSize(Client * c)3027 clientUpdateFullscreenSize (Client *c)
3028 {
3029     ScreenInfo *screen_info;
3030     XWindowChanges wc;
3031     GdkRectangle monitor, rect;
3032     int i;
3033 
3034     g_return_if_fail (c != NULL);
3035     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
3036 
3037     screen_info = c->screen_info;
3038 
3039     if (FLAG_TEST (c->flags, CLIENT_FLAG_FULLSCREEN))
3040     {
3041         if (FLAG_TEST (c->flags, CLIENT_FLAG_FULLSCREEN_MONITORS))
3042         {
3043             /* Monitor numbering is from Xinerama */
3044             myScreenGetXineramaMonitorGeometry (screen_info, c->fullscreen_monitors[0], &rect);
3045             for (i = 1; i < 4; i++)
3046             {
3047                 myScreenGetXineramaMonitorGeometry (screen_info, c->fullscreen_monitors[i], &monitor);
3048                 gdk_rectangle_union (&rect, &monitor, &rect);
3049             }
3050         }
3051         else
3052         {
3053             int cx, cy;
3054 
3055             cx = frameX (c) + (frameWidth (c) / 2);
3056             cy = frameY (c) + (frameHeight (c) / 2);
3057 
3058             myScreenFindMonitorAtPoint (screen_info, cx, cy, &rect);
3059         }
3060 
3061         wc.x = rect.x;
3062         wc.y = rect.y;
3063         wc.width = rect.width;
3064         wc.height = rect.height;
3065     }
3066     else
3067     {
3068         wc.x = c->pre_fullscreen_geometry.x;
3069         wc.y = c->pre_fullscreen_geometry.y;
3070         wc.width = c->pre_fullscreen_geometry.width;
3071         wc.height = c->pre_fullscreen_geometry.height;
3072     }
3073 
3074     if (FLAG_TEST (c->xfwm_flags, XFWM_FLAG_MANAGED))
3075     {
3076         clientConfigure (c, &wc, CWX | CWY | CWWidth | CWHeight, CFG_FORCE_REDRAW);
3077     }
3078     else
3079     {
3080         c->x = wc.x;
3081         c->y = wc.y;
3082         c->height = wc.height;
3083         c->width = wc.width;
3084     }
3085 }
3086 
clientToggleFullscreen(Client * c)3087 void clientToggleFullscreen (Client *c)
3088 {
3089     g_return_if_fail (c != NULL);
3090     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
3091 
3092     /*can we switch to full screen, does it make any sense? */
3093     if (!FLAG_TEST (c->flags, CLIENT_FLAG_FULLSCREEN) && (c->size->flags & PMaxSize))
3094     {
3095         GdkRectangle rect;
3096         int cx, cy;
3097 
3098         cx = frameX (c) + (frameWidth (c) / 2);
3099         cy = frameY (c) + (frameHeight (c) / 2);
3100 
3101         myScreenFindMonitorAtPoint (c->screen_info, cx, cy, &rect);
3102 
3103         if ((c->size->max_width < rect.width) || (c->size->max_height < rect.height))
3104         {
3105             return;
3106         }
3107     }
3108 
3109     if (!clientIsTransientOrModal (c) && (c->type == WINDOW_NORMAL) && !FLAG_TEST (c->flags, CLIENT_FLAG_SHADED))
3110     {
3111         FLAG_TOGGLE (c->flags, CLIENT_FLAG_FULLSCREEN);
3112         clientUpdateFullscreenState (c);
3113     }
3114 }
3115 
clientSetFullscreenMonitor(Client * c,gint top,gint bottom,gint left,gint right)3116 void clientSetFullscreenMonitor (Client *c, gint top, gint bottom, gint left, gint right)
3117 {
3118     ScreenInfo *screen_info;
3119     DisplayInfo *display_info;
3120     gint num_monitors;
3121 
3122     g_return_if_fail (c != NULL);
3123     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
3124 
3125     screen_info = c->screen_info;
3126     display_info = screen_info->display_info;
3127 
3128     num_monitors = xfwm_get_n_monitors (screen_info->gscr);
3129     if ((top >= 0)    && (top < num_monitors)    &&
3130         (bottom >= 0) && (bottom < num_monitors) &&
3131         (left >= 0)   && (left < num_monitors)   &&
3132         (right >= 0)  && (right < num_monitors))
3133     {
3134         c->fullscreen_monitors[0] = top;
3135         c->fullscreen_monitors[1] = bottom;
3136         c->fullscreen_monitors[2] = left;
3137         c->fullscreen_monitors[3] = right;
3138         FLAG_SET (c->flags, CLIENT_FLAG_FULLSCREEN_MONITORS);
3139     }
3140     else
3141     {
3142         c->fullscreen_monitors[0] = 0;
3143         c->fullscreen_monitors[1] = 0;
3144         c->fullscreen_monitors[2] = 0;
3145         c->fullscreen_monitors[3] = 0;
3146         FLAG_UNSET (c->flags, CLIENT_FLAG_FULLSCREEN_MONITORS);
3147     }
3148     if (FLAG_TEST (c->flags, CLIENT_FLAG_FULLSCREEN))
3149     {
3150         clientUpdateFullscreenSize (c);
3151     }
3152     if (FLAG_TEST (c->flags, CLIENT_FLAG_FULLSCREEN_MONITORS))
3153     {
3154         setNetFullscreenMonitors (display_info, c->window, top, bottom, left, right);
3155     }
3156 }
3157 
clientToggleLayerAbove(Client * c)3158 void clientToggleLayerAbove (Client *c)
3159 {
3160     g_return_if_fail (c != NULL);
3161     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
3162 
3163     if ((c->type & WINDOW_REGULAR_FOCUSABLE) &&
3164         !clientIsValidTransientOrModal (c) &&
3165         !FLAG_TEST (c->flags, CLIENT_FLAG_FULLSCREEN))
3166     {
3167         FLAG_UNSET (c->flags, CLIENT_FLAG_BELOW);
3168         FLAG_TOGGLE (c->flags, CLIENT_FLAG_ABOVE);
3169         clientUpdateLayerState (c);
3170     }
3171 }
3172 
clientToggleLayerBelow(Client * c)3173 void clientToggleLayerBelow (Client *c)
3174 {
3175     g_return_if_fail (c != NULL);
3176     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
3177 
3178     if ((c->type & WINDOW_REGULAR_FOCUSABLE) &&
3179         !clientIsValidTransientOrModal (c) &&
3180         !FLAG_TEST (c->flags, CLIENT_FLAG_FULLSCREEN))
3181     {
3182         FLAG_UNSET (c->flags, CLIENT_FLAG_ABOVE);
3183         FLAG_TOGGLE (c->flags, CLIENT_FLAG_BELOW);
3184         clientUpdateLayerState (c);
3185     }
3186 }
3187 
clientSetLayerNormal(Client * c)3188 void clientSetLayerNormal (Client *c)
3189 {
3190     g_return_if_fail (c != NULL);
3191     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
3192 
3193     if (!FLAG_TEST (c->flags, CLIENT_FLAG_FULLSCREEN))
3194     {
3195         FLAG_UNSET (c->flags, CLIENT_FLAG_ABOVE | CLIENT_FLAG_BELOW);
3196         clientUpdateLayerState (c);
3197     }
3198 }
3199 
3200 void
clientUpdateMaximizeSize(Client * c)3201 clientUpdateMaximizeSize (Client *c)
3202 {
3203     g_return_if_fail (c != NULL);
3204     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
3205 
3206     /* Recompute size and position of maximized windows */
3207     if (FLAG_TEST (c->flags, CLIENT_FLAG_MAXIMIZED))
3208     {
3209         clientRecomputeMaximizeSize (c);
3210         clientReconfigure (c, CFG_NOTIFY);
3211     }
3212 }
3213 
3214 void
clientRemoveMaximizeFlag(Client * c)3215 clientRemoveMaximizeFlag (Client *c)
3216 {
3217     g_return_if_fail (c != NULL);
3218     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
3219 
3220     FLAG_UNSET (c->flags, CLIENT_FLAG_MAXIMIZED | CLIENT_FLAG_RESTORE_SIZE_POS);
3221     frameQueueDraw (c, FALSE);
3222     clientSetNetActions (c);
3223     clientSetNetState (c);
3224 }
3225 
3226 static void
clientNewMaxState(Client * c,XWindowChanges * wc,int mode)3227 clientNewMaxState (Client *c, XWindowChanges *wc, int mode)
3228 {
3229     if (FLAG_TEST_ALL (mode, CLIENT_FLAG_MAXIMIZED))
3230     {
3231         /*
3232          * We need to test specifically for full de-maximization
3233          * otherwise it's too confusing when the window changes
3234          * from horiz to vertical maximization or vice-versa.
3235          */
3236         if (FLAG_TEST_ALL (c->flags, CLIENT_FLAG_MAXIMIZED))
3237         {
3238             FLAG_UNSET (c->flags, CLIENT_FLAG_MAXIMIZED | CLIENT_FLAG_RESTORE_SIZE_POS);
3239             wc->x = c->saved_geometry.x;
3240             wc->y = c->saved_geometry.y;
3241             wc->width = c->saved_geometry.width;
3242             wc->height = c->saved_geometry.height;
3243 
3244             return;
3245         }
3246         else if (FLAG_TEST (c->flags, CLIENT_FLAG_MAXIMIZED_HORIZ))
3247         {
3248             FLAG_SET (c->flags, CLIENT_FLAG_MAXIMIZED_VERT);
3249             return;
3250         }
3251         else if (FLAG_TEST (c->flags, CLIENT_FLAG_MAXIMIZED_VERT))
3252         {
3253             FLAG_SET (c->flags, CLIENT_FLAG_MAXIMIZED_HORIZ);
3254             return;
3255         }
3256     }
3257 
3258     if (FLAG_TEST (mode, CLIENT_FLAG_MAXIMIZED_HORIZ))
3259     {
3260         if (!FLAG_TEST (c->flags, CLIENT_FLAG_MAXIMIZED_HORIZ))
3261         {
3262             FLAG_SET (c->flags, CLIENT_FLAG_MAXIMIZED_HORIZ | CLIENT_FLAG_RESTORE_SIZE_POS);
3263         }
3264         else
3265         {
3266             FLAG_UNSET (c->flags, CLIENT_FLAG_MAXIMIZED_HORIZ);
3267             if (!FLAG_TEST (c->flags, CLIENT_FLAG_MAXIMIZED))
3268             {
3269                 FLAG_UNSET (c->flags, CLIENT_FLAG_RESTORE_SIZE_POS);
3270             }
3271             wc->x = c->saved_geometry.x;
3272             wc->y = c->saved_geometry.y;
3273             wc->width = c->saved_geometry.width;
3274             wc->height = c->saved_geometry.height;
3275         }
3276     }
3277 
3278     if (FLAG_TEST (mode, CLIENT_FLAG_MAXIMIZED_VERT))
3279     {
3280         if (!FLAG_TEST (c->flags, CLIENT_FLAG_MAXIMIZED_VERT))
3281         {
3282             FLAG_SET (c->flags, CLIENT_FLAG_MAXIMIZED_VERT | CLIENT_FLAG_RESTORE_SIZE_POS);
3283         }
3284         else
3285         {
3286             FLAG_UNSET (c->flags, CLIENT_FLAG_MAXIMIZED_VERT);
3287             if (!FLAG_TEST (c->flags, CLIENT_FLAG_MAXIMIZED))
3288             {
3289                 FLAG_UNSET (c->flags, CLIENT_FLAG_RESTORE_SIZE_POS);
3290             }
3291             wc->x = c->saved_geometry.x;
3292             wc->y = c->saved_geometry.y;
3293             wc->width = c->saved_geometry.width;
3294             wc->height = c->saved_geometry.height;
3295         }
3296     }
3297 }
3298 
3299 static gboolean
clientNewTileSize(Client * c,XWindowChanges * wc,GdkRectangle * rect,tilePositionType tile)3300 clientNewTileSize (Client *c, XWindowChanges *wc, GdkRectangle *rect, tilePositionType tile)
3301 {
3302     ScreenInfo *screen_info;
3303     int full_x, full_y, full_w, full_h;
3304 
3305     screen_info = c->screen_info;
3306 
3307     full_x = MAX (screen_info->params->xfwm_margins[STRUTS_LEFT], rect->x);
3308     full_y = MAX (screen_info->params->xfwm_margins[STRUTS_TOP], rect->y);
3309     full_w = MIN (screen_info->width - screen_info->params->xfwm_margins[STRUTS_RIGHT],
3310                   rect->x + rect->width) - full_x;
3311     full_h = MIN (screen_info->height - screen_info->params->xfwm_margins[STRUTS_BOTTOM],
3312                   rect->y + rect->height) - full_y;
3313     clientMaxSpace (screen_info, &full_x, &full_y, &full_w, &full_h);
3314 
3315     switch (tile)
3316     {
3317         case TILE_UP:
3318             wc->x = full_x + frameExtentLeft (c);
3319             wc->y = full_y + frameExtentTop (c);
3320             wc->width = full_w - frameExtentLeft (c) - frameExtentRight (c);
3321             wc->height = full_h / 2 - frameExtentTop (c) - frameExtentBottom (c);
3322             break;
3323         case TILE_DOWN:
3324             wc->x = full_x + frameExtentLeft (c);
3325             wc->y = full_y + full_h / 2 + frameExtentTop (c);
3326             wc->width = full_w - frameExtentLeft (c) - frameExtentRight (c);
3327             wc->height = full_h - full_h / 2 - frameExtentTop (c) - frameExtentBottom (c);
3328             break;
3329         case TILE_LEFT:
3330             wc->x = full_x + frameExtentLeft (c);
3331             wc->y = full_y + frameExtentTop (c);
3332             wc->width = full_w / 2 - frameExtentLeft (c) - frameExtentRight (c);
3333             wc->height = full_h - frameExtentTop (c) - frameExtentBottom (c);
3334             break;
3335         case TILE_RIGHT:
3336             wc->x = full_x + full_w / 2 + frameExtentLeft (c);
3337             wc->y = full_y + frameExtentTop (c);
3338             wc->width = full_w - full_w / 2 - frameExtentLeft (c) - frameExtentRight (c);
3339             wc->height = full_h - frameExtentTop (c) - frameExtentBottom (c);
3340             break;
3341         case TILE_DOWN_LEFT:
3342             wc->x = full_x + frameExtentLeft (c);
3343             wc->y = full_y + full_h / 2 + frameExtentTop (c);
3344             wc->width = full_w / 2 - frameExtentLeft (c) - frameExtentRight (c);
3345             wc->height = full_h - full_h / 2 - frameExtentTop (c) - frameExtentBottom (c);
3346             break;
3347         case TILE_DOWN_RIGHT:
3348             wc->x = full_x + full_w /2 + frameExtentLeft (c);
3349             wc->y = full_y + full_h / 2 + frameExtentTop (c);
3350             wc->width = full_w - full_w / 2 - frameExtentLeft (c) - frameExtentRight (c);
3351             wc->height = full_h - full_h / 2 - frameExtentTop (c) - frameExtentBottom (c);
3352             break;
3353         case TILE_UP_LEFT:
3354             wc->x = full_x + frameExtentLeft (c);
3355             wc->y = full_y + frameExtentTop (c);
3356             wc->width = full_w / 2 - frameExtentLeft (c) - frameExtentRight (c);
3357             wc->height = full_h / 2 - frameExtentTop (c) - frameExtentBottom (c);
3358             break;
3359         case TILE_UP_RIGHT:
3360             wc->x = full_x + full_w /2 + frameExtentLeft (c);
3361             wc->y = full_y + frameExtentTop (c);
3362             wc->width = full_w - full_w / 2 - frameExtentLeft (c) - frameExtentRight (c);
3363             wc->height = full_h / 2 - frameExtentTop (c) - frameExtentBottom (c);
3364             break;
3365         default:
3366             break;
3367     }
3368 
3369     return ((wc->height >= c->size->min_height) && (wc->height <= c->size->max_height) &&
3370             (wc->width >= c->size->min_width) && (wc->width <= c->size->max_width));
3371 }
3372 
3373 static gboolean
clientNewMaxSize(Client * c,XWindowChanges * wc,GdkRectangle * rect)3374 clientNewMaxSize (Client *c, XWindowChanges *wc, GdkRectangle *rect)
3375 {
3376     ScreenInfo *screen_info;
3377     int full_x, full_y, full_w, full_h;
3378 
3379     screen_info = c->screen_info;
3380 
3381     full_x = MAX (screen_info->params->xfwm_margins[STRUTS_LEFT], rect->x);
3382     full_y = MAX (screen_info->params->xfwm_margins[STRUTS_TOP], rect->y);
3383     full_w = MIN (screen_info->width - screen_info->params->xfwm_margins[STRUTS_RIGHT],
3384                   rect->x + rect->width) - full_x;
3385     full_h = MIN (screen_info->height - screen_info->params->xfwm_margins[STRUTS_BOTTOM],
3386                   rect->y + rect->height) - full_y;
3387     clientMaxSpace (screen_info, &full_x, &full_y, &full_w, &full_h);
3388 
3389     if (FLAG_TEST (c->flags, CLIENT_FLAG_MAXIMIZED_HORIZ))
3390     {
3391         /* Adjust size to the widest size available, for the current vertical position/height */
3392         wc->x = full_x + frameExtentLeft (c);
3393         wc->width = full_w - frameExtentLeft (c) - frameExtentRight (c);
3394     }
3395 
3396     if (FLAG_TEST (c->flags, CLIENT_FLAG_MAXIMIZED_VERT))
3397     {
3398         /* Adjust size to the tallest size available, for the current horizontal position/width */
3399         wc->y = full_y + frameExtentTop (c);
3400         wc->height = full_h - frameExtentTop (c) - frameExtentBottom (c);
3401     }
3402 
3403     return ((wc->height >= c->size->min_height) && (wc->height <= c->size->max_height) &&
3404             (wc->width >= c->size->min_width) && (wc->width <= c->size->max_width));
3405 }
3406 
3407 gboolean
clientToggleMaximized(Client * c,int mode,gboolean restore_position)3408 clientToggleMaximized (Client *c, int mode, gboolean restore_position)
3409 {
3410     g_return_val_if_fail (c != NULL, FALSE);
3411     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
3412 
3413     if (!CLIENT_CAN_MAXIMIZE_WINDOW (c))
3414     {
3415         return FALSE;
3416     }
3417 
3418     return clientToggleMaximizedAtPoint(c,
3419                                 frameX (c) + (frameWidth (c) / 2),
3420                                 frameY (c) + (frameHeight (c) / 2),
3421                                 mode, restore_position);
3422 }
3423 
3424 gboolean
clientToggleMaximizedAtPoint(Client * c,gint cx,gint cy,int mode,gboolean restore_position)3425 clientToggleMaximizedAtPoint (Client *c, gint cx, gint cy, int mode, gboolean restore_position)
3426 {
3427     DisplayInfo *display_info;
3428     ScreenInfo *screen_info;
3429     XWindowChanges wc;
3430     GdkRectangle rect;
3431     unsigned long old_flags;
3432 
3433     g_return_val_if_fail (c != NULL, FALSE);
3434     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
3435 
3436     if (!CLIENT_CAN_MAXIMIZE_WINDOW (c))
3437     {
3438         return FALSE;
3439     }
3440 
3441     if (c->tile_mode != TILE_NONE)
3442     {
3443         clientUntile (c);
3444     }
3445 
3446     screen_info = c->screen_info;
3447     display_info = screen_info->display_info;
3448     myScreenFindMonitorAtPoint (screen_info, cx, cy, &rect);
3449 
3450     wc.x = c->x;
3451     wc.y = c->y;
3452     wc.width = c->width;
3453     wc.height = c->height;
3454 
3455     if (restore_position &&
3456         FLAG_TEST (mode, CLIENT_FLAG_MAXIMIZED))
3457     {
3458         clientSaveSizePos (c);
3459     }
3460 
3461     old_flags = c->flags;
3462 
3463     /* 1) Compute the new state */
3464     clientNewMaxState (c, &wc, mode);
3465 
3466     /* 2) Compute the new size, based on the state */
3467     if (!clientNewMaxSize (c, &wc, &rect))
3468     {
3469         c->flags = old_flags;
3470         return FALSE;
3471     }
3472 
3473     /* 3) Update size and position fields */
3474     c->x = wc.x;
3475     c->y = wc.y;
3476     c->height = wc.height;
3477     c->width = wc.width;
3478 
3479     /* Maximizing may remove decoration on the side, update NET_FRAME_EXTENTS accordingly */
3480     setNetFrameExtents (display_info,
3481                         c->window,
3482                         frameTop (c),
3483                         frameLeft (c),
3484                         frameRight (c),
3485                         frameBottom (c));
3486 
3487     /* Maximized windows w/out border cannot be resized, update allowed actions */
3488     clientSetNetActions (c);
3489     if (restore_position && FLAG_TEST (c->xfwm_flags, XFWM_FLAG_MANAGED))
3490     {
3491         if (FLAG_TEST (c->flags, CLIENT_FLAG_SHADED))
3492         {
3493             /* It's a shame, we are configuring the same client twice in a row */
3494             clientUnshade (c);
3495         }
3496         clientConfigure (c, &wc, CWWidth | CWHeight | CWX | CWY, CFG_FORCE_REDRAW);
3497     }
3498     /* Do not update the state while moving/resizing, CSD windows may resize */
3499     if (!FLAG_TEST (c->xfwm_flags, XFWM_FLAG_MOVING_RESIZING))
3500     {
3501         clientSetNetState (c);
3502     }
3503 
3504     return TRUE;
3505 }
3506 
3507 gboolean
clientTile(Client * c,gint cx,gint cy,tilePositionType tile,gboolean send_configure,gboolean restore_position)3508 clientTile (Client *c, gint cx, gint cy, tilePositionType tile, gboolean send_configure, gboolean restore_position)
3509 {
3510     DisplayInfo *display_info;
3511     ScreenInfo *screen_info;
3512     XWindowChanges wc;
3513     GdkRectangle rect;
3514     unsigned long old_flags;
3515 
3516     g_return_val_if_fail (c != NULL, FALSE);
3517     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
3518 
3519     if (!CLIENT_CAN_TILE_WINDOW (c))
3520     {
3521         return FALSE;
3522     }
3523 
3524     screen_info = c->screen_info;
3525     display_info = screen_info->display_info;
3526     myScreenFindMonitorAtPoint (screen_info, cx, cy, &rect);
3527 
3528     wc.x = c->x;
3529     wc.y = c->y;
3530     wc.width = c->width;
3531     wc.height = c->height;
3532 
3533     if (restore_position)
3534     {
3535         clientSaveSizePos (c);
3536     }
3537 
3538     old_flags = c->flags;
3539     FLAG_UNSET (c->flags, CLIENT_FLAG_MAXIMIZED);
3540     if (!clientNewTileSize (c, &wc, &rect, tile))
3541     {
3542         c->flags = old_flags;
3543         return FALSE;
3544     }
3545     FLAG_SET (c->flags, CLIENT_FLAG_RESTORE_SIZE_POS);
3546     c->tile_mode = tile;
3547 
3548     c->x = wc.x;
3549     c->y = wc.y;
3550     c->height = wc.height;
3551     c->width = wc.width;
3552 
3553     if (send_configure)
3554     {
3555         setNetFrameExtents (display_info,
3556                             c->window,
3557                             frameTop (c),
3558                             frameLeft (c),
3559                             frameRight (c),
3560                             frameBottom (c));
3561 
3562         clientSetNetActions (c);
3563         clientReconfigure (c, CFG_FORCE_REDRAW);
3564     }
3565     /* Do not update the state while moving/resizing, CSD windows may resize */
3566     if (!FLAG_TEST (c->xfwm_flags, XFWM_FLAG_MOVING_RESIZING))
3567     {
3568         clientSetNetState (c);
3569     }
3570 
3571     return TRUE;
3572 }
3573 
3574 void
clientUntile(Client * c)3575 clientUntile (Client *c)
3576 {
3577     g_return_if_fail (c != NULL);
3578     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
3579 
3580     c->tile_mode = TILE_NONE;
3581 }
3582 
3583 gboolean
clientToggleTile(Client * c,tilePositionType tile)3584 clientToggleTile (Client *c, tilePositionType tile)
3585 {
3586     DisplayInfo *display_info;
3587     ScreenInfo *screen_info;
3588 
3589     g_return_val_if_fail (c != NULL, FALSE);
3590     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
3591 
3592     screen_info = c->screen_info;
3593     display_info = screen_info->display_info;
3594 
3595     if (c->tile_mode == tile)
3596     {
3597         clientUntile (c);
3598         clientRestoreSizePos (c);
3599         setNetFrameExtents (display_info,
3600                             c->window,
3601                             frameTop (c),
3602                             frameLeft (c),
3603                             frameRight (c),
3604                             frameBottom (c));
3605 
3606         clientSetNetActions (c);
3607         clientReconfigure (c, CFG_FORCE_REDRAW);
3608 
3609         return TRUE;
3610     }
3611     else
3612     {
3613         return clientTile (c,
3614                            frameX (c) + frameWidth (c) / 2,
3615                            frameY (c) + frameHeight (c) / 2,
3616                            tile,
3617                            TRUE,
3618                            TRUE);
3619     }
3620 }
3621 
3622 
3623 static void
clientRecomputeTileSize(Client * c)3624 clientRecomputeTileSize (Client *c)
3625 {
3626     ScreenInfo *screen_info;
3627     XWindowChanges wc;
3628     GdkRectangle rect;
3629 
3630     g_return_if_fail (c != NULL);
3631     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
3632 
3633     screen_info = c->screen_info;
3634 
3635     myScreenFindMonitorAtPoint (screen_info,
3636                                 frameX (c) + frameWidth (c) / 2,
3637                                 frameY (c) + frameHeight (c) / 2,
3638                                 &rect);
3639 
3640     if (!clientNewTileSize (c, &wc, &rect, c->tile_mode))
3641     {
3642         return;
3643     }
3644 
3645     c->x = wc.x;
3646     c->y = wc.y;
3647     c->width = wc.width;
3648     c->height = wc.height;
3649 }
3650 
3651 void
clientUpdateTileSize(Client * c)3652 clientUpdateTileSize (Client *c)
3653 {
3654     g_return_if_fail (c != NULL);
3655     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
3656 
3657     /* Recompute size and position of maximized windows */
3658     if (c->tile_mode != TILE_NONE)
3659     {
3660         clientRecomputeTileSize (c);
3661         clientReconfigure (c, CFG_NOTIFY);
3662     }
3663 }
3664 
3665 void
clientUpdateOpacity(Client * c)3666 clientUpdateOpacity (Client *c)
3667 {
3668     ScreenInfo *screen_info;
3669     DisplayInfo *display_info;
3670     Client *focused;
3671     gboolean opaque;
3672 
3673     g_return_if_fail (c != NULL);
3674 
3675     screen_info = c->screen_info;
3676     display_info = screen_info->display_info;
3677     if (!compositorIsUsable (display_info))
3678     {
3679         return;
3680     }
3681 
3682     focused = clientGetFocus ();
3683     opaque = (FLAG_TEST(c->type, WINDOW_TYPE_DONT_PLACE | WINDOW_TYPE_DONT_FOCUS)
3684               || (focused == c));
3685 
3686     clientSetOpacity (c, c->opacity, OPACITY_INACTIVE, opaque ? 0 : OPACITY_INACTIVE);
3687 }
3688 
3689 void
clientUpdateAllOpacity(ScreenInfo * screen_info)3690 clientUpdateAllOpacity (ScreenInfo *screen_info)
3691 {
3692     DisplayInfo *display_info;
3693     Client *c;
3694     guint i;
3695 
3696     g_return_if_fail (screen_info != NULL);
3697 
3698     display_info = screen_info->display_info;
3699     if (!compositorIsUsable (display_info))
3700     {
3701         return;
3702     }
3703 
3704     for (c = screen_info->clients, i = 0; i < screen_info->client_count; c = c->next, ++i)
3705     {
3706         clientUpdateOpacity (c);
3707     }
3708 }
3709 
3710 void
clientSetOpacity(Client * c,guint32 opacity,guint32 clear,guint32 xor)3711 clientSetOpacity (Client *c, guint32 opacity, guint32 clear, guint32 xor)
3712 {
3713     ScreenInfo *screen_info;
3714     DisplayInfo *display_info;
3715     guint32 applied;
3716 
3717     screen_info = c->screen_info;
3718     display_info = screen_info->display_info;
3719 
3720     if (!compositorIsUsable (display_info))
3721     {
3722         return;
3723     }
3724 
3725     c->opacity_flags = (c->opacity_flags & ~clear) ^ xor;
3726 
3727     if (FLAG_TEST (c->xfwm_flags, XFWM_FLAG_OPACITY_LOCKED))
3728     {
3729         applied = c->opacity;
3730     }
3731     else
3732     {
3733         long long multiplier = 1, divisor = 1;
3734 
3735         c->opacity = applied = opacity;
3736 
3737         if (FLAG_TEST (c->opacity_flags, OPACITY_MOVE))
3738         {
3739             multiplier *= c->screen_info->params->move_opacity;
3740             divisor *= 100;
3741         }
3742         if (FLAG_TEST (c->opacity_flags, OPACITY_RESIZE))
3743         {
3744             multiplier *= c->screen_info->params->resize_opacity;
3745             divisor *= 100;
3746         }
3747         if (FLAG_TEST (c->opacity_flags, OPACITY_INACTIVE))
3748         {
3749             multiplier *= c->screen_info->params->inactive_opacity;
3750             divisor *= 100;
3751         }
3752 
3753         applied = (guint32) (((long long) applied * multiplier / divisor) & G_MAXUINT32);
3754     }
3755 
3756     if (applied != c->opacity_applied)
3757     {
3758         c->opacity_applied = applied;
3759         compositorWindowSetOpacity (display_info, c->frame, applied);
3760     }
3761 }
3762 
3763 void
clientDecOpacity(Client * c)3764 clientDecOpacity (Client *c)
3765 {
3766     ScreenInfo *screen_info;
3767     DisplayInfo *display_info;
3768 
3769     screen_info = c->screen_info;
3770     display_info = screen_info->display_info;
3771 
3772     if (!compositorIsUsable (display_info))
3773     {
3774         return;
3775     }
3776 
3777     if ((c->opacity > OPACITY_SET_MIN) && !(FLAG_TEST (c->xfwm_flags, XFWM_FLAG_OPACITY_LOCKED)))
3778     {
3779          clientSetOpacity (c, c->opacity - OPACITY_SET_STEP, 0, 0);
3780     }
3781 }
3782 
3783 void
clientIncOpacity(Client * c)3784 clientIncOpacity (Client *c)
3785 {
3786     ScreenInfo *screen_info;
3787     DisplayInfo *display_info;
3788 
3789     screen_info = c->screen_info;
3790     display_info = screen_info->display_info;
3791 
3792     if (!compositorIsUsable (display_info))
3793     {
3794         return;
3795     }
3796 
3797     if ((c->opacity < NET_WM_OPAQUE) && !(FLAG_TEST (c->xfwm_flags, XFWM_FLAG_OPACITY_LOCKED)))
3798     {
3799          guint opacity = c->opacity + OPACITY_SET_STEP;
3800 
3801          if (opacity < OPACITY_SET_MIN)
3802          {
3803              opacity = NET_WM_OPAQUE;
3804          }
3805          clientSetOpacity (c, opacity, 0, 0);
3806     }
3807 }
3808 
3809 /* Xrandr stuff: on screen size change, make sure all clients are still visible */
3810 void
clientScreenResize(ScreenInfo * screen_info,gboolean fully_visible)3811 clientScreenResize(ScreenInfo *screen_info, gboolean fully_visible)
3812 {
3813     Client *c = NULL;
3814     GList *list, *list_of_windows;
3815     XWindowChanges wc;
3816     unsigned short configure_flags;
3817 
3818     list_of_windows = clientGetStackList (screen_info);
3819 
3820     if (!list_of_windows)
3821     {
3822         return;
3823     }
3824 
3825     /* Revalidate client struts */
3826     for (list = list_of_windows; list; list = g_list_next (list))
3827     {
3828         c = (Client *) list->data;
3829         if (FLAG_TEST (c->flags, CLIENT_FLAG_HAS_STRUT))
3830         {
3831             clientValidateNetStrut (c);
3832         }
3833     }
3834 
3835     for (list = list_of_windows; list; list = g_list_next (list))
3836     {
3837         c = (Client *) list->data;
3838         if (!CONSTRAINED_WINDOW (c))
3839         {
3840             continue;
3841         }
3842 
3843         if (FLAG_TEST (c->flags, CLIENT_FLAG_FULLSCREEN))
3844         {
3845             clientUpdateFullscreenSize (c);
3846         }
3847         else if (FLAG_TEST (c->flags, CLIENT_FLAG_MAXIMIZED))
3848         {
3849             clientUpdateMaximizeSize (c);
3850         }
3851         else if (c->tile_mode != TILE_NONE)
3852         {
3853             clientUpdateTileSize (c);
3854         }
3855         else
3856         {
3857             configure_flags = CFG_CONSTRAINED | CFG_REQUEST;
3858             if (fully_visible)
3859             {
3860                 configure_flags |= CFG_KEEP_VISIBLE;
3861             }
3862             if (FLAG_TEST (c->xfwm_flags, XFWM_FLAG_SAVED_POS))
3863             {
3864                 wc.x = c->pre_relayout_x;
3865                 wc.y = c->pre_relayout_y;
3866             }
3867             else
3868             {
3869                 FLAG_SET (c->xfwm_flags, XFWM_FLAG_SAVED_POS);
3870 
3871                 c->pre_relayout_x = c->x;
3872                 c->pre_relayout_y = c->y;
3873 
3874                 wc.x = c->x;
3875                 wc.y = c->y;
3876             }
3877 
3878             clientConfigure (c, &wc, CWX | CWY, configure_flags);
3879         }
3880     }
3881 
3882     g_list_free (list_of_windows);
3883 }
3884 
3885 void
clientUpdateCursor(Client * c)3886 clientUpdateCursor (Client *c)
3887 {
3888     ScreenInfo *screen_info;
3889     DisplayInfo *display_info;
3890     guint i;
3891 
3892     g_return_if_fail (c != NULL);
3893 
3894     screen_info = c->screen_info;
3895     display_info = screen_info->display_info;
3896 
3897     for (i = 0; i < SIDE_COUNT; i++)
3898     {
3899         xfwmWindowSetCursor (&c->sides[i],
3900             myDisplayGetCursorResize(display_info, CORNER_COUNT + i));
3901     }
3902 
3903     for (i = 0; i < CORNER_COUNT; i++)
3904     {
3905         xfwmWindowSetCursor (&c->corners[i],
3906             myDisplayGetCursorResize(display_info, i));
3907     }
3908 }
3909 
3910 void
clientUpdateAllCursor(ScreenInfo * screen_info)3911 clientUpdateAllCursor (ScreenInfo *screen_info)
3912 {
3913     Client *c;
3914     guint i;
3915 
3916     g_return_if_fail (screen_info != NULL);
3917 
3918     for (c = screen_info->clients, i = 0; i < screen_info->client_count; c = c->next, ++i)
3919     {
3920         clientUpdateCursor (c);
3921     }
3922 }
3923 
3924 static eventFilterStatus
clientButtonPressEventFilter(XfwmEvent * event,gpointer data)3925 clientButtonPressEventFilter (XfwmEvent *event, gpointer data)
3926 {
3927     ScreenInfo *screen_info;
3928     DisplayInfo *display_info;
3929     Client *c;
3930     ButtonPressData *passdata;
3931     eventFilterStatus status;
3932     int b;
3933     gboolean pressed;
3934 
3935     passdata = (ButtonPressData *) data;
3936     c = passdata->c;
3937     b = passdata->b;
3938 
3939     screen_info = c->screen_info;
3940     display_info = screen_info->display_info;
3941 
3942     /* Update the display time */
3943     myDisplayUpdateCurrentTime (display_info, event);
3944 
3945     status = EVENT_FILTER_STOP;
3946     pressed = TRUE;
3947 
3948     switch (event->meta.type)
3949     {
3950         case XFWM_EVENT_BUTTON:
3951             if (!event->button.pressed)
3952             {
3953                 pressed = FALSE;
3954             }
3955             break;
3956         case XFWM_EVENT_CROSSING:
3957             if ((event->crossing.mode == NotifyGrab) ||
3958                 (event->crossing.mode == NotifyUngrab) ||
3959                 (event->meta.window != MYWINDOW_XWINDOW (c->buttons[b])))
3960             {
3961                 break;
3962             }
3963             if (event->crossing.enter)
3964             {
3965                 c->button_status[b] = BUTTON_STATE_PRESSED;
3966                 frameQueueDraw (c, FALSE);
3967             }
3968             else
3969             {
3970                 c->button_status[b] = BUTTON_STATE_NORMAL;
3971                 frameQueueDraw (c, FALSE);
3972             }
3973             break;
3974         case XFWM_EVENT_XEVENT:
3975             if ((event->meta.xevent->type == UnmapNotify) && (event->meta.window == c->window))
3976             {
3977                 pressed = FALSE;
3978                 c->button_status[b] = BUTTON_STATE_NORMAL;
3979             }
3980             else
3981             {
3982                 status = EVENT_FILTER_CONTINUE;
3983             }
3984             break;
3985         default:
3986             break;
3987     }
3988 
3989     if (!pressed)
3990     {
3991         TRACE ("event loop now finished");
3992         gtk_main_quit ();
3993     }
3994 
3995     return status;
3996 }
3997 
3998 void
clientButtonPress(Client * c,Window w,XfwmEventButton * event)3999 clientButtonPress (Client *c, Window w, XfwmEventButton *event)
4000 {
4001     ScreenInfo *screen_info;
4002     DisplayInfo *display_info;
4003     ButtonPressData passdata;
4004     gint b;
4005     gboolean g1;
4006 
4007     g_return_if_fail (c != NULL);
4008     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
4009 
4010     for (b = 0; b < BUTTON_COUNT; b++)
4011     {
4012         if (MYWINDOW_XWINDOW (c->buttons[b]) == w)
4013         {
4014             break;
4015         }
4016     }
4017 
4018     screen_info = c->screen_info;
4019     display_info = screen_info->display_info;
4020 
4021     myDisplayErrorTrapPush (display_info);
4022     g1 = xfwm_device_grab (display_info->devices, &display_info->devices->pointer,
4023                            display_info->dpy, w, FALSE,
4024                            ButtonReleaseMask | EnterWindowMask | LeaveWindowMask,
4025                            GrabModeAsync, screen_info->xroot, None,
4026                            myDisplayGetCurrentTime (display_info));
4027     myDisplayErrorTrapPopIgnored (display_info);
4028 
4029     if (!g1)
4030     {
4031         TRACE ("grab failed in clientButtonPress");
4032         myDisplayBeep (display_info);
4033         return;
4034     }
4035 
4036     passdata.c = c;
4037     passdata.b = b;
4038 
4039     c->button_status[b] = BUTTON_STATE_PRESSED;
4040     frameQueueDraw (c, FALSE);
4041 
4042     TRACE ("entering button press loop");
4043     eventFilterPush (display_info->xfilter, clientButtonPressEventFilter, &passdata);
4044     gtk_main ();
4045     eventFilterPop (display_info->xfilter);
4046     TRACE ("leaving button press loop");
4047 
4048     myDisplayErrorTrapPush (display_info);
4049     xfwm_device_ungrab (display_info->devices, &display_info->devices->pointer,
4050                         display_info->dpy, myDisplayGetCurrentTime (display_info));
4051     myDisplayErrorTrapPopIgnored (display_info);
4052 
4053     if (c->button_status[b] == BUTTON_STATE_PRESSED)
4054     {
4055         /*
4056          * Button was pressed at the time, means the pointer was still within
4057          * the button, so return to prelight if available, normal otherwise.
4058          */
4059         if (!xfwmPixmapNone(clientGetButtonPixmap(c, b, PRELIGHT)))
4060         {
4061             c->button_status[b] = BUTTON_STATE_PRELIGHT;
4062         }
4063         else
4064         {
4065             c->button_status[b] = BUTTON_STATE_NORMAL;
4066         }
4067 
4068         switch (b)
4069         {
4070             case HIDE_BUTTON:
4071                 if (CLIENT_CAN_HIDE_WINDOW (c))
4072                 {
4073                     clientWithdraw (c, c->win_workspace, TRUE);
4074                 }
4075                 break;
4076             case CLOSE_BUTTON:
4077                 clientClose (c);
4078                 break;
4079             case MAXIMIZE_BUTTON:
4080                 if (CLIENT_CAN_MAXIMIZE_WINDOW (c))
4081                 {
4082                     if (event->button == Button1)
4083                     {
4084                         clientToggleMaximized (c, CLIENT_FLAG_MAXIMIZED, TRUE);
4085                     }
4086                     else if (event->button == Button2)
4087                     {
4088                         clientToggleMaximized (c, CLIENT_FLAG_MAXIMIZED_VERT, TRUE);
4089                     }
4090                     else if (event->button == Button3)
4091                     {
4092                         clientToggleMaximized (c, CLIENT_FLAG_MAXIMIZED_HORIZ, TRUE);
4093                     }
4094                 }
4095                 break;
4096             case SHADE_BUTTON:
4097                 clientToggleShaded (c);
4098                 break;
4099             case STICK_BUTTON:
4100                 clientToggleSticky (c, TRUE);
4101                 break;
4102             default:
4103                 break;
4104         }
4105         frameQueueDraw (c, FALSE);
4106     }
4107 }
4108 
4109 xfwmPixmap *
clientGetButtonPixmap(Client * c,int button,int state)4110 clientGetButtonPixmap (Client *c, int button, int state)
4111 {
4112     ScreenInfo *screen_info;
4113 
4114     TRACE ("button=%i, state=%i", button, state);
4115     screen_info = c->screen_info;
4116     switch (button)
4117     {
4118         case MENU_BUTTON:
4119             if ((screen_info->params->show_app_icon)
4120                 && (!xfwmPixmapNone(&c->appmenu[state])))
4121             {
4122                 return &c->appmenu[state];
4123             }
4124             break;
4125         case SHADE_BUTTON:
4126             if (FLAG_TEST (c->flags, CLIENT_FLAG_SHADED)
4127                 && (!xfwmPixmapNone(&screen_info->buttons[SHADE_BUTTON][state + STATE_TOGGLED])))
4128             {
4129                 return &screen_info->buttons[SHADE_BUTTON][state + STATE_TOGGLED];
4130             }
4131             return &screen_info->buttons[SHADE_BUTTON][state];
4132             break;
4133         case STICK_BUTTON:
4134             if (FLAG_TEST (c->flags, CLIENT_FLAG_STICKY)
4135                 && (!xfwmPixmapNone(&screen_info->buttons[STICK_BUTTON][state + STATE_TOGGLED])))
4136             {
4137                 return &screen_info->buttons[STICK_BUTTON][state + STATE_TOGGLED];
4138             }
4139             return &screen_info->buttons[STICK_BUTTON][state];
4140             break;
4141         case MAXIMIZE_BUTTON:
4142             if (FLAG_TEST (c->flags, CLIENT_FLAG_MAXIMIZED)
4143                 && (!xfwmPixmapNone(&screen_info->buttons[MAXIMIZE_BUTTON][state + STATE_TOGGLED])))
4144             {
4145                 return &screen_info->buttons[MAXIMIZE_BUTTON][state + STATE_TOGGLED];
4146             }
4147             return &screen_info->buttons[MAXIMIZE_BUTTON][state];
4148             break;
4149         default:
4150             break;
4151     }
4152     return &screen_info->buttons[button][state];
4153 }
4154 
4155 int
clientGetButtonState(Client * c,int button,int state)4156 clientGetButtonState (Client *c, int button, int state)
4157 {
4158     if (state == INACTIVE)
4159     {
4160         return (state);
4161     }
4162 
4163     if ((c->button_status[button] == BUTTON_STATE_PRESSED) &&
4164         clientGetButtonPixmap (c, button, PRESSED))
4165     {
4166         return (PRESSED);
4167     }
4168 
4169     if ((c->button_status[button] == BUTTON_STATE_PRELIGHT) &&
4170         clientGetButtonPixmap (c, button, PRELIGHT))
4171     {
4172         return (PRELIGHT);
4173     }
4174 
4175     return (ACTIVE);
4176 }
4177 
4178 
4179 Client *
clientGetLeader(Client * c)4180 clientGetLeader (Client *c)
4181 {
4182     g_return_val_if_fail (c != NULL, NULL);
4183     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
4184 
4185     if (c->group_leader != None)
4186     {
4187         return myScreenGetClientFromWindow (c->screen_info, c->group_leader, SEARCH_WINDOW);
4188     }
4189     else if (c->client_leader != None)
4190     {
4191         return myScreenGetClientFromWindow (c->screen_info, c->client_leader, SEARCH_WINDOW);
4192     }
4193     return NULL;
4194 }
4195 
4196 gboolean
clientGetGtkFrameExtents(Client * c)4197 clientGetGtkFrameExtents (Client * c)
4198 {
4199     ScreenInfo *screen_info;
4200     DisplayInfo *display_info;
4201     gboolean value_changed = FALSE;
4202     gulong *extents;
4203     int nitems;
4204     int i;
4205 
4206     g_return_val_if_fail (c != NULL, FALSE);
4207     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
4208 
4209     screen_info = c->screen_info;
4210     display_info = screen_info->display_info;
4211     extents = NULL;
4212     FLAG_UNSET (c->flags, CLIENT_FLAG_HAS_FRAME_EXTENTS);
4213 
4214     if (getCardinalList (display_info, c->window, GTK_FRAME_EXTENTS, &extents, &nitems))
4215     {
4216         if (nitems == SIDE_COUNT)
4217         {
4218             FLAG_SET (c->flags, CLIENT_FLAG_HAS_FRAME_EXTENTS);
4219             for (i = 0; i < SIDE_COUNT; i++)
4220             {
4221                 if (c->frame_extents[i] != (int) extents[i])
4222                 {
4223                     value_changed = TRUE;
4224                     c->frame_extents[i] = (int) extents[i];
4225                 }
4226             }
4227         }
4228     }
4229 
4230     if (extents)
4231     {
4232         XFree (extents);
4233     }
4234 
4235     return value_changed;
4236 }
4237 
4238 gboolean
clientGetGtkHideTitlebar(Client * c)4239 clientGetGtkHideTitlebar (Client * c)
4240 {
4241     ScreenInfo *screen_info;
4242     DisplayInfo *display_info;
4243     unsigned long old_value;
4244     long val;
4245 
4246     g_return_val_if_fail (c != NULL, FALSE);
4247     TRACE ("client \"%s\" (0x%lx)", c->name, c->window);
4248 
4249     screen_info = c->screen_info;
4250     display_info = screen_info->display_info;
4251     old_value = FLAG_TEST (c->flags, CLIENT_FLAG_HIDE_TITLEBAR);
4252     FLAG_UNSET (c->flags, CLIENT_FLAG_HIDE_TITLEBAR);
4253 
4254     if (getHint (display_info, c->window, GTK_HIDE_TITLEBAR_WHEN_MAXIMIZED, &val) &&( val != 0))
4255     {
4256         FLAG_SET (c->flags, CLIENT_FLAG_HIDE_TITLEBAR);
4257     }
4258     return old_value != FLAG_TEST (c->flags, CLIENT_FLAG_HIDE_TITLEBAR);
4259 }
4260 
4261 #ifdef HAVE_LIBSTARTUP_NOTIFICATION
4262 char *
clientGetStartupId(Client * c)4263 clientGetStartupId (Client *c)
4264 {
4265     ScreenInfo *screen_info;
4266     DisplayInfo *display_info;
4267     gboolean got_startup_id;
4268 
4269     g_return_val_if_fail (c != NULL, NULL);
4270     g_return_val_if_fail (c->window != None, NULL);
4271 
4272     screen_info = c->screen_info;
4273     display_info = screen_info->display_info;
4274     got_startup_id = FALSE;
4275 
4276     if (c->startup_id)
4277     {
4278         return (c->startup_id);
4279     }
4280 
4281     got_startup_id = getWindowStartupId (display_info, c->window, &c->startup_id);
4282 
4283     if (!got_startup_id && (c->client_leader))
4284     {
4285         got_startup_id = getWindowStartupId (display_info, c->client_leader, &c->startup_id);
4286     }
4287 
4288     if (!got_startup_id && (c->group_leader))
4289     {
4290         got_startup_id = getWindowStartupId (display_info, c->group_leader, &c->startup_id);
4291     }
4292 
4293     return (c->startup_id);
4294 }
4295 #endif /* HAVE_LIBSTARTUP_NOTIFICATION */
4296