]> icculus.org git repositories - dana/openbox.git/blob - openbox/client.c
Ramove unneeded frame_get_window
[dana/openbox.git] / openbox / client.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2
3    client.c for the Openbox window manager
4    Copyright (c) 2006        Mikael Magnusson
5    Copyright (c) 2003-2007   Dana Jansens
6
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    See the COPYING file for a copy of the GNU General Public License.
18 */
19
20 #include "client.h"
21 #include "debug.h"
22 #include "startupnotify.h"
23 #include "dock.h"
24 #include "screen.h"
25 #include "moveresize.h"
26 #include "ping.h"
27 #include "place.h"
28 #include "engine_interface.h"
29 #include "session.h"
30 #include "event.h"
31 #include "grab.h"
32 #include "prompt.h"
33 #include "focus.h"
34 #include "stacking.h"
35 #include "openbox.h"
36 #include "group.h"
37 #include "config.h"
38 #include "menuframe.h"
39 #include "keyboard.h"
40 #include "mouse.h"
41 #include "render/render.h"
42 #include "gettext.h"
43 #include "obt/display.h"
44 #include "obt/prop.h"
45 #include "window.h"
46
47 #ifdef HAVE_UNISTD_H
48 #  include <unistd.h>
49 #endif
50
51 #ifdef HAVE_SIGNAL_H
52 #  include <signal.h> /* for kill() */
53 #endif
54
55 #include <glib.h>
56 #include <X11/Xutil.h>
57
58 /*! The event mask to grab on client windows */
59 #define CLIENT_EVENTMASK (PropertyChangeMask | StructureNotifyMask | \
60                           ColormapChangeMask)
61
62 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
63                                 ButtonMotionMask)
64
65 typedef struct
66 {
67     ObClientCallback func;
68     gpointer data;
69 } ClientCallback;
70
71 GList            *client_list          = NULL;
72
73 static GSList *client_destroy_notifies = NULL;
74
75 static void client_get_all(ObClient *self, gboolean real);
76 static void client_get_startup_id(ObClient *self);
77 static void client_get_session_ids(ObClient *self);
78 static void client_get_area(ObClient *self);
79 static void client_get_desktop(ObClient *self);
80 static void client_get_state(ObClient *self);
81 static void client_get_shaped(ObClient *self);
82 static void client_get_mwm_hints(ObClient *self);
83 static void client_get_colormap(ObClient *self);
84 static void client_set_desktop_recursive(ObClient *self,
85                                          guint target,
86                                          gboolean donthide,
87                                          gboolean dontraise);
88 static void client_change_allowed_actions(ObClient *self);
89 static void client_change_state(ObClient *self);
90 static void client_change_wm_state(ObClient *self);
91 static void client_apply_startup_state(ObClient *self,
92                                        gint x, gint y, gint w, gint h);
93 static void client_restore_session_state(ObClient *self);
94 static gboolean client_restore_session_stacking(ObClient *self);
95 static ObAppSettings *client_get_settings_state(ObClient *self);
96 static void client_update_transient_tree(ObClient *self,
97                                          ObGroup *oldgroup, ObGroup *newgroup,
98                                          gboolean oldgtran, gboolean newgtran,
99                                          ObClient* oldparent,
100                                          ObClient *newparent);
101 static void client_present(ObClient *self, gboolean here, gboolean raise,
102                            gboolean unshade);
103 static GSList *client_search_all_top_parents_internal(ObClient *self,
104                                                       gboolean bylayer,
105                                                       ObStackingLayer layer);
106 static void client_call_notifies(ObClient *self, GSList *list);
107 static void client_ping_event(ObClient *self, gboolean dead);
108 static void client_prompt_kill(ObClient *self);
109
110 Window createWindow(Window parent, Visual *visual, gulong mask,
111         XSetWindowAttributes *attrib)
112 {
113     return XCreateWindow(obt_display, parent, 0, 0, 1, 1, 0, (visual ? 32
114             : RrDepth(ob_rr_inst)), InputOutput, (visual ? visual
115             : RrVisual(ob_rr_inst)), mask, attrib);
116
117 }
118
119 Visual *check_32bit_client(ObClient *c)
120 {
121     XWindowAttributes wattrib;
122     Status ret;
123
124     /* we're already running at 32 bit depth, yay. we don't need to use their
125      visual */
126     if (RrDepth(ob_rr_inst) == 32)
127         return NULL;
128
129     ret = XGetWindowAttributes(obt_display, c->w_client, &wattrib);
130     g_assert(ret != BadDrawable);
131     g_assert(ret != BadWindow);
132
133     if (wattrib.depth == 32)
134         return wattrib.visual;
135     return NULL;
136 }
137
138 void client_startup(gboolean reconfig)
139 {
140     if (reconfig) return;
141
142     client_set_list();
143 }
144
145 void client_shutdown(gboolean reconfig)
146 {
147     if (reconfig) return;
148 }
149
150 static void client_call_notifies(ObClient *self, GSList *list)
151 {
152     GSList *it;
153
154     for (it = list; it; it = g_slist_next(it)) {
155         ClientCallback *d = it->data;
156         d->func(self, d->data);
157     }
158 }
159
160 void client_add_destroy_notify(ObClientCallback func, gpointer data)
161 {
162     ClientCallback *d = g_new(ClientCallback, 1);
163     d->func = func;
164     d->data = data;
165     client_destroy_notifies = g_slist_prepend(client_destroy_notifies, d);
166 }
167
168 void client_remove_destroy_notify(ObClientCallback func)
169 {
170     GSList *it;
171
172     for (it = client_destroy_notifies; it; it = g_slist_next(it)) {
173         ClientCallback *d = it->data;
174         if (d->func == func) {
175             g_free(d);
176             client_destroy_notifies =
177                 g_slist_delete_link(client_destroy_notifies, it);
178             break;
179         }
180     }
181 }
182
183 void client_set_list(void)
184 {
185     Window *windows, *win_it;
186     GList *it;
187     guint size = g_list_length(client_list);
188
189     /* create an array of the window ids */
190     if (size > 0) {
191         windows = g_new(Window, size);
192         win_it = windows;
193         for (it = client_list; it; it = g_list_next(it), ++win_it)
194             *win_it = ((ObClient*)it->data)->w_client;
195     } else
196         windows = NULL;
197
198     OBT_PROP_SETA32(obt_root(ob_screen), NET_CLIENT_LIST, WINDOW,
199                     (gulong*)windows, size);
200
201     if (windows)
202         g_free(windows);
203
204     stacking_set_list();
205 }
206
207 void client_manage(Window window, ObPrompt *prompt)
208 {
209     ObClient *self;
210     XSetWindowAttributes attrib_set;
211     gboolean activate = FALSE;
212     ObAppSettings *settings;
213     gboolean transient = FALSE;
214     Rect place, *monitor;
215     Time launch_time, map_time;
216
217     ob_debug("Managing window: 0x%lx", window);
218
219     map_time = event_get_server_time();
220
221     /* choose the events we want to receive on the CLIENT window
222        (ObPrompt windows can request events too) */
223     attrib_set.event_mask = CLIENT_EVENTMASK |
224         (prompt ? prompt->event_mask : 0);
225     attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK;
226     XChangeWindowAttributes(obt_display, window,
227                             CWEventMask|CWDontPropagate, &attrib_set);
228
229     /* create the ObClient struct, and populate it from the hints on the
230        window */
231     self = g_new0(ObClient, 1);
232     self->obwin.type = OB_WINDOW_CLASS_CLIENT;
233     self->w_client = window;
234     self->prompt = prompt;
235
236     /* non-zero defaults */
237     self->wmstate = WithdrawnState; /* make sure it gets updated first time */
238     self->gravity = NorthWestGravity;
239     self->desktop = screen_num_desktops; /* always an invalid value */
240
241     /* get all the stuff off the window */
242     client_get_all(self, TRUE);
243
244     ob_debug("Window type: %d", self->type);
245     ob_debug("Window group: 0x%x", self->group?self->group->leader:0);
246
247     /* now we have all of the window's information so we can set this up.
248        do this before creating the frame, so it can tell that we are still
249        mapping and doesn't go applying things right away */
250     client_setup_decor_and_functions(self, FALSE);
251
252     /* specify that if we exit, the window should not be destroyed and
253        should be reparented back to root automatically, unless we are managing
254        an internal ObPrompt window  */
255     if (!self->prompt)
256         XChangeSaveSet(obt_display, window, SetModeInsert);
257
258     /* create the decoration frame for the client window */
259     Visual *visual = check_32bit_client(self);
260     gulong mask = 0;
261     XSetWindowAttributes attrib;
262     if (visual) {
263         /* client has a 32-bit visual */
264         mask |= CWColormap | CWBackPixel | CWBorderPixel;
265         /* create a colormap with the visual */
266         attrib.colormap = XCreateColormap(
267                 obt_display, RootWindow(obt_display, ob_screen), visual,
268                 AllocNone);
269         attrib.background_pixel = BlackPixel(obt_display, ob_screen);
270         attrib.border_pixel = BlackPixel(obt_display, ob_screen);
271     }
272     self->w_frame = createWindow(RootWindow(obt_display, ob_screen), visual,
273             mask, &attrib);
274     self->frame = frame_engine->frame_new(self, self->w_client, self->w_frame);
275     frame_engine->frame_grab(self->frame, window_map);
276
277     /* we've grabbed everything and set everything that we need to at mapping
278        time now */
279     grab_server(FALSE);
280
281     /* per-app settings override stuff from client_get_all, and return the
282        settings for other uses too. the returned settings is a shallow copy,
283        that needs to be freed with g_free(). */
284     settings = client_get_settings_state(self);
285     /* the session should get the last say though */
286     client_restore_session_state(self);
287
288     /* tell startup notification that this app started */
289     launch_time = sn_app_started(self->startup_id, self->class, self->name);
290
291     /* do this after we have a frame.. it uses the frame to help determine the
292        WM_STATE to apply. */
293     client_change_state(self);
294
295     /* add ourselves to the focus order */
296     focus_order_add_new(self);
297
298     /* do this to add ourselves to the stacking list in a non-intrusive way */
299     client_calc_layer(self);
300
301     /* focus the new window? */
302     if (ob_state() != OB_STATE_STARTING &&
303         (!self->session || self->session->focused) &&
304         /* this means focus=true for window is same as config_focus_new=true */
305         ((config_focus_new || (settings && settings->focus == 1)) ||
306          client_search_focus_tree_full(self)) &&
307         /* this checks for focus=false for the window */
308         (!settings || settings->focus != 0) &&
309         focus_valid_target(self, FALSE, FALSE, TRUE, FALSE, FALSE))
310     {
311         activate = TRUE;
312     }
313
314     /* remove the client's border */
315     XSetWindowBorderWidth(obt_display, self->w_client, 0);
316
317     /* adjust the frame to the client's size before showing or placing
318        the window */
319     frame_engine->frame_set_hover_flag (self->frame, OB_BUTTON_NONE);
320     frame_engine->frame_set_press_flag (self->frame, OB_BUTTON_NONE);
321     frame_engine->frame_set_is_focus (self->frame, FALSE);
322     frame_engine->frame_set_decorations (self->frame, self->decorations);
323     frame_engine->frame_update_title (self->frame, self->title);
324     frame_engine->frame_update_layout (self->frame, self->area, FALSE, TRUE);
325     frame_engine->frame_update_skin (self->frame);
326
327     /* where the frame was placed is where the window was originally */
328     place = self->area;
329     monitor = screen_physical_area_monitor(screen_find_monitor(&place));
330
331     /* figure out placement for the window if the window is new */
332     if (ob_state() == OB_STATE_RUNNING) {
333         ob_debug("Positioned: %s @ %d %d",
334                  (!self->positioned ? "no" :
335                   (self->positioned == PPosition ? "program specified" :
336                    (self->positioned == USPosition ? "user specified" :
337                     (self->positioned == (PPosition | USPosition) ?
338                      "program + user specified" :
339                      "BADNESS !?")))), place.x, place.y);
340
341         ob_debug("Sized: %s @ %d %d",
342                  (!self->sized ? "no" :
343                   (self->sized == PSize ? "program specified" :
344                    (self->sized == USSize ? "user specified" :
345                     (self->sized == (PSize | USSize) ?
346                      "program + user specified" :
347                      "BADNESS !?")))), place.width, place.height);
348
349         /* splash screens are also returned as TRUE for transient,
350            and so will be forced on screen below */
351         transient = place_client(self, &place.x, &place.y, settings);
352
353         /* make sure the window is visible. */
354         client_find_onscreen(self, &place.x, &place.y,
355                              place.width, place.height,
356                              /* non-normal clients has less rules, and
357                                 windows that are being restored from a
358                                 session do also. we can assume you want
359                                 it back where you saved it. Clients saying
360                                 they placed themselves are subjected to
361                                 harder rules, ones that are placed by
362                                 place.c or by the user are allowed partially
363                                 off-screen and on xinerama divides (ie,
364                                 it is up to the placement routines to avoid
365                                 the xinerama divides)
366
367                                 splash screens get "transient" set to TRUE by
368                                 the place_client call
369                              */
370                              ob_state() == OB_STATE_RUNNING &&
371                              (transient ||
372                               (!((self->positioned & USPosition) ||
373                                  (settings && settings->pos_given)) &&
374                                client_normal(self) &&
375                                !self->session &&
376                                /* don't move oldschool fullscreen windows to
377                                   fit inside the struts (fixes Acroread, which
378                                   makes its fullscreen window fit the screen
379                                   but it is not USSize'd or USPosition'd) */
380                                !(self->decorations == 0 &&
381                                  RECT_EQUAL(place, *monitor)))));
382     }
383
384     /* if the window isn't user-sized, then make it fit inside
385        the visible screen area on its monitor. Use basically the same rules
386        for forcing the window on screen in the client_find_onscreen call.
387
388        do this after place_client, it chooses the monitor!
389
390        splash screens get "transient" set to TRUE by
391        the place_client call
392     */
393     if (ob_state() == OB_STATE_RUNNING &&
394         (transient ||
395          (!(self->sized & USSize || self->positioned & USPosition) &&
396           client_normal(self) &&
397           !self->session &&
398           /* don't shrink oldschool fullscreen windows to fit inside the
399              struts (fixes Acroread, which makes its fullscreen window
400              fit the screen but it is not USSize'd or USPosition'd) */
401           !(self->decorations == 0 && RECT_EQUAL(place, *monitor)))))
402     {
403         Rect *a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &place);
404
405         Strut size;
406         frame_engine->frame_get_size(self->frame, &size);
407         /* get the size of the frame */
408         place.width += size.left + size.right;
409         place.height += size.top + size.bottom;
410
411         /* fit the window inside the area */
412         place.width = MIN(place.width, a->width);
413         place.height = MIN(place.height, a->height);
414
415         ob_debug("setting window size to %dx%d", place.width, place.height);
416
417         /* get the size of the client back */
418         place.width -= size.left + size.right;
419         place.height -= size.top + size.bottom;
420
421         g_free(a);
422     }
423
424     ob_debug("placing window 0x%x at %d, %d with size %d x %d. "
425              "some restrictions may apply",
426              self->w_client, place.x, place.y, place.width, place.height);
427     if (self->session)
428         ob_debug("  but session requested %d, %d  %d x %d instead, "
429                  "overriding",
430                  self->session->x, self->session->y,
431                  self->session->w, self->session->h);
432
433     /* do this after the window is placed, so the premax/prefullscreen numbers
434        won't be all wacko!!
435
436        this also places the window
437     */
438     client_apply_startup_state(self, place.x, place.y,
439                                place.width, place.height);
440
441     g_free(monitor);
442     monitor = NULL;
443
444     ob_debug_type(OB_DEBUG_FOCUS, "Going to try activate new window? %s",
445                   activate ? "yes" : "no");
446     if (activate) {
447         gboolean raise = FALSE;
448
449         /* This is focus stealing prevention */
450         ob_debug_type(OB_DEBUG_FOCUS,
451                       "Want to focus new window 0x%x at time %u "
452                       "launched at %u (last user interaction time %u)",
453                       self->w_client, map_time, launch_time,
454                       event_last_user_time);
455
456         if (menu_frame_visible || frame_engine->moveresize_in_progress) {
457             activate = FALSE;
458             raise = TRUE;
459             ob_debug_type(OB_DEBUG_FOCUS,
460                           "Not focusing the window because the user is inside "
461                           "an Openbox menu or is move/resizing a window and "
462                           "we don't want to interrupt them");
463         }
464
465         /* if it's on another desktop */
466         else if (!(self->desktop == screen_desktop ||
467                    self->desktop == DESKTOP_ALL) &&
468                  /* the timestamp is from before you changed desktops */
469                  launch_time && screen_desktop_user_time &&
470                  !event_time_after(launch_time, screen_desktop_user_time))
471         {
472             activate = FALSE;
473             raise = TRUE;
474             ob_debug_type(OB_DEBUG_FOCUS,
475                           "Not focusing the window because its on another "
476                           "desktop");
477         }
478         /* If something is focused, and it's not our relative... */
479         else if (focus_client && client_search_focus_tree_full(self) == NULL &&
480                  client_search_focus_group_full(self) == NULL)
481         {
482             /* If the user is working in another window right now, then don't
483                steal focus */
484             if (event_last_user_time && launch_time &&
485                 event_time_after(event_last_user_time, launch_time) &&
486                 event_last_user_time != launch_time &&
487                 event_time_after(event_last_user_time,
488                                  map_time - OB_EVENT_USER_TIME_DELAY))
489             {
490                 activate = FALSE;
491                 ob_debug_type(OB_DEBUG_FOCUS,
492                               "Not focusing the window because the user is "
493                               "working in another window");
494             }
495             /* If its a transient (and its parents aren't focused) */
496             else if (client_has_parent(self)) {
497                 activate = FALSE;
498                 ob_debug_type(OB_DEBUG_FOCUS,
499                               "Not focusing the window because it is a "
500                               "transient, and its relatives aren't focused");
501             }
502             /* Don't steal focus from globally active clients.
503                I stole this idea from KWin. It seems nice.
504              */
505             else if (!(focus_client->can_focus ||
506                        focus_client->focus_notify))
507             {
508                 activate = FALSE;
509                 ob_debug_type(OB_DEBUG_FOCUS,
510                               "Not focusing the window because a globally "
511                               "active client has focus");
512             }
513             /* Don't move focus if it's not going to go to this window
514                anyway */
515             else if (client_focus_target(self) != self) {
516                 activate = FALSE;
517                 raise = TRUE;
518                 ob_debug_type(OB_DEBUG_FOCUS,
519                               "Not focusing the window because another window "
520                               "would get the focus anyway");
521             }
522             else if (!(self->desktop == screen_desktop ||
523                        self->desktop == DESKTOP_ALL))
524             {
525                 activate = FALSE;
526                 raise = TRUE;
527                 ob_debug_type(OB_DEBUG_FOCUS,
528                               "Not focusing the window because it is on "
529                               "another desktop and no relatives are focused ");
530             }
531         }
532
533         if (!activate) {
534             ob_debug_type(OB_DEBUG_FOCUS,
535                           "Focus stealing prevention activated for %s at "
536                           "time %u (last user interactioon time %u)",
537                           self->title, map_time, event_last_user_time);
538             /* if the client isn't focused, then hilite it so the user
539                knows it is there */
540             client_hilite(self, TRUE);
541             /* we may want to raise it even tho we're not activating it */
542             if (raise && !client_restore_session_stacking(self))
543                 stacking_raise(CLIENT_AS_WINDOW(self));
544         }
545     }
546     else {
547         /* This may look rather odd. Well it's because new windows are added
548            to the stacking order non-intrusively. If we're not going to focus
549            the new window or hilite it, then we raise it to the top. This will
550            take affect for things that don't get focused like splash screens.
551            Also if you don't have focus_new enabled, then it's going to get
552            raised to the top. Legacy begets legacy I guess?
553         */
554         if (!client_restore_session_stacking(self))
555             stacking_raise(CLIENT_AS_WINDOW(self));
556     }
557
558     mouse_grab_for_client(self, TRUE);
559
560     /* this has to happen before we try focus the window, but we want it to
561        happen after the client's stacking has been determined or it looks bad
562     */
563     {
564         gulong ignore_start;
565         if (!config_focus_under_mouse)
566             ignore_start = event_start_ignore_all_enters();
567
568         client_show(self);
569
570         if (!config_focus_under_mouse)
571             event_end_ignore_all_enters(ignore_start);
572     }
573
574     if (activate) {
575         gboolean stacked = client_restore_session_stacking(self);
576         client_present(self, FALSE, !stacked, TRUE);
577     }
578
579     /* add to client list/map */
580     client_list = g_list_append(client_list, self);
581     window_add(&self->w_client, CLIENT_AS_WINDOW(self));
582
583     /* this has to happen after we're in the client_list */
584     if (STRUT_EXISTS(self->strut))
585         screen_update_areas();
586
587     /* update the list hints */
588     client_set_list();
589
590     /* watch for when the application stops responding.  only do this for
591        normal windows, i.e. windows which have titlebars and close buttons 
592        and things like that.
593        we don't need to stop pinging on unmanage, because it will be handled
594        automatically by the destroy callback!
595     */
596     if (self->ping && client_normal(self))
597         ping_start(self, client_ping_event);
598
599     /* free the ObAppSettings shallow copy */
600     g_free(settings);
601
602     ob_debug("Managed window 0x%lx plate 0x%x (%s)",
603              window, self->w_frame, self->class);
604 }
605
606
607 ObClient *client_fake_manage(Window window)
608 {
609     ObClient *self;
610     ObAppSettings *settings;
611
612     ob_debug("Pretend-managing window: %lx", window);
613
614     /* do this minimal stuff to figure out the client's decorations */
615
616     self = g_new0(ObClient, 1);
617     self->w_client = window;
618
619     client_get_all(self, FALSE);
620     /* per-app settings override stuff, and return the settings for other
621        uses too. this returns a shallow copy that needs to be freed */
622     settings = client_get_settings_state(self);
623
624     client_setup_decor_and_functions(self, FALSE);
625
626     /* create the decoration frame for the client window and adjust its size */
627     Visual *visual = check_32bit_client(self);
628     gulong mask = 0;
629     XSetWindowAttributes attrib;
630     if (visual) {
631         /* client has a 32-bit visual */
632         mask |= CWColormap | CWBackPixel | CWBorderPixel;
633         /* create a colormap with the visual */
634         attrib.colormap = XCreateColormap(
635                 obt_display, RootWindow(obt_display, ob_screen), visual,
636                 AllocNone);
637         attrib.background_pixel = BlackPixel(obt_display, ob_screen);
638         attrib.border_pixel = BlackPixel(obt_display, ob_screen);
639     }
640     self->w_frame = createWindow(RootWindow(obt_display, ob_screen), visual,
641             mask, &attrib);
642     self->frame = frame_engine->frame_new(self, self->w_client, self->w_frame);
643     frame_engine->frame_set_decorations (self->frame, self->decorations);
644     frame_engine->frame_update_title (self->frame, self->title);
645     frame_engine->frame_update_layout (self->frame, self->area, FALSE, FALSE);
646     frame_engine->frame_update_skin (self->frame);
647
648     Strut size;
649     frame_engine->frame_get_size(self->frame, &size);    
650     ob_debug("gave extents left %d right %d top %d bottom %d",
651              size.left, size.right,
652              size.top, size.bottom);
653
654     /* free the ObAppSettings shallow copy */
655     g_free(settings);
656
657     return self;
658 }
659
660 void client_unmanage_all(void)
661 {
662     while (client_list != NULL)
663         client_unmanage(client_list->data);
664 }
665
666 void client_unmanage(ObClient *self)
667 {
668     guint j;
669     GSList *it;
670     gulong ignore_start;
671
672     ob_debug("Unmanaging window: 0x%x plate 0x%x (%s) (%s)",
673              self->w_client, self->w_frame,
674              self->class, self->title ? self->title : "");
675
676     g_assert(self != NULL);
677
678     /* we dont want events no more. do this before hiding the frame so we
679        don't generate more events */
680     XSelectInput(obt_display, self->w_client, NoEventMask);
681
682     /* ignore enter events from the unmap so it doesnt mess with the focus */
683     if (!config_focus_under_mouse)
684         ignore_start = event_start_ignore_all_enters();
685
686     client_hide_frame(self);
687     /* flush to send the hide to the server quickly */
688     XFlush(obt_display);
689
690     if (!config_focus_under_mouse)
691         event_end_ignore_all_enters(ignore_start);
692
693     mouse_grab_for_client(self, FALSE);
694
695     /* remove the window from our save set, unless we are managing an internal
696        ObPrompt window */
697     if (!self->prompt)
698         XChangeSaveSet(obt_display, self->w_client, SetModeDelete);
699
700     /* update the focus lists */
701     focus_order_remove(self);
702     if (client_focused(self)) {
703         /* don't leave an invalid focus_client */
704         focus_client = NULL;
705     }
706
707     /* if we're prompting to kill the client, close that */
708     prompt_unref(self->kill_prompt);
709     self->kill_prompt = NULL;
710
711     client_list = g_list_remove(client_list, self);
712     stacking_remove(self);
713     window_remove(self->w_client);
714
715     /* once the client is out of the list, update the struts to remove its
716        influence */
717     if (STRUT_EXISTS(self->strut))
718         screen_update_areas();
719
720     client_call_notifies(self, client_destroy_notifies);
721
722     /* tell our parent(s) that we're gone */
723     for (it = self->parents; it; it = g_slist_next(it))
724         ((ObClient*)it->data)->transients =
725             g_slist_remove(((ObClient*)it->data)->transients,self);
726
727     /* tell our transients that we're gone */
728     for (it = self->transients; it; it = g_slist_next(it)) {
729         ((ObClient*)it->data)->parents =
730             g_slist_remove(((ObClient*)it->data)->parents, self);
731         /* we could be keeping our children in a higher layer */
732         client_calc_layer(it->data);
733     }
734
735     /* remove from its group */
736     if (self->group) {
737         group_remove(self->group, self);
738         self->group = NULL;
739     }
740
741     /* restore the window's original geometry so it is not lost */
742     {
743         Rect a;
744
745         a = self->area;
746
747         if (self->fullscreen)
748             a = self->pre_fullscreen_area;
749         else if (self->max_horz || self->max_vert) {
750             if (self->max_horz) {
751                 a.x = self->pre_max_area.x;
752                 a.width = self->pre_max_area.width;
753             }
754             if (self->max_vert) {
755                 a.y = self->pre_max_area.y;
756                 a.height = self->pre_max_area.height;
757             }
758         }
759
760         self->fullscreen = self->max_horz = self->max_vert = FALSE;
761         frame_engine->frame_trigger(self, OB_TRIGGER_UNMAX_VERT);
762         frame_engine->frame_trigger(self, OB_TRIGGER_UNMAX_HORZ);
763         /* let it be moved and resized no matter what */
764         self->functions = OB_CLIENT_FUNC_MOVE | OB_CLIENT_FUNC_RESIZE;
765         self->decorations = 0; /* unmanaged windows have no decor */
766
767         /* give the client its border back */
768         XSetWindowBorderWidth(obt_display, self->w_client, self->border_width);
769
770         client_move_resize(self, a.x, a.y, a.width, a.height);
771     }
772
773     /* reparent the window out of the frame, and free the frame */
774     frame_engine->frame_ungrab(self->frame, window_map);
775     frame_engine->frame_free(self->frame);
776     self->frame = NULL;
777
778     if (ob_state() != OB_STATE_EXITING) {
779         /* these values should not be persisted across a window
780            unmapping/mapping */
781         OBT_PROP_ERASE(self->w_client, NET_WM_DESKTOP);
782         OBT_PROP_ERASE(self->w_client, NET_WM_STATE);
783         OBT_PROP_ERASE(self->w_client, WM_STATE);
784     } else {
785         /* if we're left in an unmapped state, the client wont be mapped.
786            this is bad, since we will no longer be managing the window on
787            restart */
788         XMapWindow(obt_display, self->w_client);
789     }
790
791     /* these should not be left on the window ever.  other window managers
792        don't necessarily use them and it will mess them up (like compiz) */
793     OBT_PROP_ERASE(self->w_client, NET_WM_VISIBLE_NAME);
794     OBT_PROP_ERASE(self->w_client, NET_WM_VISIBLE_ICON_NAME);
795
796     /* update the list hints */
797     client_set_list();
798
799     ob_debug("Unmanaged window 0x%lx", self->w_client);
800
801     /* free all data allocated in the client struct */
802     g_slist_free(self->transients);
803     for (j = 0; j < self->nicons; ++j)
804         g_free(self->icons[j].data);
805     if (self->nicons > 0)
806         g_free(self->icons);
807     g_free(self->startup_id);
808     g_free(self->wm_command);
809     g_free(self->title);
810     g_free(self->icon_title);
811     g_free(self->original_title);
812     g_free(self->name);
813     g_free(self->class);
814     g_free(self->role);
815     g_free(self->client_machine);
816     g_free(self->sm_client_id);
817     g_free(self);
818 }
819
820 void client_fake_unmanage(ObClient *self)
821 {
822     /* this is all that got allocated to get the decorations */
823
824     frame_engine->frame_free(self->frame);
825     g_free(self);
826 }
827
828 /*! Returns a new structure containing the per-app settings for this client.
829   The returned structure needs to be freed with g_free. */
830 static ObAppSettings *client_get_settings_state(ObClient *self)
831 {
832     ObAppSettings *settings;
833     GSList *it;
834
835     settings = config_create_app_settings();
836
837     for (it = config_per_app_settings; it; it = g_slist_next(it)) {
838         ObAppSettings *app = it->data;
839         gboolean match = TRUE;
840
841         g_assert(app->name != NULL || app->class != NULL);
842
843         /* we know that either name or class is not NULL so it will have to
844            match to use the rule */
845         if (app->name &&
846             !g_pattern_match(app->name, strlen(self->name), self->name, NULL))
847             match = FALSE;
848         else if (app->class &&
849                 !g_pattern_match(app->class,
850                                  strlen(self->class), self->class, NULL))
851             match = FALSE;
852         else if (app->role &&
853                  !g_pattern_match(app->role,
854                                   strlen(self->role), self->role, NULL))
855             match = FALSE;
856
857         if (match) {
858             ob_debug("Window matching: %s", app->name);
859
860             /* copy the settings to our struct, overriding the existing
861                settings if they are not defaults */
862             config_app_settings_copy_non_defaults(app, settings);
863         }
864     }
865
866     if (settings->shade != -1)
867         self->shaded = !!settings->shade;
868     if (settings->decor != -1)
869         self->undecorated = !settings->decor;
870     if (settings->iconic != -1)
871         self->iconic = !!settings->iconic;
872     if (settings->skip_pager != -1)
873         self->skip_pager = !!settings->skip_pager;
874     if (settings->skip_taskbar != -1)
875         self->skip_taskbar = !!settings->skip_taskbar;
876
877     if (settings->max_vert != -1)
878         self->max_vert = !!settings->max_vert;
879     if (settings->max_horz != -1)
880         self->max_horz = !!settings->max_horz;
881
882     if (settings->fullscreen != -1)
883         self->fullscreen = !!settings->fullscreen;
884
885     if (settings->desktop) {
886         if (settings->desktop == DESKTOP_ALL)
887             self->desktop = settings->desktop;
888         else if (settings->desktop > 0 &&
889                  settings->desktop <= screen_num_desktops)
890             self->desktop = settings->desktop - 1;
891     }
892
893     if (settings->layer == -1) {
894         self->below = TRUE;
895         self->above = FALSE;
896     }
897     else if (settings->layer == 0) {
898         self->below = FALSE;
899         self->above = FALSE;
900     }
901     else if (settings->layer == 1) {
902         self->below = FALSE;
903         self->above = TRUE;
904     }
905     return settings;
906 }
907
908 static void client_restore_session_state(ObClient *self)
909 {
910     GList *it;
911
912     ob_debug_type(OB_DEBUG_SM,
913                   "Restore session for client %s", self->title);
914
915     if (!(it = session_state_find(self))) {
916         ob_debug_type(OB_DEBUG_SM,
917                       "Session data not found for client %s", self->title);
918         return;
919     }
920
921     self->session = it->data;
922
923     ob_debug_type(OB_DEBUG_SM, "Session data loaded for client %s",
924                   self->title);
925
926     RECT_SET_POINT(self->area, self->session->x, self->session->y);
927     self->positioned = USPosition;
928     self->sized = USSize;
929     if (self->session->w > 0)
930         self->area.width = self->session->w;
931     if (self->session->h > 0)
932         self->area.height = self->session->h;
933     XResizeWindow(obt_display, self->w_client,
934                   self->area.width, self->area.height);
935
936     self->desktop = (self->session->desktop == DESKTOP_ALL ?
937                      self->session->desktop :
938                      MIN(screen_num_desktops - 1, self->session->desktop));
939     OBT_PROP_SET32(self->w_client, NET_WM_DESKTOP, CARDINAL, self->desktop);
940
941     self->shaded = self->session->shaded;
942     self->iconic = self->session->iconic;
943     self->skip_pager = self->session->skip_pager;
944     self->skip_taskbar = self->session->skip_taskbar;
945     self->fullscreen = self->session->fullscreen;
946     self->above = self->session->above;
947     self->below = self->session->below;
948     self->max_horz = self->session->max_horz;
949     self->max_vert = self->session->max_vert;
950     self->undecorated = self->session->undecorated;
951
952     if (self->session->max_horz) {
953         frame_engine->frame_trigger(self->frame, OB_TRIGGER_MAX_HORZ);
954     }
955     else {
956         frame_engine->frame_trigger(self->frame, OB_TRIGGER_UNMAX_HORZ);
957     }
958     
959     if (self->session->max_vert) {
960         frame_engine->frame_trigger(self->frame, OB_TRIGGER_MAX_VERT);
961     }
962     else {
963         frame_engine->frame_trigger(self->frame, OB_TRIGGER_UNMAX_VERT);
964     }
965 }
966
967 static gboolean client_restore_session_stacking(ObClient *self)
968 {
969     GList *it, *mypos;
970
971     if (!self->session) return FALSE;
972
973     mypos = g_list_find(session_saved_state, self->session);
974     if (!mypos) return FALSE;
975
976     /* start above me and look for the first client */
977     for (it = g_list_previous(mypos); it; it = g_list_previous(it)) {
978         GList *cit;
979
980         for (cit = client_list; cit; cit = g_list_next(cit)) {
981             ObClient *c = cit->data;
982             /* found a client that was in the session, so go below it */
983             if (c->session == it->data) {
984                 stacking_below(CLIENT_AS_WINDOW(self),
985                                CLIENT_AS_WINDOW(cit->data));
986                 return TRUE;
987             }
988         }
989     }
990     return FALSE;
991 }
992
993 void client_move_onscreen(ObClient *self, gboolean rude)
994 {
995     gint x = self->area.x;
996     gint y = self->area.y;
997     if (client_find_onscreen(self, &x, &y,
998                              self->area.width,
999                              self->area.height, rude)) {
1000         client_move(self, x, y);
1001     }
1002 }
1003
1004 gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
1005                               gboolean rude)
1006 {
1007     gint ox = *x, oy = *y;
1008     gboolean rudel = rude, ruder = rude, rudet = rude, rudeb = rude;
1009     gint fw, fh;
1010     Rect desired;
1011     guint i;
1012     gboolean found_mon;
1013
1014     RECT_SET(desired, *x, *y, w, h);
1015     frame_rect_to_frame(self, &desired);
1016
1017     /* get where the frame would be */
1018     frame_client_gravity(self, x, y);
1019
1020     Strut size;
1021     frame_engine->frame_get_size(self->frame, &size);
1022
1023     /* get the requested size of the window with decorations */
1024     fw = size.left + w + size.right;
1025     fh = size.top + h + size.bottom;
1026
1027     /* If rudeness wasn't requested, then still be rude in a given direction
1028        if the client is not moving, only resizing in that direction */
1029     if (!rude) {
1030         Point oldtl, oldtr, oldbl, oldbr;
1031         Point newtl, newtr, newbl, newbr;
1032         gboolean stationary_l, stationary_r, stationary_t, stationary_b;
1033
1034         Rect area;
1035         frame_engine->frame_get_window_area(self->frame, &area);
1036         POINT_SET(oldtl, area.x, area.y);
1037         POINT_SET(oldbr, area.x + area.width - 1,
1038                   area.y + area.height - 1);
1039         POINT_SET(oldtr, oldbr.x, oldtl.y);
1040         POINT_SET(oldbl, oldtl.x, oldbr.y);
1041
1042         POINT_SET(newtl, *x, *y);
1043         POINT_SET(newbr, *x + fw - 1, *y + fh - 1);
1044         POINT_SET(newtr, newbr.x, newtl.y);
1045         POINT_SET(newbl, newtl.x, newbr.y);
1046
1047         /* is it moving or just resizing from some corner? */
1048         stationary_l = oldtl.x == newtl.x;
1049         stationary_r = oldtr.x == newtr.x;
1050         stationary_t = oldtl.y == newtl.y;
1051         stationary_b = oldbl.y == newbl.y;
1052
1053         /* if left edge is growing and didnt move right edge */
1054         if (stationary_r && newtl.x < oldtl.x)
1055             rudel = TRUE;
1056         /* if right edge is growing and didnt move left edge */
1057         if (stationary_l && newtr.x > oldtr.x)
1058             ruder = TRUE;
1059         /* if top edge is growing and didnt move bottom edge */
1060         if (stationary_b && newtl.y < oldtl.y)
1061             rudet = TRUE;
1062         /* if bottom edge is growing and didnt move top edge */
1063         if (stationary_t && newbl.y > oldbl.y)
1064             rudeb = TRUE;
1065     }
1066
1067     /* we iterate through every monitor that the window is at least partially
1068        on, to make sure it is obeying the rules on them all
1069
1070        if the window does not appear on any monitors, then use the first one
1071     */
1072     found_mon = FALSE;
1073     for (i = 0; i < screen_num_monitors; ++i) {
1074         Rect *a;
1075
1076         if (!screen_physical_area_monitor_contains(i, &desired)) {
1077             if (i < screen_num_monitors - 1 || found_mon)
1078                 continue;
1079
1080             /* the window is not inside any monitor! so just use the first
1081                one */
1082             a = screen_area(self->desktop, 0, NULL);
1083         } else {
1084             found_mon = TRUE;
1085             a = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR, &desired);
1086         }
1087
1088         /* This makes sure windows aren't entirely outside of the screen so you
1089            can't see them at all.
1090            It makes sure 10% of the window is on the screen at least. At don't
1091            let it move itself off the top of the screen, which would hide the
1092            titlebar on you. (The user can still do this if they want too, it's
1093            only limiting the application.
1094         */
1095         if (client_normal(self)) {
1096             if (!self->strut.right && *x + fw/10 >= a->x + a->width - 1)
1097                 *x = a->x + a->width - fw/10;
1098             if (!self->strut.bottom && *y + fh/10 >= a->y + a->height - 1)
1099                 *y = a->y + a->height - fh/10;
1100             if (!self->strut.left && *x + fw*9/10 - 1 < a->x)
1101                 *x = a->x - fw*9/10;
1102             if (!self->strut.top && *y + fh*9/10 - 1 < a->y)
1103                 *y = a->y - fh*9/10;
1104         }
1105
1106         /* This here doesn't let windows even a pixel outside the
1107            struts/screen. When called from client_manage, programs placing
1108            themselves are forced completely onscreen, while things like
1109            xterm -geometry resolution-width/2 will work fine. Trying to
1110            place it completely offscreen will be handled in the above code.
1111            Sorry for this confused comment, i am tired. */
1112         if (rudel && !self->strut.left && *x < a->x) *x = a->x;
1113         if (ruder && !self->strut.right && *x + fw > a->x + a->width)
1114             *x = a->x + MAX(0, a->width - fw);
1115
1116         if (rudet && !self->strut.top && *y < a->y) *y = a->y;
1117         if (rudeb && !self->strut.bottom && *y + fh > a->y + a->height)
1118             *y = a->y + MAX(0, a->height - fh);
1119
1120         g_free(a);
1121     }
1122
1123     /* get where the client should be */
1124     frame_frame_gravity(self, x, y);
1125
1126     return ox != *x || oy != *y;
1127 }
1128
1129 static void client_get_all(ObClient *self, gboolean real)
1130 {
1131     /* this is needed for the frame to set itself up */
1132     client_get_area(self);
1133
1134     /* these things can change the decor and functions of the window */
1135
1136     client_get_mwm_hints(self);
1137     /* this can change the mwmhints for special cases */
1138     client_get_type_and_transientness(self);
1139     client_get_state(self);
1140     client_update_normal_hints(self);
1141
1142     /* get the session related properties, these can change decorations
1143        from per-app settings */
1144     client_get_session_ids(self);
1145
1146     /* now we got everything that can affect the decorations */
1147     if (!real)
1148         return;
1149
1150     /* get this early so we have it for debugging */
1151     client_update_title(self);
1152
1153     client_update_protocols(self);
1154
1155     client_update_wmhints(self);
1156     /* this may have already been called from client_update_wmhints */
1157     if (!self->parents && !self->transient_for_group)
1158         client_update_transient_for(self);
1159
1160     client_get_startup_id(self);
1161     client_get_desktop(self);/* uses transient data/group/startup id if a
1162                                 desktop is not specified */
1163     client_get_shaped(self);
1164
1165     {
1166         /* a couple type-based defaults for new windows */
1167
1168         /* this makes sure that these windows appear on all desktops */
1169         if (self->type == OB_CLIENT_TYPE_DESKTOP)
1170             self->desktop = DESKTOP_ALL;
1171     }
1172
1173 #ifdef SYNC
1174     client_update_sync_request_counter(self);
1175 #endif
1176
1177     client_get_colormap(self);
1178     client_update_strut(self);
1179     client_update_icons(self);
1180     client_update_icon_geometry(self);
1181 }
1182
1183 static void client_get_startup_id(ObClient *self)
1184 {
1185     if (!(OBT_PROP_GETS(self->w_client, NET_STARTUP_ID, utf8,
1186                         &self->startup_id)))
1187         if (self->group)
1188             OBT_PROP_GETS(self->group->leader,
1189                           NET_STARTUP_ID, utf8, &self->startup_id);
1190 }
1191
1192 static void client_get_area(ObClient *self)
1193 {
1194     XWindowAttributes wattrib;
1195     Status ret;
1196
1197     ret = XGetWindowAttributes(obt_display, self->w_client, &wattrib);
1198     g_assert(ret != BadWindow);
1199
1200     RECT_SET(self->area, wattrib.x, wattrib.y, wattrib.width, wattrib.height);
1201     POINT_SET(self->root_pos, wattrib.x, wattrib.y);
1202     self->border_width = wattrib.border_width;
1203
1204     ob_debug("client area: %d %d  %d %d  bw %d", wattrib.x, wattrib.y,
1205              wattrib.width, wattrib.height, wattrib.border_width);
1206 }
1207
1208 static void client_get_desktop(ObClient *self)
1209 {
1210     guint32 d = screen_num_desktops; /* an always-invalid value */
1211
1212     if (OBT_PROP_GET32(self->w_client, NET_WM_DESKTOP, CARDINAL, &d)) {
1213         if (d >= screen_num_desktops && d != DESKTOP_ALL)
1214             self->desktop = screen_num_desktops - 1;
1215         else
1216             self->desktop = d;
1217         ob_debug("client requested desktop 0x%x", self->desktop);
1218     } else {
1219         GSList *it;
1220         gboolean first = TRUE;
1221         guint all = screen_num_desktops; /* not a valid value */
1222
1223         /* if they are all on one desktop, then open it on the
1224            same desktop */
1225         for (it = self->parents; it; it = g_slist_next(it)) {
1226             ObClient *c = it->data;
1227
1228             if (c->desktop == DESKTOP_ALL) continue;
1229
1230             if (first) {
1231                 all = c->desktop;
1232                 first = FALSE;
1233             }
1234             else if (all != c->desktop)
1235                 all = screen_num_desktops; /* make it invalid */
1236         }
1237         if (all != screen_num_desktops) {
1238             self->desktop = all;
1239
1240             ob_debug("client desktop set from parents: 0x%x",
1241                      self->desktop);
1242         }
1243         /* try get from the startup-notification protocol */
1244         else if (sn_get_desktop(self->startup_id, &self->desktop)) {
1245             if (self->desktop >= screen_num_desktops &&
1246                 self->desktop != DESKTOP_ALL)
1247                 self->desktop = screen_num_desktops - 1;
1248             ob_debug("client desktop set from startup-notification: 0x%x",
1249                      self->desktop);
1250         }
1251         /* defaults to the current desktop */
1252         else {
1253             self->desktop = screen_desktop;
1254             ob_debug("client desktop set to the current desktop: %d",
1255                      self->desktop);
1256         }
1257     }
1258 }
1259
1260 static void client_get_state(ObClient *self)
1261 {
1262     guint32 *state;
1263     guint num;
1264
1265     if (OBT_PROP_GETA32(self->w_client, NET_WM_STATE, ATOM, &state, &num)) {
1266         gulong i;
1267         for (i = 0; i < num; ++i) {
1268             if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MODAL))
1269                 self->modal = TRUE;
1270             else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SHADED))
1271                 self->shaded = TRUE;
1272             else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN))
1273                 self->iconic = TRUE;
1274             else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR))
1275                 self->skip_taskbar = TRUE;
1276             else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER))
1277                 self->skip_pager = TRUE;
1278             else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN))
1279                 self->fullscreen = TRUE;
1280             else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT)) {
1281                 self->max_vert = TRUE;
1282                 frame_engine->frame_trigger(self->frame, OB_TRIGGER_MAX_VERT);
1283             }
1284             else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ)) {
1285                 self->max_horz = TRUE;
1286                 frame_engine->frame_trigger(self->frame, OB_TRIGGER_MAX_HORZ);
1287             }
1288             else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_ABOVE))
1289                 self->above = TRUE;
1290             else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_BELOW))
1291                 self->below = TRUE;
1292             else if (state[i] == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION))
1293                 self->demands_attention = TRUE;
1294             else if (state[i] == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED))
1295                 self->undecorated = TRUE;
1296         }
1297
1298         g_free(state);
1299     }
1300 }
1301
1302 static void client_get_shaped(ObClient *self)
1303 {
1304     self->shaped = FALSE;
1305 #ifdef   SHAPE
1306     if (obt_display_extension_shape) {
1307         gint foo;
1308         guint ufoo;
1309         gint s;
1310
1311         XShapeSelectInput(obt_display, self->w_client, ShapeNotifyMask);
1312
1313         XShapeQueryExtents(obt_display, self->w_client, &s, &foo,
1314                            &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo,
1315                            &ufoo);
1316         self->shaped = (s != 0);
1317     }
1318 #endif
1319 }
1320
1321 void client_update_transient_for(ObClient *self)
1322 {
1323     Window t = None;
1324     ObClient *target = NULL;
1325     gboolean trangroup = FALSE;
1326
1327     if (XGetTransientForHint(obt_display, self->w_client, &t)) {
1328         if (t != self->w_client) { /* cant be transient to itself! */
1329             ObWindow *tw = window_find(t);
1330             /* if this happens then we need to check for it*/
1331             g_assert(tw != CLIENT_AS_WINDOW(self));
1332             if (tw && WINDOW_IS_CLIENT(tw)) {
1333                 /* watch out for windows with a parent that is something
1334                    different, like a dockapp for example */
1335                 target = WINDOW_AS_CLIENT(tw);
1336             }
1337         }
1338
1339         /* Setting the transient_for to Root is actually illegal, however
1340            applications from time have done this to specify transient for
1341            their group */
1342         if (!target && self->group && t == obt_root(ob_screen))
1343             trangroup = TRUE;
1344     } else if (self->group && self->transient)
1345         trangroup = TRUE;
1346
1347     client_update_transient_tree(self, self->group, self->group,
1348                                  self->transient_for_group, trangroup,
1349                                  client_direct_parent(self), target);
1350     self->transient_for_group = trangroup;
1351
1352 }
1353
1354 static void client_update_transient_tree(ObClient *self,
1355                                          ObGroup *oldgroup, ObGroup *newgroup,
1356                                          gboolean oldgtran, gboolean newgtran,
1357                                          ObClient* oldparent,
1358                                          ObClient *newparent)
1359 {
1360     GSList *it, *next;
1361     ObClient *c;
1362
1363     g_assert(!oldgtran || oldgroup);
1364     g_assert(!newgtran || newgroup);
1365     g_assert((!oldgtran && !oldparent) ||
1366              (oldgtran && !oldparent) ||
1367              (!oldgtran && oldparent));
1368     g_assert((!newgtran && !newparent) ||
1369              (newgtran && !newparent) ||
1370              (!newgtran && newparent));
1371
1372     /* * *
1373       Group transient windows are not allowed to have other group
1374       transient windows as their children.
1375       * * */
1376
1377
1378     /* No change has occured */
1379     if (oldgroup == newgroup &&
1380         oldgtran == newgtran &&
1381         oldparent == newparent) return;
1382
1383     /** Remove the client from the transient tree **/
1384
1385     for (it = self->transients; it; it = next) {
1386         next = g_slist_next(it);
1387         c = it->data;
1388         self->transients = g_slist_delete_link(self->transients, it);
1389         c->parents = g_slist_remove(c->parents, self);
1390     }
1391     for (it = self->parents; it; it = next) {
1392         next = g_slist_next(it);
1393         c = it->data;
1394         self->parents = g_slist_delete_link(self->parents, it);
1395         c->transients = g_slist_remove(c->transients, self);
1396     }
1397
1398     /** Re-add the client to the transient tree **/
1399
1400     /* If we're transient for a group then we need to add ourselves to all our
1401        parents */
1402     if (newgtran) {
1403         for (it = newgroup->members; it; it = g_slist_next(it)) {
1404             c = it->data;
1405             if (c != self &&
1406                 !client_search_top_direct_parent(c)->transient_for_group &&
1407                 client_normal(c))
1408             {
1409                 c->transients = g_slist_prepend(c->transients, self);
1410                 self->parents = g_slist_prepend(self->parents, c);
1411             }
1412         }
1413     }
1414
1415     /* If we are now transient for a single window we need to add ourselves to
1416        its children
1417
1418        WARNING: Cyclical transient ness is possible if two windows are
1419        transient for eachother.
1420     */
1421     else if (newparent &&
1422              /* don't make ourself its child if it is already our child */
1423              !client_is_direct_child(self, newparent) &&
1424              client_normal(newparent))
1425     {
1426         newparent->transients = g_slist_prepend(newparent->transients, self);
1427         self->parents = g_slist_prepend(self->parents, newparent);
1428     }
1429
1430     /* Add any group transient windows to our children. But if we're transient
1431        for the group, then other group transients are not our children.
1432
1433        WARNING: Cyclical transient-ness is possible. For e.g. if:
1434        A is transient for the group
1435        B is transient for A
1436        C is transient for B
1437        A can't be transient for C or we have a cycle
1438     */
1439     if (!newgtran && newgroup &&
1440         (!newparent ||
1441          !client_search_top_direct_parent(newparent)->transient_for_group) &&
1442         client_normal(self))
1443     {
1444         for (it = newgroup->members; it; it = g_slist_next(it)) {
1445             c = it->data;
1446             if (c != self && c->transient_for_group &&
1447                 /* Don't make it our child if it is already our parent */
1448                 !client_is_direct_child(c, self))
1449             {
1450                 self->transients = g_slist_prepend(self->transients, c);
1451                 c->parents = g_slist_prepend(c->parents, self);
1452             }
1453         }
1454     }
1455
1456     /** If we change our group transient-ness, our children change their
1457         effect group transient-ness, which affects how they relate to other
1458         group windows **/
1459
1460     for (it = self->transients; it; it = g_slist_next(it)) {
1461         c = it->data;
1462         if (!c->transient_for_group)
1463             client_update_transient_tree(c, c->group, c->group,
1464                                          c->transient_for_group,
1465                                          c->transient_for_group,
1466                                          client_direct_parent(c),
1467                                          client_direct_parent(c));
1468     }
1469 }
1470
1471 static void client_get_mwm_hints(ObClient *self)
1472 {
1473     guint num;
1474     guint32 *hints;
1475
1476     self->mwmhints.flags = 0; /* default to none */
1477
1478     if (OBT_PROP_GETA32(self->w_client, MOTIF_WM_HINTS, MOTIF_WM_HINTS,
1479                         &hints, &num)) {
1480         if (num >= OB_MWM_ELEMENTS) {
1481             self->mwmhints.flags = hints[0];
1482             self->mwmhints.functions = hints[1];
1483             self->mwmhints.decorations = hints[2];
1484         }
1485         g_free(hints);
1486     }
1487 }
1488
1489 void client_get_type_and_transientness(ObClient *self)
1490 {
1491     guint num, i;
1492     guint32 *val;
1493     Window t;
1494
1495     self->type = -1;
1496     self->transient = FALSE;
1497
1498     if (OBT_PROP_GETA32(self->w_client, NET_WM_WINDOW_TYPE, ATOM, &val, &num)) {
1499         /* use the first value that we know about in the array */
1500         for (i = 0; i < num; ++i) {
1501             if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DESKTOP))
1502                 self->type = OB_CLIENT_TYPE_DESKTOP;
1503             else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DOCK))
1504                 self->type = OB_CLIENT_TYPE_DOCK;
1505             else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_TOOLBAR))
1506                 self->type = OB_CLIENT_TYPE_TOOLBAR;
1507             else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_MENU))
1508                 self->type = OB_CLIENT_TYPE_MENU;
1509             else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_UTILITY))
1510                 self->type = OB_CLIENT_TYPE_UTILITY;
1511             else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_SPLASH))
1512                 self->type = OB_CLIENT_TYPE_SPLASH;
1513             else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_DIALOG))
1514                 self->type = OB_CLIENT_TYPE_DIALOG;
1515             else if (val[i] == OBT_PROP_ATOM(NET_WM_WINDOW_TYPE_NORMAL))
1516                 self->type = OB_CLIENT_TYPE_NORMAL;
1517             else if (val[i] == OBT_PROP_ATOM(KDE_NET_WM_WINDOW_TYPE_OVERRIDE))
1518             {
1519                 /* prevent this window from getting any decor or
1520                    functionality */
1521                 self->mwmhints.flags &= (OB_MWM_FLAG_FUNCTIONS |
1522                                          OB_MWM_FLAG_DECORATIONS);
1523                 self->mwmhints.decorations = 0;
1524                 self->mwmhints.functions = 0;
1525             }
1526             if (self->type != (ObClientType) -1)
1527                 break; /* grab the first legit type */
1528         }
1529         g_free(val);
1530     }
1531
1532     if (XGetTransientForHint(obt_display, self->w_client, &t))
1533         self->transient = TRUE;
1534
1535     if (self->type == (ObClientType) -1) {
1536         /*the window type hint was not set, which means we either classify
1537           ourself as a normal window or a dialog, depending on if we are a
1538           transient. */
1539         if (self->transient)
1540             self->type = OB_CLIENT_TYPE_DIALOG;
1541         else
1542             self->type = OB_CLIENT_TYPE_NORMAL;
1543     }
1544
1545     /* then, based on our type, we can update our transientness.. */
1546     if (self->type == OB_CLIENT_TYPE_DIALOG ||
1547         self->type == OB_CLIENT_TYPE_TOOLBAR ||
1548         self->type == OB_CLIENT_TYPE_MENU ||
1549         self->type == OB_CLIENT_TYPE_UTILITY)
1550     {
1551         self->transient = TRUE;
1552     }
1553 }
1554
1555 void client_update_protocols(ObClient *self)
1556 {
1557     guint32 *proto;
1558     guint num_ret, i;
1559
1560     self->focus_notify = FALSE;
1561     self->delete_window = FALSE;
1562
1563     if (OBT_PROP_GETA32(self->w_client, WM_PROTOCOLS, ATOM, &proto, &num_ret)) {
1564         for (i = 0; i < num_ret; ++i) {
1565             if (proto[i] == OBT_PROP_ATOM(WM_DELETE_WINDOW))
1566                 /* this means we can request the window to close */
1567                 self->delete_window = TRUE;
1568             else if (proto[i] == OBT_PROP_ATOM(WM_TAKE_FOCUS))
1569                 /* if this protocol is requested, then the window will be
1570                    notified whenever we want it to receive focus */
1571                 self->focus_notify = TRUE;
1572             else if (proto[i] == OBT_PROP_ATOM(NET_WM_PING))
1573                 /* if this protocol is requested, then the window will allow
1574                    pings to determine if it is still alive */
1575                 self->ping = TRUE;
1576 #ifdef SYNC
1577             else if (proto[i] == OBT_PROP_ATOM(NET_WM_SYNC_REQUEST))
1578                 /* if this protocol is requested, then resizing the
1579                    window will be synchronized between the frame and the
1580                    client */
1581                 self->sync_request = TRUE;
1582 #endif
1583         }
1584         g_free(proto);
1585     }
1586 }
1587
1588 #ifdef SYNC
1589 void client_update_sync_request_counter(ObClient *self)
1590 {
1591     guint32 i;
1592
1593     if (OBT_PROP_GET32(self->w_client, NET_WM_SYNC_REQUEST_COUNTER, CARDINAL,&i))
1594     {
1595         self->sync_counter = i;
1596     } else
1597         self->sync_counter = None;
1598 }
1599 #endif
1600
1601 static void client_get_colormap(ObClient *self)
1602 {
1603     XWindowAttributes wa;
1604
1605     if (XGetWindowAttributes(obt_display, self->w_client, &wa))
1606         client_update_colormap(self, wa.colormap);
1607 }
1608
1609 void client_update_colormap(ObClient *self, Colormap colormap)
1610 {
1611     if (colormap == self->colormap) return;
1612
1613     ob_debug("Setting client %s colormap: 0x%x", self->title, colormap);
1614
1615     if (client_focused(self)) {
1616         screen_install_colormap(self, FALSE); /* uninstall old one */
1617         self->colormap = colormap;
1618         screen_install_colormap(self, FALSE); /* install new one */
1619     } else
1620         self->colormap = colormap;
1621 }
1622
1623 void client_update_normal_hints(ObClient *self)
1624 {
1625     XSizeHints size;
1626     glong ret;
1627
1628     /* defaults */
1629     self->min_ratio = 0.0f;
1630     self->max_ratio = 0.0f;
1631     SIZE_SET(self->size_inc, 1, 1);
1632     SIZE_SET(self->base_size, 0, 0);
1633     SIZE_SET(self->min_size, 0, 0);
1634     SIZE_SET(self->max_size, G_MAXINT, G_MAXINT);
1635
1636     /* get the hints from the window */
1637     if (XGetWMNormalHints(obt_display, self->w_client, &size, &ret)) {
1638         /* normal windows can't request placement! har har
1639         if (!client_normal(self))
1640         */
1641         self->positioned = (size.flags & (PPosition|USPosition));
1642         self->sized = (size.flags & (PSize|USSize));
1643
1644         if (size.flags & PWinGravity)
1645             self->gravity = size.win_gravity;
1646
1647         if (size.flags & PAspect) {
1648             if (size.min_aspect.y)
1649                 self->min_ratio =
1650                     (gfloat) size.min_aspect.x / size.min_aspect.y;
1651             if (size.max_aspect.y)
1652                 self->max_ratio =
1653                     (gfloat) size.max_aspect.x / size.max_aspect.y;
1654         }
1655
1656         if (size.flags & PMinSize)
1657             SIZE_SET(self->min_size, size.min_width, size.min_height);
1658
1659         if (size.flags & PMaxSize)
1660             SIZE_SET(self->max_size, size.max_width, size.max_height);
1661
1662         if (size.flags & PBaseSize)
1663             SIZE_SET(self->base_size, size.base_width, size.base_height);
1664
1665         if (size.flags & PResizeInc && size.width_inc && size.height_inc)
1666             SIZE_SET(self->size_inc, size.width_inc, size.height_inc);
1667
1668         ob_debug("Normal hints: min size (%d %d) max size (%d %d)",
1669                  self->min_size.width, self->min_size.height,
1670                  self->max_size.width, self->max_size.height);
1671         ob_debug("size inc (%d %d) base size (%d %d)",
1672                  self->size_inc.width, self->size_inc.height,
1673                  self->base_size.width, self->base_size.height);
1674     }
1675     else
1676         ob_debug("Normal hints: not set");
1677 }
1678
1679 void client_setup_decor_and_functions(ObClient *self, gboolean reconfig)
1680 {
1681     /* start with everything (cept fullscreen) */
1682     self->decorations =
1683         (OB_FRAME_DECOR_TITLEBAR |
1684          OB_FRAME_DECOR_HANDLE |
1685          OB_FRAME_DECOR_GRIPS |
1686          OB_FRAME_DECOR_BORDER |
1687          OB_FRAME_DECOR_ICON |
1688          OB_FRAME_DECOR_ALLDESKTOPS |
1689          OB_FRAME_DECOR_ICONIFY |
1690          OB_FRAME_DECOR_MAXIMIZE |
1691          OB_FRAME_DECOR_SHADE |
1692          OB_FRAME_DECOR_CLOSE);
1693     self->functions =
1694         (OB_CLIENT_FUNC_RESIZE |
1695          OB_CLIENT_FUNC_MOVE |
1696          OB_CLIENT_FUNC_ICONIFY |
1697          OB_CLIENT_FUNC_MAXIMIZE |
1698          OB_CLIENT_FUNC_SHADE |
1699          OB_CLIENT_FUNC_CLOSE |
1700          OB_CLIENT_FUNC_BELOW |
1701          OB_CLIENT_FUNC_ABOVE |
1702          OB_CLIENT_FUNC_UNDECORATE);
1703
1704     if (!(self->min_size.width < self->max_size.width ||
1705           self->min_size.height < self->max_size.height))
1706         self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1707
1708     switch (self->type) {
1709     case OB_CLIENT_TYPE_NORMAL:
1710         /* normal windows retain all of the possible decorations and
1711            functionality, and can be fullscreen */
1712         self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
1713         break;
1714
1715     case OB_CLIENT_TYPE_DIALOG:
1716         /* sometimes apps make dialog windows fullscreen for some reason (for
1717            e.g. kpdf does this..) */
1718         self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
1719         break;
1720
1721     case OB_CLIENT_TYPE_UTILITY:
1722         /* these windows don't have anything added or removed by default */
1723         break;
1724
1725     case OB_CLIENT_TYPE_MENU:
1726     case OB_CLIENT_TYPE_TOOLBAR:
1727         /* these windows can't iconify or maximize */
1728         self->decorations &= ~(OB_FRAME_DECOR_ICONIFY |
1729                                OB_FRAME_DECOR_MAXIMIZE);
1730         self->functions &= ~(OB_CLIENT_FUNC_ICONIFY |
1731                              OB_CLIENT_FUNC_MAXIMIZE);
1732         break;
1733
1734     case OB_CLIENT_TYPE_SPLASH:
1735         /* these don't get get any decorations, and the only thing you can
1736            do with them is move them */
1737         self->decorations = 0;
1738         self->functions = OB_CLIENT_FUNC_MOVE;
1739         break;
1740
1741     case OB_CLIENT_TYPE_DESKTOP:
1742         /* these windows are not manipulated by the window manager */
1743         self->decorations = 0;
1744         self->functions = 0;
1745         break;
1746
1747     case OB_CLIENT_TYPE_DOCK:
1748         /* these windows are not manipulated by the window manager, but they
1749            can set below layer which has a special meaning */
1750         self->decorations = 0;
1751         self->functions = OB_CLIENT_FUNC_BELOW;
1752         break;
1753     }
1754
1755     /* Mwm Hints are applied subtractively to what has already been chosen for
1756        decor and functionality */
1757     if (self->mwmhints.flags & OB_MWM_FLAG_DECORATIONS) {
1758         if (! (self->mwmhints.decorations & OB_MWM_DECOR_ALL)) {
1759             if (! ((self->mwmhints.decorations & OB_MWM_DECOR_HANDLE) ||
1760                    (self->mwmhints.decorations & OB_MWM_DECOR_TITLE)))
1761             {
1762                 /* if the mwm hints request no handle or title, then all
1763                    decorations are disabled, but keep the border if that's
1764                    specified */
1765                 if (self->mwmhints.decorations & OB_MWM_DECOR_BORDER)
1766                     self->decorations = OB_FRAME_DECOR_BORDER;
1767                 else
1768                     self->decorations = 0;
1769             }
1770         }
1771     }
1772
1773     if (self->mwmhints.flags & OB_MWM_FLAG_FUNCTIONS) {
1774         if (! (self->mwmhints.functions & OB_MWM_FUNC_ALL)) {
1775             if (! (self->mwmhints.functions & OB_MWM_FUNC_RESIZE))
1776                 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1777             if (! (self->mwmhints.functions & OB_MWM_FUNC_MOVE))
1778                 self->functions &= ~OB_CLIENT_FUNC_MOVE;
1779             /* dont let mwm hints kill any buttons
1780                if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1781                self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1782                if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1783                self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1784             */
1785             /* dont let mwm hints kill the close button
1786                if (! (self->mwmhints.functions & MwmFunc_Close))
1787                self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1788         }
1789     }
1790
1791     if (!(self->functions & OB_CLIENT_FUNC_SHADE))
1792         self->decorations &= ~OB_FRAME_DECOR_SHADE;
1793     if (!(self->functions & OB_CLIENT_FUNC_ICONIFY))
1794         self->decorations &= ~OB_FRAME_DECOR_ICONIFY;
1795     if (!(self->functions & OB_CLIENT_FUNC_RESIZE))
1796         self->decorations &= ~(OB_FRAME_DECOR_GRIPS | OB_FRAME_DECOR_HANDLE);
1797
1798     /* can't maximize without moving/resizing */
1799     if (!((self->functions & OB_CLIENT_FUNC_MAXIMIZE) &&
1800           (self->functions & OB_CLIENT_FUNC_MOVE) &&
1801           (self->functions & OB_CLIENT_FUNC_RESIZE))) {
1802         self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1803         self->decorations &= ~OB_FRAME_DECOR_MAXIMIZE;
1804     }
1805
1806     if (self->max_horz && self->max_vert) {
1807         /* you can't resize fully maximized windows */
1808         self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1809         /* kill the handle on fully maxed windows */
1810         self->decorations &= ~(OB_FRAME_DECOR_HANDLE | OB_FRAME_DECOR_GRIPS);
1811     }
1812
1813     /* If there are no decorations to remove, don't allow the user to try
1814        toggle the state */
1815     if (self->decorations == 0)
1816         self->functions &= ~OB_CLIENT_FUNC_UNDECORATE;
1817
1818     /* finally, the user can have requested no decorations, which overrides
1819        everything (but doesnt give it a border if it doesnt have one) */
1820     if (self->undecorated)
1821         self->decorations = 0;
1822
1823     /* if we don't have a titlebar, then we cannot shade! */
1824     if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
1825         self->functions &= ~OB_CLIENT_FUNC_SHADE;
1826
1827     /* now we need to check against rules for the client's current state */
1828     if (self->fullscreen) {
1829         self->functions &= (OB_CLIENT_FUNC_CLOSE |
1830                             OB_CLIENT_FUNC_FULLSCREEN |
1831                             OB_CLIENT_FUNC_ICONIFY);
1832         self->decorations = 0;
1833     }
1834
1835     client_change_allowed_actions(self);
1836
1837     if (reconfig)
1838         /* force reconfigure to make sure decorations are updated */
1839         client_reconfigure(self, TRUE);
1840 }
1841
1842 static void client_change_allowed_actions(ObClient *self)
1843 {
1844     gulong actions[12];
1845     gint num = 0;
1846
1847     /* desktop windows are kept on all desktops */
1848     if (self->type != OB_CLIENT_TYPE_DESKTOP)
1849         actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_CHANGE_DESKTOP);
1850
1851     if (self->functions & OB_CLIENT_FUNC_SHADE)
1852         actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_SHADE);
1853     if (self->functions & OB_CLIENT_FUNC_CLOSE)
1854         actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_CLOSE);
1855     if (self->functions & OB_CLIENT_FUNC_MOVE)
1856         actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MOVE);
1857     if (self->functions & OB_CLIENT_FUNC_ICONIFY)
1858         actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MINIMIZE);
1859     if (self->functions & OB_CLIENT_FUNC_RESIZE)
1860         actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_RESIZE);
1861     if (self->functions & OB_CLIENT_FUNC_FULLSCREEN)
1862         actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_FULLSCREEN);
1863     if (self->functions & OB_CLIENT_FUNC_MAXIMIZE) {
1864         actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_HORZ);
1865         actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_MAXIMIZE_VERT);
1866     }
1867     if (self->functions & OB_CLIENT_FUNC_ABOVE)
1868         actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_ABOVE);
1869     if (self->functions & OB_CLIENT_FUNC_BELOW)
1870         actions[num++] = OBT_PROP_ATOM(NET_WM_ACTION_BELOW);
1871     if (self->functions & OB_CLIENT_FUNC_UNDECORATE)
1872         actions[num++] = OBT_PROP_ATOM(OB_WM_ACTION_UNDECORATE);
1873
1874     OBT_PROP_SETA32(self->w_client, NET_WM_ALLOWED_ACTIONS, ATOM, actions, num);
1875
1876     /* make sure the window isn't breaking any rules now
1877
1878        don't check ICONIFY here.  just cuz a window can't iconify doesnt mean
1879        it can't be iconified with its parent
1880     */
1881
1882     if (!(self->functions & OB_CLIENT_FUNC_SHADE) && self->shaded) {
1883         if (self->frame) client_shade(self, FALSE);
1884         else self->shaded = FALSE;
1885     }
1886     if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) && self->fullscreen) {
1887         if (self->frame) client_fullscreen(self, FALSE);
1888         else self->fullscreen = FALSE;
1889     }
1890     if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && (self->max_horz ||
1891                                                          self->max_vert)) {
1892         if (self->frame) client_maximize(self, FALSE, 0);
1893         else {
1894             self->max_vert = self->max_horz = FALSE;
1895             frame_engine->frame_trigger (self->frame, OB_TRIGGER_UNMAX_VERT);
1896             frame_engine->frame_trigger (self->frame, OB_TRIGGER_UNMAX_HORZ);
1897         }
1898
1899     }
1900 }
1901
1902 void client_update_wmhints(ObClient *self)
1903 {
1904     XWMHints *hints;
1905
1906     /* assume a window takes input if it doesnt specify */
1907     self->can_focus = TRUE;
1908
1909     if ((hints = XGetWMHints(obt_display, self->w_client)) != NULL) {
1910         gboolean ur;
1911
1912         if (hints->flags & InputHint)
1913             self->can_focus = hints->input;
1914
1915         /* only do this when first managing the window *AND* when we aren't
1916            starting up! */
1917         if (ob_state() != OB_STATE_STARTING && self->frame == NULL)
1918             if (hints->flags & StateHint)
1919                 self->iconic = hints->initial_state == IconicState;
1920
1921         ur = self->urgent;
1922         self->urgent = (hints->flags & XUrgencyHint);
1923         if (self->urgent && !ur)
1924             client_hilite(self, TRUE);
1925         else if (!self->urgent && ur && self->demands_attention)
1926             client_hilite(self, FALSE);
1927
1928         if (!(hints->flags & WindowGroupHint))
1929             hints->window_group = None;
1930
1931         /* did the group state change? */
1932         if (hints->window_group !=
1933             (self->group ? self->group->leader : None))
1934         {
1935             ObGroup *oldgroup = self->group;
1936
1937             /* remove from the old group if there was one */
1938             if (self->group != NULL) {
1939                 group_remove(self->group, self);
1940                 self->group = NULL;
1941             }
1942
1943             /* add ourself to the group if we have one */
1944             if (hints->window_group != None) {
1945                 self->group = group_add(hints->window_group, self);
1946             }
1947
1948             /* Put ourselves into the new group's transient tree, and remove
1949                ourselves from the old group's */
1950             client_update_transient_tree(self, oldgroup, self->group,
1951                                          self->transient_for_group,
1952                                          self->transient_for_group,
1953                                          client_direct_parent(self),
1954                                          client_direct_parent(self));
1955
1956             /* Lastly, being in a group, or not, can change if the window is
1957                transient for anything.
1958
1959                The logic for this is:
1960                self->transient = TRUE always if the window wants to be
1961                transient for something, even if transient_for was NULL because
1962                it wasn't in a group before.
1963
1964                If parents was NULL and oldgroup was NULL we can assume
1965                that when we add the new group, it will become transient for
1966                something.
1967
1968                If transient_for_group is TRUE, then it must have already
1969                had a group. If it is getting a new group, the above call to
1970                client_update_transient_tree has already taken care of
1971                everything ! If it is losing all group status then it will
1972                no longer be transient for anything and that needs to be
1973                updated.
1974             */
1975             if (self->transient &&
1976                 ((self->parents == NULL && oldgroup == NULL) ||
1977                  (self->transient_for_group && !self->group)))
1978                 client_update_transient_for(self);
1979         }
1980
1981         /* the WM_HINTS can contain an icon */
1982         if (hints->flags & IconPixmapHint)
1983             client_update_icons(self);
1984
1985         XFree(hints);
1986     }
1987 }
1988
1989 void client_update_title(ObClient *self)
1990 {
1991     gchar *data = NULL;
1992     gchar *visible = NULL;
1993
1994     g_free(self->title);
1995     g_free(self->original_title);
1996
1997     /* try netwm */
1998     if (!OBT_PROP_GETS(self->w_client, NET_WM_NAME, utf8, &data)) {
1999         /* try old x stuff */
2000         if (!(OBT_PROP_GETS(self->w_client, WM_NAME, locale, &data)
2001               || OBT_PROP_GETS(self->w_client, WM_NAME, utf8, &data))) {
2002             if (self->transient) {
2003     /*
2004     GNOME alert windows are not given titles:
2005     http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
2006     */
2007                 data = g_strdup("");
2008             } else
2009                 data = g_strdup("Unnamed Window");
2010         }
2011     }
2012     self->original_title = g_strdup(data);
2013
2014     if (self->client_machine) {
2015         visible = g_strdup_printf("%s (%s)", data, self->client_machine);
2016         g_free(data);
2017     } else
2018         visible = data;
2019
2020     if (self->not_responding) {
2021         data = visible;
2022         if (self->kill_level > 0)
2023             visible = g_strdup_printf("%s - [%s]", data, _("Killing..."));
2024         else
2025             visible = g_strdup_printf("%s - [%s]", data, _("Not Responding"));
2026         g_free(data);
2027     }
2028
2029     OBT_PROP_SETS(self->w_client, NET_WM_VISIBLE_NAME, utf8, visible);
2030     self->title = visible;
2031
2032     if (self->frame) {
2033         /* update title render */
2034         frame_engine->frame_update_title (self->frame, self->title);
2035         frame_engine->frame_update_skin (self->frame);
2036     }
2037
2038     /* update the icon title */
2039     data = NULL;
2040     g_free(self->icon_title);
2041
2042     /* try netwm */
2043     if (!OBT_PROP_GETS(self->w_client, NET_WM_ICON_NAME, utf8, &data))
2044         /* try old x stuff */
2045         if (!(OBT_PROP_GETS(self->w_client, WM_ICON_NAME, locale, &data) ||
2046               OBT_PROP_GETS(self->w_client, WM_ICON_NAME, utf8, &data)))
2047             data = g_strdup(self->title);
2048
2049     if (self->client_machine) {
2050         visible = g_strdup_printf("%s (%s)", data, self->client_machine);
2051         g_free(data);
2052     } else
2053         visible = data;
2054
2055     if (self->not_responding) {
2056         data = visible;
2057         if (self->kill_level > 0)
2058             visible = g_strdup_printf("%s - [%s]", data, _("Killing..."));
2059         else
2060             visible = g_strdup_printf("%s - [%s]", data, _("Not Responding"));
2061         g_free(data);
2062     }
2063
2064     OBT_PROP_SETS(self->w_client, NET_WM_VISIBLE_ICON_NAME, utf8, visible);
2065     self->icon_title = visible;
2066 }
2067
2068 void client_update_strut(ObClient *self)
2069 {
2070     guint num;
2071     guint32 *data;
2072     gboolean got = FALSE;
2073     StrutPartial strut;
2074
2075     if (OBT_PROP_GETA32(self->w_client, NET_WM_STRUT_PARTIAL, CARDINAL,
2076                         &data, &num))
2077     {
2078         if (num == 12) {
2079             got = TRUE;
2080             STRUT_PARTIAL_SET(strut,
2081                               data[0], data[2], data[1], data[3],
2082                               data[4], data[5], data[8], data[9],
2083                               data[6], data[7], data[10], data[11]);
2084         }
2085         g_free(data);
2086     }
2087
2088     if (!got &&
2089         OBT_PROP_GETA32(self->w_client, NET_WM_STRUT, CARDINAL, &data, &num)) {
2090         if (num == 4) {
2091             Rect *a;
2092
2093             got = TRUE;
2094
2095             /* use the screen's width/height */
2096             a = screen_physical_area_all_monitors();
2097
2098             STRUT_PARTIAL_SET(strut,
2099                               data[0], data[2], data[1], data[3],
2100                               a->y, a->y + a->height - 1,
2101                               a->x, a->x + a->width - 1,
2102                               a->y, a->y + a->height - 1,
2103                               a->x, a->x + a->width - 1);
2104             g_free(a);
2105         }
2106         g_free(data);
2107     }
2108
2109     if (!got)
2110         STRUT_PARTIAL_SET(strut, 0, 0, 0, 0,
2111                           0, 0, 0, 0, 0, 0, 0, 0);
2112
2113     if (!STRUT_EQUAL(strut, self->strut)) {
2114         self->strut = strut;
2115
2116         /* updating here is pointless while we're being mapped cuz we're not in
2117            the client list yet */
2118         if (self->frame)
2119             screen_update_areas();
2120     }
2121 }
2122
2123 /* Avoid storing icons above this size if possible */
2124 #define AVOID_ABOVE 64
2125
2126 void client_update_icons(ObClient *self)
2127 {
2128     guint num;
2129     guint32 *data;
2130     guint w, h, i, j;
2131     guint num_seen;  /* number of icons present */
2132     guint num_small_seen;  /* number of icons small enough present */
2133     guint smallest, smallest_area;
2134
2135     for (i = 0; i < self->nicons; ++i)
2136         g_free(self->icons[i].data);
2137     if (self->nicons > 0)
2138         g_free(self->icons);
2139     self->nicons = 0;
2140
2141     if (OBT_PROP_GETA32(self->w_client, NET_WM_ICON, CARDINAL, &data, &num)) {
2142         /* figure out how many valid icons are in here */
2143         i = 0;
2144         num_seen = num_small_seen = 0;
2145         smallest = smallest_area = 0;
2146         if (num > 2)
2147             while (i < num) {
2148                 w = data[i++];
2149                 h = data[i++];
2150                 i += w * h;
2151                 /* watch for it being too small for the specified size, or for
2152                    zero sized icons. */
2153                 if (i > num || w == 0 || h == 0) break;
2154
2155                 if (!smallest_area || w*h < smallest_area) {
2156                     smallest = num_seen;
2157                     smallest_area = w*h;
2158                 }
2159                 ++num_seen;
2160                 if (w <= AVOID_ABOVE && h <= AVOID_ABOVE)
2161                     ++num_small_seen;
2162             }
2163         if (num_small_seen > 0)
2164             self->nicons = num_small_seen;
2165         else if (num_seen)
2166             self->nicons = 1;
2167
2168         self->icons = g_new(ObClientIcon, self->nicons);
2169
2170         /* store the icons */
2171         i = 0;
2172         for (j = 0; j < self->nicons;) {
2173             guint x, y, t;
2174
2175             w = self->icons[j].width = data[i++];
2176             h = self->icons[j].height = data[i++];
2177
2178             /* if there are some icons smaller than the threshold, we're
2179                skipping all the ones above */
2180             if (num_small_seen > 0) {
2181                 if (w > AVOID_ABOVE || h > AVOID_ABOVE) {
2182                     i += w*h;
2183                     continue;
2184                 }
2185             }
2186             /* if there were no icons smaller than the threshold, then we are
2187                only taking the smallest available one we saw */
2188             else if (j != smallest) {
2189                 i += w*h;
2190                 continue;
2191             }
2192
2193             self->icons[j].data = g_new(RrPixel32, w * h);
2194             for (x = 0, y = 0, t = 0; t < w * h; ++t, ++x, ++i) {
2195                 if (x >= w) {
2196                     x = 0;
2197                     ++y;
2198                 }
2199                 self->icons[j].data[t] =
2200                     (((data[i] >> 24) & 0xff) << RrDefaultAlphaOffset) +
2201                     (((data[i] >> 16) & 0xff) << RrDefaultRedOffset) +
2202                     (((data[i] >> 8) & 0xff) << RrDefaultGreenOffset) +
2203                     (((data[i] >> 0) & 0xff) << RrDefaultBlueOffset);
2204             }
2205             g_assert(i <= num);
2206
2207             ++j;
2208         }
2209
2210         g_free(data);
2211     } else {
2212         XWMHints *hints;
2213
2214         if ((hints = XGetWMHints(obt_display, self->w_client))) {
2215             if (hints->flags & IconPixmapHint) {
2216                 self->nicons = 1;
2217                 self->icons = g_new(ObClientIcon, self->nicons);
2218                 obt_display_ignore_errors(TRUE);
2219                 if (!RrPixmapToRGBA(ob_rr_inst,
2220                                     hints->icon_pixmap,
2221                                     (hints->flags & IconMaskHint ?
2222                                      hints->icon_mask : None),
2223                                     &self->icons[0].width,
2224                                     &self->icons[0].height,
2225                                     &self->icons[0].data))
2226                 {
2227                     g_free(self->icons);
2228                     self->nicons = 0;
2229                 }
2230                 obt_display_ignore_errors(FALSE);
2231             }
2232             XFree(hints);
2233         }
2234     }
2235
2236     /* set the default icon onto the window
2237        in theory, this could be a race, but if a window doesn't set an icon
2238        or removes it entirely, it's not very likely it is going to set one
2239        right away afterwards
2240
2241        if it has parents, then one of them will have an icon already
2242     */
2243     if (self->nicons == 0 && !self->parents) {
2244         RrPixel32 *icon = ob_rr_theme->def_win_icon;
2245         gulong *data;
2246
2247         data = g_new(gulong, 48*48+2);
2248         data[0] = data[1] =  48;
2249         for (i = 0; i < 48*48; ++i)
2250             data[i+2] = (((icon[i] >> RrDefaultAlphaOffset) & 0xff) << 24) +
2251                 (((icon[i] >> RrDefaultRedOffset) & 0xff) << 16) +
2252                 (((icon[i] >> RrDefaultGreenOffset) & 0xff) << 8) +
2253                 (((icon[i] >> RrDefaultBlueOffset) & 0xff) << 0);
2254         OBT_PROP_SETA32(self->w_client, NET_WM_ICON, CARDINAL, data, 48*48+2);
2255         g_free(data);
2256     } else if (self->frame)
2257         frame_engine->frame_update_skin (self->frame);
2258         /* don't draw the icon empty if we're just setting one now anyways,
2259            we'll get the property change any second */
2260         //frame_adjust_icon(self->frame);
2261 }
2262
2263 void client_update_icon_geometry(ObClient *self)
2264 {
2265     guint num;
2266     guint32 *data;
2267
2268     RECT_SET(self->icon_geometry, 0, 0, 0, 0);
2269
2270     if (OBT_PROP_GETA32(self->w_client, NET_WM_ICON_GEOMETRY, CARDINAL,
2271                         &data, &num))
2272     {
2273         if (num == 4)
2274             /* don't let them set it with an area < 0 */
2275             RECT_SET(self->icon_geometry, data[0], data[1],
2276                      MAX(data[2],0), MAX(data[3],0));
2277         g_free(data);
2278     }
2279 }
2280
2281 static void client_get_session_ids(ObClient *self)
2282 {
2283     guint32 leader;
2284     gboolean got;
2285     gchar *s;
2286     gchar **ss;
2287
2288     if (!OBT_PROP_GET32(self->w_client, WM_CLIENT_LEADER, WINDOW, &leader))
2289         leader = None;
2290
2291     /* get the SM_CLIENT_ID */
2292     got = FALSE;
2293     if (leader)
2294         got = OBT_PROP_GETS(leader, SM_CLIENT_ID, locale, &self->sm_client_id);
2295     if (!got)
2296         OBT_PROP_GETS(self->w_client, SM_CLIENT_ID, locale, &self->sm_client_id);
2297
2298     /* get the WM_CLASS (name and class). make them "" if they are not
2299        provided */
2300     got = FALSE;
2301     if (leader)
2302         got = OBT_PROP_GETSS(leader, WM_CLASS, locale, &ss);
2303     if (!got)
2304         got = OBT_PROP_GETSS(self->w_client, WM_CLASS, locale, &ss);
2305
2306     if (got) {
2307         if (ss[0]) {
2308             self->name = g_strdup(ss[0]);
2309             if (ss[1])
2310                 self->class = g_strdup(ss[1]);
2311         }
2312         g_strfreev(ss);
2313     }
2314
2315     if (self->name == NULL) self->name = g_strdup("");
2316     if (self->class == NULL) self->class = g_strdup("");
2317
2318     /* get the WM_WINDOW_ROLE. make it "" if it is not provided */
2319     got = FALSE;
2320     if (leader)
2321         got = OBT_PROP_GETS(leader, WM_WINDOW_ROLE, locale, &s);
2322     if (!got)
2323         got = OBT_PROP_GETS(self->w_client, WM_WINDOW_ROLE, locale, &s);
2324
2325     if (got)
2326         self->role = s;
2327     else
2328         self->role = g_strdup("");
2329
2330     /* get the WM_COMMAND */
2331     got = FALSE;
2332
2333     if (leader)
2334         got = OBT_PROP_GETSS(leader, WM_COMMAND, locale, &ss);
2335     if (!got)
2336         got = OBT_PROP_GETSS(self->w_client, WM_COMMAND, locale, &ss);
2337
2338     if (got) {
2339         /* merge/mash them all together */
2340         gchar *merge = NULL;
2341         gint i;
2342
2343         for (i = 0; ss[i]; ++i) {
2344             gchar *tmp = merge;
2345             if (merge)
2346                 merge = g_strconcat(merge, ss[i], NULL);
2347             else
2348                 merge = g_strconcat(ss[i], NULL);
2349             g_free(tmp);
2350         }
2351         g_strfreev(ss);
2352
2353         self->wm_command = merge;
2354     }
2355
2356     /* get the WM_CLIENT_MACHINE */
2357     got = FALSE;
2358     if (leader)
2359         got = OBT_PROP_GETS(leader, WM_CLIENT_MACHINE, locale, &s);
2360     if (!got)
2361         got = OBT_PROP_GETS(self->w_client, WM_CLIENT_MACHINE, locale, &s);
2362
2363     if (got) {
2364         gchar localhost[128];
2365         guint32 pid;
2366
2367         gethostname(localhost, 127);
2368         localhost[127] = '\0';
2369         if (strcmp(localhost, s) != 0)
2370             self->client_machine = s;
2371         else
2372             g_free(s);
2373
2374         /* see if it has the PID set too (the PID requires that the
2375            WM_CLIENT_MACHINE be set) */
2376         if (OBT_PROP_GET32(self->w_client, NET_WM_PID, CARDINAL, &pid))
2377             self->pid = pid;
2378     }
2379 }
2380
2381 static void client_change_wm_state(ObClient *self)
2382 {
2383     gulong state[2];
2384     glong old;
2385
2386     old = self->wmstate;
2387
2388     if (self->shaded || self->iconic ||
2389         (self->desktop != DESKTOP_ALL && self->desktop != screen_desktop))
2390     {
2391         self->wmstate = IconicState;
2392     } else
2393         self->wmstate = NormalState;
2394
2395     if (old != self->wmstate) {
2396         OBT_PROP_MSG(ob_screen, self->w_client, KDE_WM_CHANGE_STATE,
2397                      self->wmstate, 1, 0, 0, 0);
2398
2399         state[0] = self->wmstate;
2400         state[1] = None;
2401         OBT_PROP_SETA32(self->w_client, WM_STATE, WM_STATE, state, 2);
2402     }
2403 }
2404
2405 static void client_change_state(ObClient *self)
2406 {
2407     gulong netstate[12];
2408     guint num;
2409
2410     num = 0;
2411     if (self->modal)
2412         netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MODAL);
2413     if (self->shaded)
2414         netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SHADED);
2415     if (self->iconic)
2416         netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_HIDDEN);
2417     if (self->skip_taskbar)
2418         netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR);
2419     if (self->skip_pager)
2420         netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER);
2421     if (self->fullscreen)
2422         netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN);
2423     if (self->max_vert)
2424         netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT);
2425     if (self->max_horz)
2426         netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ);
2427     if (self->above)
2428         netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_ABOVE);
2429     if (self->below)
2430         netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_BELOW);
2431     if (self->demands_attention)
2432         netstate[num++] = OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION);
2433     if (self->undecorated)
2434         netstate[num++] = OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED);
2435     OBT_PROP_SETA32(self->w_client, NET_WM_STATE, ATOM, netstate, num);
2436
2437     if (self->frame)
2438         frame_engine->frame_update_layout (self->frame, self->area, FALSE, FALSE);
2439 }
2440
2441 ObClient *client_search_focus_tree(ObClient *self)
2442 {
2443     GSList *it;
2444     ObClient *ret;
2445
2446     for (it = self->transients; it; it = g_slist_next(it)) {
2447         if (client_focused(it->data)) return it->data;
2448         if ((ret = client_search_focus_tree(it->data))) return ret;
2449     }
2450     return NULL;
2451 }
2452
2453 ObClient *client_search_focus_tree_full(ObClient *self)
2454 {
2455     if (self->parents) {
2456         GSList *it;
2457
2458         for (it = self->parents; it; it = g_slist_next(it)) {
2459             ObClient *c = it->data;
2460             if ((c = client_search_focus_tree_full(it->data))) return c;
2461         }
2462
2463         return NULL;
2464     }
2465     else {
2466         /* this function checks the whole tree, the client_search_focus_tree
2467            does not, so we need to check this window */
2468         if (client_focused(self))
2469             return self;
2470         return client_search_focus_tree(self);
2471     }
2472 }
2473
2474 ObClient *client_search_focus_group_full(ObClient *self)
2475 {
2476     GSList *it;
2477
2478     if (self->group) {
2479         for (it = self->group->members; it; it = g_slist_next(it)) {
2480             ObClient *c = it->data;
2481
2482             if (client_focused(c)) return c;
2483             if ((c = client_search_focus_tree(it->data))) return c;
2484         }
2485     } else
2486         if (client_focused(self)) return self;
2487     return NULL;
2488 }
2489
2490 gboolean client_has_parent(ObClient *self)
2491 {
2492     return self->parents != NULL;
2493 }
2494
2495 static ObStackingLayer calc_layer(ObClient *self)
2496 {
2497     ObStackingLayer l;
2498     Rect *monitor;
2499
2500     monitor = screen_physical_area_monitor(client_monitor(self));
2501
2502     if (self->type == OB_CLIENT_TYPE_DESKTOP)
2503         l = OB_STACKING_LAYER_DESKTOP;
2504     else if (self->type == OB_CLIENT_TYPE_DOCK) {
2505         if (self->below) l = OB_STACKING_LAYER_NORMAL;
2506         else l = OB_STACKING_LAYER_ABOVE;
2507     }
2508     else if ((self->fullscreen ||
2509               /* No decorations and fills the monitor = oldskool fullscreen.
2510                  But not for maximized windows.
2511               */
2512               (self->decorations == 0 &&
2513                !(self->max_horz && self->max_vert) &&
2514                RECT_EQUAL(self->area, *monitor))) &&
2515              /* you are fullscreen while you or your children are focused.. */
2516              (client_focused(self) || client_search_focus_tree(self) ||
2517               /* you can be fullscreen if you're on another desktop */
2518               (self->desktop != screen_desktop &&
2519                self->desktop != DESKTOP_ALL) ||
2520               /* and you can also be fullscreen if the focused client is on
2521                  another monitor, or nothing else is focused */
2522               (!focus_client ||
2523                client_monitor(focus_client) != client_monitor(self))))
2524         l = OB_STACKING_LAYER_FULLSCREEN;
2525     else if (self->above) l = OB_STACKING_LAYER_ABOVE;
2526     else if (self->below) l = OB_STACKING_LAYER_BELOW;
2527     else l = OB_STACKING_LAYER_NORMAL;
2528
2529     g_free(monitor);
2530
2531     return l;
2532 }
2533
2534 static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
2535                                         ObStackingLayer min)
2536 {
2537     ObStackingLayer old, own;
2538     GSList *it;
2539
2540     old = self->layer;
2541     own = calc_layer(self);
2542     self->layer = MAX(own, min);
2543
2544     if (self->layer != old) {
2545         stacking_remove(CLIENT_AS_WINDOW(self));
2546         stacking_add_nonintrusive(CLIENT_AS_WINDOW(self));
2547     }
2548
2549     /* we've been restacked */
2550     self->visited = TRUE;
2551
2552     for (it = self->transients; it; it = g_slist_next(it))
2553         client_calc_layer_recursive(it->data, orig,
2554                                     self->layer);
2555 }
2556
2557 static void client_calc_layer_internal(ObClient *self)
2558 {
2559     GSList *sit;
2560
2561     /* transients take on the layer of their parents */
2562     sit = client_search_all_top_parents(self);
2563
2564     for (; sit; sit = g_slist_next(sit))
2565         client_calc_layer_recursive(sit->data, self, 0);
2566 }
2567
2568 void client_calc_layer(ObClient *self)
2569 {
2570     GList *it;
2571
2572     /* skip over stuff above fullscreen layer */
2573     for (it = stacking_list; it; it = g_list_next(it))
2574         if (window_layer(it->data) <= OB_STACKING_LAYER_FULLSCREEN) break;
2575
2576     /* find the windows in the fullscreen layer, and mark them not-visited */
2577     for (; it; it = g_list_next(it)) {
2578         if (window_layer(it->data) < OB_STACKING_LAYER_FULLSCREEN) break;
2579         else if (WINDOW_IS_CLIENT(it->data))
2580             WINDOW_AS_CLIENT(it->data)->visited = FALSE;
2581     }
2582
2583     client_calc_layer_internal(self);
2584
2585     /* skip over stuff above fullscreen layer */
2586     for (it = stacking_list; it; it = g_list_next(it))
2587         if (window_layer(it->data) <= OB_STACKING_LAYER_FULLSCREEN) break;
2588
2589     /* now recalc any windows in the fullscreen layer which have not
2590        had their layer recalced already */
2591     for (; it; it = g_list_next(it)) {
2592         if (window_layer(it->data) < OB_STACKING_LAYER_FULLSCREEN) break;
2593         else if (WINDOW_IS_CLIENT(it->data) &&
2594                  !WINDOW_AS_CLIENT(it->data)->visited)
2595             client_calc_layer_internal(it->data);
2596     }
2597 }
2598
2599 gboolean client_should_show(ObClient *self)
2600 {
2601     if (self->iconic)
2602         return FALSE;
2603     if (client_normal(self) && screen_showing_desktop)
2604         return FALSE;
2605     if (self->desktop == screen_desktop || self->desktop == DESKTOP_ALL)
2606         return TRUE;
2607
2608     return FALSE;
2609 }
2610
2611 gboolean client_show(ObClient *self)
2612 {
2613     gboolean show = FALSE;
2614
2615     if (client_should_show(self)) {
2616         /* replay pending pointer event before showing the window, in case it
2617            should be going to something under the window */
2618         mouse_replay_pointer();
2619
2620         client_show_frame(self);
2621         show = TRUE;
2622
2623         /* According to the ICCCM (sec 4.1.3.1) when a window is not visible,
2624            it needs to be in IconicState. This includes when it is on another
2625            desktop!
2626         */
2627         client_change_wm_state(self);
2628     }
2629     return show;
2630 }
2631
2632 gboolean client_hide(ObClient *self)
2633 {
2634     gboolean hide = FALSE;
2635
2636     if (!client_should_show(self)) {
2637         if (self == focus_client) {
2638             /* if there is a grab going on, then we need to cancel it. if we
2639                move focus during the grab, applications will get
2640                NotifyWhileGrabbed events and ignore them !
2641
2642                actions should not rely on being able to move focus during an
2643                interactive grab.
2644             */
2645             event_cancel_all_key_grabs();
2646         }
2647
2648         /* We don't need to ignore enter events here.
2649            The window can hide/iconify in 3 different ways:
2650            1 - through an x message. in this case we ignore all enter events
2651                caused by responding to the x message (unless underMouse)
2652            2 - by a keyboard action. in this case we ignore all enter events
2653                caused by the action
2654            3 - by a mouse action. in this case they are doing stuff with the
2655                mouse and focus _should_ move.
2656
2657            Also in action_end, we simulate an enter event that can't be ignored
2658            so trying to ignore them is futile in case 3 anyways
2659         */
2660
2661         /* replay pending pointer event before hiding the window, in case it
2662            should be going to the window */
2663         mouse_replay_pointer();
2664
2665         client_hide_frame(self);
2666         hide = TRUE;
2667
2668         /* According to the ICCCM (sec 4.1.3.1) when a window is not visible,
2669            it needs to be in IconicState. This includes when it is on another
2670            desktop!
2671         */
2672         client_change_wm_state(self);
2673     }
2674     return hide;
2675 }
2676
2677 void client_showhide(ObClient *self)
2678 {
2679     if (!client_show(self))
2680         client_hide(self);
2681 }
2682
2683 gboolean client_normal(ObClient *self) {
2684     return ! (self->type == OB_CLIENT_TYPE_DESKTOP ||
2685               self->type == OB_CLIENT_TYPE_DOCK ||
2686               self->type == OB_CLIENT_TYPE_SPLASH);
2687 }
2688
2689 gboolean client_helper(ObClient *self)
2690 {
2691     return (self->type == OB_CLIENT_TYPE_UTILITY ||
2692             self->type == OB_CLIENT_TYPE_MENU ||
2693             self->type == OB_CLIENT_TYPE_TOOLBAR);
2694 }
2695
2696 gboolean client_mouse_focusable(ObClient *self)
2697 {
2698     return !(self->type == OB_CLIENT_TYPE_MENU ||
2699              self->type == OB_CLIENT_TYPE_TOOLBAR ||
2700              self->type == OB_CLIENT_TYPE_SPLASH ||
2701              self->type == OB_CLIENT_TYPE_DOCK);
2702 }
2703
2704 gboolean client_enter_focusable(ObClient *self)
2705 {
2706     /* you can focus desktops but it shouldn't on enter */
2707     return (client_mouse_focusable(self) &&
2708             self->type != OB_CLIENT_TYPE_DESKTOP);
2709 }
2710
2711
2712 static void client_apply_startup_state(ObClient *self,
2713                                        gint x, gint y, gint w, gint h)
2714 {
2715     /* save the states that we are going to apply */
2716     gboolean iconic = self->iconic;
2717     gboolean fullscreen = self->fullscreen;
2718     gboolean undecorated = self->undecorated;
2719     gboolean shaded = self->shaded;
2720     gboolean demands_attention = self->demands_attention;
2721     gboolean max_horz = self->max_horz;
2722     gboolean max_vert = self->max_vert;
2723     Rect oldarea;
2724     gint l;
2725
2726     /* turn them all off in the client, so they won't affect the window
2727        being placed */
2728     self->iconic = self->fullscreen = self->undecorated = self->shaded =
2729         self->demands_attention = self->max_horz = self->max_vert = FALSE;
2730
2731     /* move the client to its placed position, or it it's already there,
2732        generate a ConfigureNotify telling the client where it is.
2733
2734        do this after adjusting the frame. otherwise it gets all weird and
2735        clients don't work right
2736
2737        do this before applying the states so they have the correct
2738        pre-max/pre-fullscreen values
2739     */
2740     client_try_configure(self, &x, &y, &w, &h, &l, &l, FALSE);
2741     ob_debug("placed window 0x%x at %d, %d with size %d x %d",
2742              self->w_client, x, y, w, h);
2743     /* save the area, and make it where it should be for the premax stuff */
2744     oldarea = self->area;
2745     RECT_SET(self->area, x, y, w, h);
2746     frame_engine->frame_set_client_area (self->frame, self->area);
2747
2748     /* apply the states. these are in a carefully crafted order.. */
2749
2750     if (iconic)
2751         client_iconify(self, TRUE, FALSE, TRUE);
2752     if (fullscreen)
2753         client_fullscreen(self, TRUE);
2754     if (undecorated)
2755         client_set_undecorated(self, TRUE);
2756     if (shaded)
2757         client_shade(self, TRUE);
2758     if (demands_attention)
2759         client_hilite(self, TRUE);
2760
2761     if (max_vert && max_horz)
2762         client_maximize(self, TRUE, 0);
2763     else if (max_vert)
2764         client_maximize(self, TRUE, 2);
2765     else if (max_horz)
2766         client_maximize(self, TRUE, 1);
2767
2768     /* if the window hasn't been configured yet, then do so now, in fact the
2769        x,y,w,h may _not_ be the same as the area rect, which can end up
2770        meaning that the client isn't properly moved/resized by the fullscreen
2771        function
2772        pho can cause this because it maps at size of the screen but not 0,0
2773        so openbox moves it on screen to 0,0 (thus x,y=0,0 and area.x,y don't).
2774        then fullscreen'ing makes it go to 0,0 which it thinks it already is at
2775        cuz thats where the pre-fullscreen will be. however the actual area is
2776        not, so this needs to be called even if we have fullscreened/maxed
2777     */
2778     self->area = oldarea;
2779     frame_engine->frame_set_decorations (self->frame, self->decorations);
2780     frame_engine->frame_update_layout (self->frame, self->area, FALSE, FALSE);
2781     client_configure(self, x, y, w, h, FALSE, TRUE, FALSE);
2782
2783     /* set the desktop hint, to make sure that it always exists */
2784     OBT_PROP_SET32(self->w_client, NET_WM_DESKTOP, CARDINAL, self->desktop);
2785
2786     /* nothing to do for the other states:
2787        skip_taskbar
2788        skip_pager
2789        modal
2790        above
2791        below
2792     */
2793 }
2794
2795 void client_gravity_resize_w(ObClient *self, gint *x, gint oldw, gint neww)
2796 {
2797     /* these should be the current values. this is for when you're not moving,
2798        just resizing */
2799     g_assert(*x == self->area.x);
2800     g_assert(oldw == self->area.width);
2801
2802     /* horizontal */
2803     switch (self->gravity) {
2804     default:
2805     case NorthWestGravity:
2806     case WestGravity:
2807     case SouthWestGravity:
2808     case StaticGravity:
2809     case ForgetGravity:
2810         break;
2811     case NorthGravity:
2812     case CenterGravity:
2813     case SouthGravity:
2814         *x -= (neww - oldw) / 2;
2815         break;
2816     case NorthEastGravity:
2817     case EastGravity:
2818     case SouthEastGravity:
2819         *x -= neww - oldw;
2820         break;
2821     }
2822 }
2823
2824 void client_gravity_resize_h(ObClient *self, gint *y, gint oldh, gint newh)
2825 {
2826     /* these should be the current values. this is for when you're not moving,
2827        just resizing */
2828     g_assert(*y == self->area.y);
2829     g_assert(oldh == self->area.height);
2830
2831     /* vertical */
2832     switch (self->gravity) {
2833     default:
2834     case NorthWestGravity:
2835     case NorthGravity:
2836     case NorthEastGravity:
2837     case StaticGravity:
2838     case ForgetGravity:
2839         break;
2840     case WestGravity:
2841     case CenterGravity:
2842     case EastGravity:
2843         *y -= (newh - oldh) / 2;
2844         break;
2845     case SouthWestGravity:
2846     case SouthGravity:
2847     case SouthEastGravity:
2848         *y -= newh - oldh;
2849         break;
2850     }
2851 }
2852
2853 void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
2854                           gint *logicalw, gint *logicalh,
2855                           gboolean user)
2856 {
2857     Rect desired = {*x, *y, *w, *h};
2858     frame_rect_to_frame(self, &desired);
2859
2860     /* make the frame recalculate its dimentions n shit without changing
2861        anything visible for real, this way the constraints below can work with
2862        the updated frame dimensions. */
2863     frame_engine->frame_set_decorations (self->frame, self->decorations);
2864     frame_engine->frame_update_layout (self->frame, self->area, FALSE, TRUE);
2865
2866     /* gets the frame's position */
2867     frame_client_gravity(self, x, y);
2868
2869     /* these positions are frame positions, not client positions */
2870
2871     /* set the size and position if fullscreen */
2872     if (self->fullscreen) {
2873         Rect *a;
2874         guint i;
2875
2876         i = screen_find_monitor(&desired);
2877         a = screen_physical_area_monitor(i);
2878
2879         *x = a->x;
2880         *y = a->y;
2881         *w = a->width;
2882         *h = a->height;
2883
2884         user = FALSE; /* ignore if the client can't be moved/resized when it
2885                          is fullscreening */
2886
2887         g_free(a);
2888     } else if (self->max_horz || self->max_vert) {
2889         Rect *a;
2890         guint i;
2891
2892         /* use all possible struts when maximizing to the full screen */
2893         i = screen_find_monitor(&desired);
2894         a = screen_area(self->desktop, i,
2895                         (self->max_horz && self->max_vert ? NULL : &desired));
2896
2897         Strut size;
2898         frame_engine->frame_get_size(self->frame, &size);
2899         /* set the size and position if maximized */
2900         if (self->max_horz) {
2901             *x = a->x;
2902             *w = a->width - size.left - size.right;
2903         }
2904         if (self->max_vert) {
2905             *y = a->y;
2906             *h = a->height - size.top - size.bottom;
2907         }
2908
2909         user = FALSE; /* ignore if the client can't be moved/resized when it
2910                          is maximizing */
2911
2912         g_free(a);
2913     }
2914
2915     /* gets the client's position */
2916     frame_frame_gravity(self, x, y);
2917
2918     /* work within the prefered sizes given by the window */
2919     if (!(*w == self->area.width && *h == self->area.height)) {
2920         gint basew, baseh, minw, minh;
2921         gint incw, inch;
2922         gfloat minratio, maxratio;
2923
2924         incw = self->fullscreen || self->max_horz ? 1 : self->size_inc.width;
2925         inch = self->fullscreen || self->max_vert ? 1 : self->size_inc.height;
2926         minratio = self->fullscreen || (self->max_horz && self->max_vert) ?
2927             0 : self->min_ratio;
2928         maxratio = self->fullscreen || (self->max_horz && self->max_vert) ?
2929             0 : self->max_ratio;
2930
2931         /* base size is substituted with min size if not specified */
2932         if (self->base_size.width || self->base_size.height) {
2933             basew = self->base_size.width;
2934             baseh = self->base_size.height;
2935         } else {
2936             basew = self->min_size.width;
2937             baseh = self->min_size.height;
2938         }
2939         /* min size is substituted with base size if not specified */
2940         if (self->min_size.width || self->min_size.height) {
2941             minw = self->min_size.width;
2942             minh = self->min_size.height;
2943         } else {
2944             minw = self->base_size.width;
2945             minh = self->base_size.height;
2946         }
2947
2948         /* if this is a user-requested resize, then check against min/max
2949            sizes */
2950
2951         /* smaller than min size or bigger than max size? */
2952         if (*w > self->max_size.width) *w = self->max_size.width;
2953         if (*w < minw) *w = minw;
2954         if (*h > self->max_size.height) *h = self->max_size.height;
2955         if (*h < minh) *h = minh;
2956
2957         *w -= basew;
2958         *h -= baseh;
2959
2960         /* keep to the increments */
2961         *w /= incw;
2962         *h /= inch;
2963
2964         /* you cannot resize to nothing */
2965         if (basew + *w < 1) *w = 1 - basew;
2966         if (baseh + *h < 1) *h = 1 - baseh;
2967
2968         /* save the logical size */
2969         *logicalw = incw > 1 ? *w : *w + basew;
2970         *logicalh = inch > 1 ? *h : *h + baseh;
2971
2972         *w *= incw;
2973         *h *= inch;
2974
2975         *w += basew;
2976         *h += baseh;
2977
2978         /* adjust the height to match the width for the aspect ratios.
2979            for this, min size is not substituted for base size ever. */
2980         *w -= self->base_size.width;
2981         *h -= self->base_size.height;
2982
2983         if (minratio)
2984             if (*h * minratio > *w) {
2985                 *h = (gint)(*w / minratio);
2986
2987                 /* you cannot resize to nothing */
2988                 if (*h < 1) {
2989                     *h = 1;
2990                     *w = (gint)(*h * minratio);
2991                 }
2992             }
2993         if (maxratio)
2994             if (*h * maxratio < *w) {
2995                 *h = (gint)(*w / maxratio);
2996
2997                 /* you cannot resize to nothing */
2998                 if (*h < 1) {
2999                     *h = 1;
3000                     *w = (gint)(*h * minratio);
3001                 }
3002             }
3003
3004         *w += self->base_size.width;
3005         *h += self->base_size.height;
3006     }
3007
3008     /* these override the above states! if you cant move you can't move! */
3009     if (user) {
3010         if (!(self->functions & OB_CLIENT_FUNC_MOVE)) {
3011             *x = self->area.x;
3012             *y = self->area.y;
3013         }
3014         if (!(self->functions & OB_CLIENT_FUNC_RESIZE)) {
3015             *w = self->area.width;
3016             *h = self->area.height;
3017         }
3018     }
3019
3020     g_assert(*w > 0);
3021     g_assert(*h > 0);
3022 }
3023
3024
3025 void client_configure(ObClient *self, gint x, gint y, gint w, gint h,
3026                       gboolean user, gboolean final, gboolean force_reply)
3027 {
3028     Rect oldframe;
3029     gint oldw, oldh;
3030     gboolean send_resize_client;
3031     gboolean moved = FALSE, resized = FALSE, rootmoved = FALSE;
3032     gboolean fmoved, fresized;
3033     guint fdecor = frame_engine->frame_get_decorations(self->frame);
3034     gboolean fhorz = frame_engine->frame_is_max_horz(self->frame);
3035     gboolean fvert = frame_engine->frame_is_max_vert(self->frame);
3036     gint logicalw, logicalh;
3037
3038     Strut size;
3039     frame_engine->frame_get_size(self->frame, &size);
3040     Rect area;
3041     frame_engine->frame_get_window_area(self->frame, &area);
3042
3043     /* find the new x, y, width, and height (and logical size) */
3044     client_try_configure(self, &x, &y, &w, &h, &logicalw, &logicalh, user);
3045
3046     /* set the logical size if things changed */
3047     if (!(w == self->area.width && h == self->area.height))
3048         SIZE_SET(self->logical_size, logicalw, logicalh);
3049
3050     /* figure out if we moved or resized or what */
3051     moved = (x != self->area.x || y != self->area.y);
3052     resized = (w != self->area.width || h != self->area.height);
3053
3054     oldw = self->area.width;
3055     oldh = self->area.height;
3056     frame_engine->frame_get_window_area(self->frame, &oldframe);
3057     RECT_SET(self->area, x, y, w, h);
3058
3059     frame_engine->frame_set_client_area (self->frame, self->area);
3060
3061     /* for app-requested resizes, always resize if 'resized' is true.
3062        for user-requested ones, only resize if final is true, or when
3063        resizing in redraw mode */
3064     send_resize_client = ((!user && resized) ||
3065                           (user && (final ||
3066                                     (resized && config_resize_redraw))));
3067
3068     /* if the client is enlarging, then resize the client before the frame */
3069     if (send_resize_client && (w > oldw || h > oldh)) {
3070         frame_engine->frame_set_decorations (self->frame, self->decorations);
3071         frame_engine->frame_update_layout (self->frame, self->area, FALSE, FALSE);
3072     }
3073
3074     /* find the frame's dimensions and move/resize it */
3075     fmoved = moved;
3076     fresized = resized;
3077
3078     /* if decorations changed, then readjust everything for the frame */
3079     if (self->decorations != fdecor ||
3080         self->max_horz != fhorz || self->max_vert != fvert)
3081     {
3082         fmoved = fresized = TRUE;
3083     }
3084
3085     /* adjust the frame */
3086     if (fmoved || fresized) {
3087         gulong ignore_start;
3088         if (!user)
3089             ignore_start = event_start_ignore_all_enters();
3090
3091         /* replay pending pointer event before move the window, in case it
3092            would change what window gets the event */
3093         mouse_replay_pointer();
3094
3095         frame_engine->frame_set_decorations (self->frame, self->decorations);
3096         frame_engine->frame_update_layout (self->frame, self->area, TRUE, FALSE);
3097
3098         if (!user)
3099             event_end_ignore_all_enters(ignore_start);
3100     }
3101
3102     if (!user || final) {
3103         gint oldrx = self->root_pos.x;
3104         gint oldry = self->root_pos.y;
3105         /* we have reset the client to 0 border width, so don't include
3106            it in these coords */
3107         POINT_SET(self->root_pos,
3108                   area.x + size.left -
3109                   self->border_width,
3110                   area.y + size.top -
3111                   self->border_width);
3112         if (self->root_pos.x != oldrx || self->root_pos.y != oldry)
3113             rootmoved = TRUE;
3114     }
3115
3116     /* This is kinda tricky and should not be changed.. let me explain!
3117
3118        When user = FALSE, then the request is coming from the application
3119        itself, and we are more strict about when to send a synthetic
3120        ConfigureNotify.  We strictly follow the rules of the ICCCM sec 4.1.5
3121        in this case (if force_reply is true)
3122
3123        When user = TRUE, then the request is coming from "us", like when we
3124        maximize a window or something.  In this case we are more lenient.  We
3125        used to follow the same rules as above, but _Java_ Swing can't handle
3126        this. So just to appease Swing, when user = TRUE, we always send
3127        a synthetic ConfigureNotify to give the window its root coordinates.
3128     */
3129     if ((!user && !resized && (rootmoved || force_reply)) ||
3130         (user && final && rootmoved))
3131     {
3132         XEvent event;
3133
3134         event.type = ConfigureNotify;
3135         event.xconfigure.display = obt_display;
3136         event.xconfigure.event = self->w_client;
3137         event.xconfigure.window = self->w_client;
3138
3139         ob_debug("Sending ConfigureNotify to %s for %d,%d %dx%d",
3140                  self->title, self->root_pos.x, self->root_pos.y, w, h);
3141
3142         /* root window real coords */
3143         event.xconfigure.x = self->root_pos.x;
3144         event.xconfigure.y = self->root_pos.y;
3145         event.xconfigure.width = w;
3146         event.xconfigure.height = h;
3147         event.xconfigure.border_width = self->border_width;
3148         event.xconfigure.above = None;
3149         event.xconfigure.override_redirect = FALSE;
3150         XSendEvent(event.xconfigure.display, event.xconfigure.window,
3151                    FALSE, StructureNotifyMask, &event);
3152     }
3153
3154     /* if the client is shrinking, then resize the frame before the client.
3155
3156        both of these resize sections may run, because the top one only resizes
3157        in the direction that is growing
3158      */
3159     if (send_resize_client && (w <= oldw || h <= oldh)) {
3160       frame_engine->frame_set_decorations (self->frame, self->decorations);
3161       frame_engine->frame_update_layout (self->frame, self->area, FALSE, FALSE);
3162     }
3163
3164     XFlush(obt_display);
3165
3166     /* if it moved between monitors, then this can affect the stacking
3167        layer of this window or others - for fullscreen windows */
3168     Rect current_frame;
3169     frame_engine->frame_get_window_area(self->frame, &current_frame);
3170     if (screen_find_monitor(&current_frame) !=
3171         screen_find_monitor(&oldframe))
3172     {
3173         client_calc_layer(self);
3174     }
3175 }
3176
3177 void client_fullscreen(ObClient *self, gboolean fs)
3178 {
3179     gint x, y, w, h;
3180
3181     if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) || /* can't */
3182         self->fullscreen == fs) return;                   /* already done */
3183
3184     self->fullscreen = fs;
3185     client_change_state(self); /* change the state hints on the client */
3186
3187     if (fs) {
3188         self->pre_fullscreen_area = self->area;
3189         /* if the window is maximized, its area isn't all that meaningful.
3190            save it's premax area instead. */
3191         if (self->max_horz) {
3192             self->pre_fullscreen_area.x = self->pre_max_area.x;
3193             self->pre_fullscreen_area.width = self->pre_max_area.width;
3194         }
3195         if (self->max_vert) {
3196             self->pre_fullscreen_area.y = self->pre_max_area.y;
3197             self->pre_fullscreen_area.height = self->pre_max_area.height;
3198         }
3199
3200         /* these will help configure_full figure out where to fullscreen
3201            the window */
3202         x = self->area.x;
3203         y = self->area.y;
3204         w = self->area.width;
3205         h = self->area.height;
3206     } else {
3207         g_assert(self->pre_fullscreen_area.width > 0 &&
3208                  self->pre_fullscreen_area.height > 0);
3209
3210         x = self->pre_fullscreen_area.x;
3211         y = self->pre_fullscreen_area.y;
3212         w = self->pre_fullscreen_area.width;
3213         h = self->pre_fullscreen_area.height;
3214         RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0);
3215     }
3216
3217     ob_debug("Window %s going fullscreen (%d)",
3218              self->title, self->fullscreen);
3219
3220     client_setup_decor_and_functions(self, FALSE);
3221     client_move_resize(self, x, y, w, h);
3222
3223     /* and adjust our layer/stacking. do this after resizing the window,
3224        and applying decorations, because windows which fill the screen are
3225        considered "fullscreen" and it affects their layer */
3226     client_calc_layer(self);
3227
3228     if (fs) {
3229         /* try focus us when we go into fullscreen mode */
3230         client_focus(self);
3231     }
3232 }
3233
3234 static void client_iconify_recursive(ObClient *self,
3235                                      gboolean iconic, gboolean curdesk,
3236                                      gboolean hide_animation)
3237 {
3238     GSList *it;
3239     gboolean changed = FALSE;
3240
3241
3242     if (self->iconic != iconic) {
3243         ob_debug("%sconifying window: 0x%lx", (iconic ? "I" : "Uni"),
3244                  self->w_client);
3245
3246         if (iconic) {
3247             /* don't let non-normal windows iconify along with their parents
3248                or whatever */
3249             if (client_normal(self)) {
3250                 self->iconic = iconic;
3251
3252                 /* update the focus lists.. iconic windows go to the bottom of
3253                    the list */
3254                 focus_order_to_bottom(self);
3255
3256                 changed = TRUE;
3257             }
3258         } else {
3259             self->iconic = iconic;
3260
3261             if (curdesk && self->desktop != screen_desktop &&
3262                 self->desktop != DESKTOP_ALL)
3263                 client_set_desktop(self, screen_desktop, FALSE, FALSE);
3264
3265             /* this puts it after the current focused window */
3266             focus_order_remove(self);
3267             focus_order_add_new(self);
3268
3269             changed = TRUE;
3270         }
3271     }
3272
3273     if (changed) {
3274         client_change_state(self);
3275         /* do this after starting the animation so it doesn't flash */
3276         client_showhide(self);
3277     }
3278
3279     /* iconify all direct transients, and deiconify all transients
3280        (non-direct too) */
3281     for (it = self->transients; it; it = g_slist_next(it))
3282         if (it->data != self)
3283             if (client_is_direct_child(self, it->data) || !iconic)
3284                 client_iconify_recursive(it->data, iconic, curdesk,
3285                                          hide_animation);
3286 }
3287
3288 void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk,
3289                     gboolean hide_animation)
3290 {
3291     if (self->functions & OB_CLIENT_FUNC_ICONIFY || !iconic) {
3292         /* move up the transient chain as far as possible first */
3293         self = client_search_top_direct_parent(self);
3294         client_iconify_recursive(self, iconic, curdesk, hide_animation);
3295     }
3296 }
3297
3298 void client_maximize(ObClient *self, gboolean max, gint dir)
3299 {
3300     gint x, y, w, h;
3301
3302     g_assert(dir == 0 || dir == 1 || dir == 2);
3303     if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE)) return; /* can't */
3304
3305     /* check if already done */
3306     if (max) {
3307         if (dir == 0 && self->max_horz && self->max_vert) return;
3308         if (dir == 1 && self->max_horz) return;
3309         if (dir == 2 && self->max_vert) return;
3310     } else {
3311         if (dir == 0 && !self->max_horz && !self->max_vert) return;
3312         if (dir == 1 && !self->max_horz) return;
3313         if (dir == 2 && !self->max_vert) return;
3314     }
3315
3316     /* these will help configure_full figure out which screen to fill with
3317        the window */
3318     x = self->area.x;
3319     y = self->area.y;
3320     w = self->area.width;
3321     h = self->area.height;
3322
3323     if (max) {
3324         if ((dir == 0 || dir == 1) && !self->max_horz) { /* horz */
3325             RECT_SET(self->pre_max_area,
3326                      self->area.x, self->pre_max_area.y,
3327                      self->area.width, self->pre_max_area.height);
3328         }
3329         if ((dir == 0 || dir == 2) && !self->max_vert) { /* vert */
3330             RECT_SET(self->pre_max_area,
3331                      self->pre_max_area.x, self->area.y,
3332                      self->pre_max_area.width, self->area.height);
3333         }
3334     } else {
3335         if ((dir == 0 || dir == 1) && self->max_horz) { /* horz */
3336             g_assert(self->pre_max_area.width > 0);
3337
3338             x = self->pre_max_area.x;
3339             w = self->pre_max_area.width;
3340
3341             RECT_SET(self->pre_max_area, 0, self->pre_max_area.y,
3342                      0, self->pre_max_area.height);
3343         }
3344         if ((dir == 0 || dir == 2) && self->max_vert) { /* vert */
3345             g_assert(self->pre_max_area.height > 0);
3346
3347             y = self->pre_max_area.y;
3348             h = self->pre_max_area.height;
3349
3350             RECT_SET(self->pre_max_area, self->pre_max_area.x, 0,
3351                      self->pre_max_area.width, 0);
3352         }
3353     }
3354
3355     if (dir == 0 || dir == 1) { 
3356         self->max_horz = max; /* horz */
3357         frame_engine->frame_trigger(self->frame, max? OB_TRIGGER_MAX_HORZ : OB_TRIGGER_UNMAX_HORZ);
3358     }
3359
3360     if (dir == 0 || dir == 2){
3361         self->max_vert = max; /* vert */
3362         frame_engine->frame_trigger(self->frame, max? OB_TRIGGER_MAX_VERT : OB_TRIGGER_UNMAX_VERT);
3363     }
3364
3365     client_change_state(self); /* change the state hints on the client */
3366
3367     client_setup_decor_and_functions(self, FALSE);
3368     client_move_resize(self, x, y, w, h);
3369 }
3370
3371 void client_shade(ObClient *self, gboolean shade)
3372 {
3373     if ((!(self->functions & OB_CLIENT_FUNC_SHADE) &&
3374          shade) ||                         /* can't shade */
3375         self->shaded == shade) return;     /* already done */
3376
3377     self->shaded = shade;
3378     client_change_state(self);
3379     client_change_wm_state(self); /* the window is being hidden/shown */
3380     /* resize the frame to just the titlebar */
3381     frame_engine->frame_set_is_shaded (self->frame, self->shaded);
3382     frame_engine->frame_update_layout(self->frame, self->area, FALSE, FALSE);
3383 }
3384
3385 static void client_ping_event(ObClient *self, gboolean dead)
3386 {
3387     self->not_responding = dead;
3388     client_update_title(self);
3389
3390     if (!dead) {
3391         /* it came back to life ! */
3392
3393         if (self->kill_prompt) {
3394             prompt_unref(self->kill_prompt);
3395             self->kill_prompt = NULL;
3396         }
3397
3398         self->kill_level = 0;
3399     }
3400 }
3401
3402 void client_close(ObClient *self)
3403 {
3404     if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return;
3405
3406     if (self->prompt) {
3407         prompt_cancel(self->prompt);
3408         return;
3409     }
3410
3411     /* in the case that the client provides no means to requesting that it
3412        close, we just kill it */
3413     if (!self->delete_window)
3414         /* don't use client_kill(), we should only kill based on PID in
3415            response to a lack of PING replies */
3416         XKillClient(obt_display, self->w_client);
3417     else {
3418         /* request the client to close with WM_DELETE_WINDOW */
3419         OBT_PROP_MSG_TO(self->w_client, self->w_client, WM_PROTOCOLS,
3420                         OBT_PROP_ATOM(WM_DELETE_WINDOW), event_curtime,
3421                         0, 0, 0, NoEventMask);
3422
3423         if (self->not_responding)
3424             client_prompt_kill(self);
3425     }
3426 }
3427
3428 #define OB_KILL_RESULT_NO 0
3429 #define OB_KILL_RESULT_YES 1
3430
3431 static void client_kill_requested(ObPrompt *p, gint result, gpointer data)
3432 {
3433     ObClient *self = data;
3434
3435     if (result == OB_KILL_RESULT_YES)
3436         client_kill(self);
3437
3438     prompt_unref(self->kill_prompt);
3439     self->kill_prompt = NULL;
3440 }
3441
3442 static void client_prompt_kill(ObClient *self)
3443 {
3444     /* check if we're already prompting */
3445     if (!self->kill_prompt) {
3446         ObPromptAnswer answers[] = {
3447             { _("No"), OB_KILL_RESULT_NO },
3448             { _("Yes"), OB_KILL_RESULT_YES }
3449         };
3450         gchar *m;
3451         const gchar *sig;
3452
3453         if (self->kill_level == 0)
3454             sig = "terminate";
3455         else
3456             sig = "kill";
3457
3458         m = g_strdup_printf
3459             (_("The window \"%s\" does not seem to be responding.  Do you want to force it to exit by sending the %s signal?"), self->original_title, sig);
3460
3461         self->kill_prompt = prompt_new(m, answers,
3462                                        sizeof(answers)/sizeof(answers[0]),
3463                                        OB_KILL_RESULT_NO, /* default = no */
3464                                        OB_KILL_RESULT_NO, /* cancel = no */
3465                                        client_kill_requested, self);
3466         g_free(m);
3467     }
3468
3469     prompt_show(self->kill_prompt, self);
3470 }
3471
3472 void client_kill(ObClient *self)
3473 {
3474     /* don't kill our own windows */
3475     if (self->prompt) return;
3476
3477     if (!self->client_machine && self->pid) {
3478         /* running on the local host */
3479         if (self->kill_level == 0) {
3480             ob_debug("killing window 0x%x with pid %lu, with SIGTERM",
3481                      self->w_client, self->pid);
3482             kill(self->pid, SIGTERM);
3483             ++self->kill_level;
3484
3485             /* show that we're trying to kill it */
3486             client_update_title(self);
3487         }
3488         else {
3489             ob_debug("killing window 0x%x with pid %lu, with SIGKILL",
3490                      self->w_client, self->pid);
3491             kill(self->pid, SIGKILL); /* kill -9 */
3492         }
3493     }
3494     else {
3495         /* running on a remote host */
3496         XKillClient(obt_display, self->w_client);
3497     }
3498 }
3499
3500 void client_hilite(ObClient *self, gboolean hilite)
3501 {
3502     if (self->demands_attention == hilite)
3503         return; /* no change */
3504
3505     /* don't allow focused windows to hilite */
3506     self->demands_attention = hilite && !client_focused(self);
3507     if (self->frame != NULL) { /* if we're mapping, just set the state */
3508         if (self->demands_attention)
3509         frame_engine->frame_flash_start(self->frame);
3510         else
3511         frame_engine->frame_flash_stop(self->frame);
3512         client_change_state(self);
3513     }
3514 }
3515
3516 static void client_set_desktop_recursive(ObClient *self,
3517                                          guint target,
3518                                          gboolean donthide,
3519                                          gboolean dontraise)
3520 {
3521     guint old;
3522     GSList *it;
3523
3524     if (target != self->desktop && self->type != OB_CLIENT_TYPE_DESKTOP) {
3525
3526         ob_debug("Setting desktop %u", target+1);
3527
3528         g_assert(target < screen_num_desktops || target == DESKTOP_ALL);
3529
3530         old = self->desktop;
3531         self->desktop = target;
3532         OBT_PROP_SET32(self->w_client, NET_WM_DESKTOP, CARDINAL, target);
3533         /* the frame can display the current desktop state */
3534         frame_engine->frame_set_decorations (self->frame, self->decorations);
3535         frame_engine->frame_update_layout(self->frame, self->area, FALSE, FALSE);
3536         /* 'move' the window to the new desktop */
3537         if (!donthide)
3538             client_hide(self);
3539         client_show(self);
3540         /* raise if it was not already on the desktop */
3541         if (old != DESKTOP_ALL && !dontraise)
3542             stacking_raise(CLIENT_AS_WINDOW(self));
3543         if (STRUT_EXISTS(self->strut))
3544             screen_update_areas();
3545         else
3546             /* the new desktop's geometry may be different, so we may need to
3547                resize, for example if we are maximized */
3548             client_reconfigure(self, FALSE);
3549     }
3550
3551     /* move all transients */
3552     for (it = self->transients; it; it = g_slist_next(it))
3553         if (it->data != self)
3554             if (client_is_direct_child(self, it->data))
3555                 client_set_desktop_recursive(it->data, target,
3556                                              donthide, dontraise);
3557 }
3558
3559 void client_set_desktop(ObClient *self, guint target,
3560                         gboolean donthide, gboolean dontraise)
3561 {
3562     self = client_search_top_direct_parent(self);
3563     client_set_desktop_recursive(self, target, donthide, dontraise);
3564 }
3565
3566 gboolean client_is_direct_child(ObClient *parent, ObClient *child)
3567 {
3568     while (child != parent && (child = client_direct_parent(child)));
3569     return child == parent;
3570 }
3571
3572 ObClient *client_search_modal_child(ObClient *self)
3573 {
3574     GSList *it;
3575     ObClient *ret;
3576
3577     for (it = self->transients; it; it = g_slist_next(it)) {
3578         ObClient *c = it->data;
3579         if ((ret = client_search_modal_child(c))) return ret;
3580         if (c->modal) return c;
3581     }
3582     return NULL;
3583 }
3584
3585 gboolean client_validate(ObClient *self)
3586 {
3587     XEvent e;
3588
3589     XSync(obt_display, FALSE); /* get all events on the server */
3590
3591     if (XCheckTypedWindowEvent(obt_display, self->w_client, DestroyNotify, &e) ||
3592         XCheckTypedWindowEvent(obt_display, self->w_client, UnmapNotify, &e))
3593     {
3594         XPutBackEvent(obt_display, &e);
3595         return FALSE;
3596     }
3597
3598     return TRUE;
3599 }
3600
3601 void client_set_wm_state(ObClient *self, glong state)
3602 {
3603     if (state == self->wmstate) return; /* no change */
3604
3605     switch (state) {
3606     case IconicState:
3607         client_iconify(self, TRUE, TRUE, FALSE);
3608         break;
3609     case NormalState:
3610         client_iconify(self, FALSE, TRUE, FALSE);
3611         break;
3612     }
3613 }
3614
3615 void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
3616 {
3617     gboolean shaded = self->shaded;
3618     gboolean fullscreen = self->fullscreen;
3619     gboolean undecorated = self->undecorated;
3620     gboolean max_horz = self->max_horz;
3621     gboolean max_vert = self->max_vert;
3622     gboolean modal = self->modal;
3623     gboolean iconic = self->iconic;
3624     gboolean demands_attention = self->demands_attention;
3625     gboolean above = self->above;
3626     gboolean below = self->below;
3627     gint i;
3628
3629     if (!(action == OBT_PROP_ATOM(NET_WM_STATE_ADD) ||
3630           action == OBT_PROP_ATOM(NET_WM_STATE_REMOVE) ||
3631           action == OBT_PROP_ATOM(NET_WM_STATE_TOGGLE)))
3632         /* an invalid action was passed to the client message, ignore it */
3633         return;
3634
3635     for (i = 0; i < 2; ++i) {
3636         Atom state = i == 0 ? data1 : data2;
3637
3638         if (!state) continue;
3639
3640         /* if toggling, then pick whether we're adding or removing */
3641         if (action == OBT_PROP_ATOM(NET_WM_STATE_TOGGLE)) {
3642             if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL))
3643                 action = modal ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3644                     OBT_PROP_ATOM(NET_WM_STATE_ADD);
3645             else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT))
3646                 action = self->max_vert ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3647                     OBT_PROP_ATOM(NET_WM_STATE_ADD);
3648             else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ))
3649                 action = self->max_horz ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3650                     OBT_PROP_ATOM(NET_WM_STATE_ADD);
3651             else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED))
3652                 action = shaded ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3653                     OBT_PROP_ATOM(NET_WM_STATE_ADD);
3654             else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR))
3655                 action = self->skip_taskbar ?
3656                     OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3657                     OBT_PROP_ATOM(NET_WM_STATE_ADD);
3658             else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER))
3659                 action = self->skip_pager ?
3660                     OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3661                     OBT_PROP_ATOM(NET_WM_STATE_ADD);
3662             else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN))
3663                 action = self->iconic ?
3664                     OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3665                     OBT_PROP_ATOM(NET_WM_STATE_ADD);
3666             else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN))
3667                 action = fullscreen ?
3668                     OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3669                     OBT_PROP_ATOM(NET_WM_STATE_ADD);
3670             else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE))
3671                 action = self->above ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3672                     OBT_PROP_ATOM(NET_WM_STATE_ADD);
3673             else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW))
3674                 action = self->below ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3675                     OBT_PROP_ATOM(NET_WM_STATE_ADD);
3676             else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION))
3677                 action = self->demands_attention ?
3678                     OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3679                     OBT_PROP_ATOM(NET_WM_STATE_ADD);
3680             else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED))
3681                 action = undecorated ? OBT_PROP_ATOM(NET_WM_STATE_REMOVE) :
3682                     OBT_PROP_ATOM(NET_WM_STATE_ADD);
3683         }
3684
3685         if (action == OBT_PROP_ATOM(NET_WM_STATE_ADD)) {
3686             if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL)) {
3687                 modal = TRUE;
3688             } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT)) {
3689                 max_vert = TRUE;
3690             } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ)) {
3691                 max_horz = TRUE;
3692             } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED)) {
3693                 shaded = TRUE;
3694             } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR)) {
3695                 self->skip_taskbar = TRUE;
3696             } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER)) {
3697                 self->skip_pager = TRUE;
3698             } else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN)) {
3699                 iconic = TRUE;
3700             } else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN)) {
3701                 fullscreen = TRUE;
3702             } else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE)) {
3703                 above = TRUE;
3704                 below = FALSE;
3705             } else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW)) {
3706                 above = FALSE;
3707                 below = TRUE;
3708             } else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION)){
3709                 demands_attention = TRUE;
3710             } else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED)) {
3711                 undecorated = TRUE;
3712             }
3713
3714         } else { /* action == OBT_PROP_ATOM(NET_WM_STATE_REMOVE) */
3715             if (state == OBT_PROP_ATOM(NET_WM_STATE_MODAL)) {
3716                 modal = FALSE;
3717             } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_VERT)) {
3718                 max_vert = FALSE;
3719             } else if (state == OBT_PROP_ATOM(NET_WM_STATE_MAXIMIZED_HORZ)) {
3720                 max_horz = FALSE;
3721             } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SHADED)) {
3722                 shaded = FALSE;
3723             } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_TASKBAR)) {
3724                 self->skip_taskbar = FALSE;
3725             } else if (state == OBT_PROP_ATOM(NET_WM_STATE_SKIP_PAGER)) {
3726                 self->skip_pager = FALSE;
3727             } else if (state == OBT_PROP_ATOM(NET_WM_STATE_HIDDEN)) {
3728                 iconic = FALSE;
3729             } else if (state == OBT_PROP_ATOM(NET_WM_STATE_FULLSCREEN)) {
3730                 fullscreen = FALSE;
3731             } else if (state == OBT_PROP_ATOM(NET_WM_STATE_ABOVE)) {
3732                 above = FALSE;
3733             } else if (state == OBT_PROP_ATOM(NET_WM_STATE_BELOW)) {
3734                 below = FALSE;
3735             } else if (state == OBT_PROP_ATOM(NET_WM_STATE_DEMANDS_ATTENTION)){
3736                 demands_attention = FALSE;
3737             } else if (state == OBT_PROP_ATOM(OB_WM_STATE_UNDECORATED)) {
3738                 undecorated = FALSE;
3739             }
3740         }
3741     }
3742
3743     if (max_horz != self->max_horz || max_vert != self->max_vert) {
3744         if (max_horz != self->max_horz && max_vert != self->max_vert) {
3745             /* toggling both */
3746             if (max_horz == max_vert) { /* both going the same way */
3747                 client_maximize(self, max_horz, 0);
3748             } else {
3749                 client_maximize(self, max_horz, 1);
3750                 client_maximize(self, max_vert, 2);
3751             }
3752         } else {
3753             /* toggling one */
3754             if (max_horz != self->max_horz)
3755                 client_maximize(self, max_horz, 1);
3756             else
3757                 client_maximize(self, max_vert, 2);
3758         }
3759     }
3760     /* change fullscreen state before shading, as it will affect if the window
3761        can shade or not */
3762     if (fullscreen != self->fullscreen)
3763         client_fullscreen(self, fullscreen);
3764     if (shaded != self->shaded)
3765         client_shade(self, shaded);
3766     if (undecorated != self->undecorated)
3767         client_set_undecorated(self, undecorated);
3768     if (above != self->above || below != self->below) {
3769         self->above = above;
3770         self->below = below;
3771         client_calc_layer(self);
3772     }
3773
3774     if (modal != self->modal) {
3775         self->modal = modal;
3776         /* when a window changes modality, then its stacking order with its
3777            transients needs to change */
3778         stacking_raise(CLIENT_AS_WINDOW(self));
3779
3780         /* it also may get focused. if something is focused that shouldn't
3781            be focused anymore, then move the focus */
3782         if (focus_client && client_focus_target(focus_client) != focus_client)
3783             client_focus(focus_client);
3784     }
3785
3786     if (iconic != self->iconic)
3787         client_iconify(self, iconic, FALSE, FALSE);
3788
3789     if (demands_attention != self->demands_attention)
3790         client_hilite(self, demands_attention);
3791
3792     client_change_state(self); /* change the hint to reflect these changes */
3793 }
3794
3795 ObClient *client_focus_target(ObClient *self)
3796 {
3797     ObClient *child = NULL;
3798
3799     child = client_search_modal_child(self);
3800     if (child) return child;
3801     return self;
3802 }
3803
3804 gboolean client_can_focus(ObClient *self)
3805 {
3806     /* choose the correct target */
3807     self = client_focus_target(self);
3808
3809     if (!frame_engine->frame_is_visible(self->frame))
3810         return FALSE;
3811
3812     if (!(self->can_focus || self->focus_notify))
3813         return FALSE;
3814
3815     return TRUE;
3816 }
3817
3818 gboolean client_focus(ObClient *self)
3819 {
3820     /* we might not focus this window, so if we have modal children which would
3821        be focused instead, bring them to this desktop */
3822     client_bring_modal_windows(self);
3823
3824     /* choose the correct target */
3825     self = client_focus_target(self);
3826
3827     if (!client_can_focus(self)) {
3828         ob_debug_type(OB_DEBUG_FOCUS,
3829                       "Client %s can't be focused", self->title);
3830         return FALSE;
3831     }
3832
3833     ob_debug_type(OB_DEBUG_FOCUS,
3834                   "Focusing client \"%s\" (0x%x) at time %u",
3835                   self->title, self->w_client, event_curtime);
3836
3837     /* if using focus_delay, stop the timer now so that focus doesn't
3838        go moving on us */
3839     event_halt_focus_delay();
3840
3841     /* if there is a grab going on, then we need to cancel it. if we move
3842        focus during the grab, applications will get NotifyWhileGrabbed events
3843        and ignore them !
3844
3845        actions should not rely on being able to move focus during an
3846        interactive grab.
3847     */
3848     event_cancel_all_key_grabs();
3849
3850     obt_display_ignore_errors(TRUE);
3851
3852     if (self->can_focus) {
3853         /* This can cause a BadMatch error with CurrentTime, or if an app
3854            passed in a bad time for _NET_WM_ACTIVE_WINDOW. */
3855         XSetInputFocus(obt_display, self->w_client, RevertToPointerRoot,
3856                        event_curtime);
3857     }
3858
3859     if (self->focus_notify) {
3860         XEvent ce;
3861         ce.xclient.type = ClientMessage;
3862         ce.xclient.message_type = OBT_PROP_ATOM(WM_PROTOCOLS);
3863         ce.xclient.display = obt_display;
3864         ce.xclient.window = self->w_client;
3865         ce.xclient.format = 32;
3866         ce.xclient.data.l[0] = OBT_PROP_ATOM(WM_TAKE_FOCUS);
3867         ce.xclient.data.l[1] = event_curtime;
3868         ce.xclient.data.l[2] = 0l;
3869         ce.xclient.data.l[3] = 0l;
3870         ce.xclient.data.l[4] = 0l;
3871         XSendEvent(obt_display, self->w_client, FALSE, NoEventMask, &ce);
3872     }
3873
3874     obt_display_ignore_errors(FALSE);
3875
3876     ob_debug_type(OB_DEBUG_FOCUS, "Error focusing? %d",
3877                   obt_display_error_occured);
3878     return !obt_display_error_occured;
3879 }
3880
3881 static void client_present(ObClient *self, gboolean here, gboolean raise,
3882                            gboolean unshade)
3883 {
3884     if (client_normal(self) && screen_showing_desktop)
3885         screen_show_desktop(FALSE, self);
3886     if (self->iconic)
3887         client_iconify(self, FALSE, here, FALSE);
3888     if (self->desktop != DESKTOP_ALL &&
3889         self->desktop != screen_desktop)
3890     {
3891         if (here)
3892             client_set_desktop(self, screen_desktop, FALSE, TRUE);
3893         else
3894             screen_set_desktop(self->desktop, FALSE);
3895     } else if (!frame_engine->frame_is_visible(self->frame))
3896         /* if its not visible for other reasons, then don't mess
3897            with it */
3898         return;
3899     if (self->shaded && unshade)
3900         client_shade(self, FALSE);
3901     if (raise)
3902         stacking_raise(CLIENT_AS_WINDOW(self));
3903
3904     client_focus(self);
3905 }
3906
3907 void client_activate(ObClient *self, gboolean here, gboolean raise,
3908                      gboolean unshade, gboolean user)
3909 {
3910     client_present(self, here, raise, unshade);
3911 }
3912
3913 static void client_bring_windows_recursive(ObClient *self,
3914                                            guint desktop,
3915                                            gboolean helpers,
3916                                            gboolean modals,
3917                                            gboolean iconic)
3918 {
3919     GSList *it;
3920
3921     for (it = self->transients; it; it = g_slist_next(it))
3922         client_bring_windows_recursive(it->data, desktop,
3923                                        helpers, modals, iconic);
3924
3925     if (((helpers && client_helper(self)) ||
3926          (modals && self->modal)) &&
3927         ((self->desktop != desktop && self->desktop != DESKTOP_ALL) ||
3928          (iconic && self->iconic)))
3929     {
3930         if (iconic && self->iconic)
3931             client_iconify(self, FALSE, TRUE, FALSE);
3932         else
3933             client_set_desktop(self, desktop, FALSE, FALSE);
3934     }
3935 }
3936
3937 void client_bring_helper_windows(ObClient *self)
3938 {
3939     client_bring_windows_recursive(self, self->desktop, TRUE, FALSE, FALSE);
3940 }
3941
3942 void client_bring_modal_windows(ObClient *self)
3943 {
3944     client_bring_windows_recursive(self, self->desktop, FALSE, TRUE, TRUE);
3945 }
3946
3947 gboolean client_focused(ObClient *self)
3948 {
3949     return self == focus_client;
3950 }
3951
3952 static ObClientIcon* client_icon_recursive(ObClient *self, gint w, gint h)
3953 {
3954     guint i;
3955     gulong min_diff, min_i;
3956
3957     if (!self->nicons) {
3958         ObClientIcon *parent = NULL;
3959         GSList *it;
3960
3961         for (it = self->parents; it; it = g_slist_next(it)) {
3962             ObClient *c = it->data;
3963             if ((parent = client_icon_recursive(c, w, h)))
3964                 break;
3965         }
3966
3967         return parent;
3968     }
3969
3970     /* some kind of crappy approximation to find the icon closest in size to
3971        what we requested, but icons are generally all the same ratio as
3972        eachother so it's good enough. */
3973
3974     min_diff = ABS(self->icons[0].width - w) + ABS(self->icons[0].height - h);
3975     min_i = 0;
3976
3977     for (i = 1; i < self->nicons; ++i) {
3978         gulong diff;
3979
3980         diff = ABS(self->icons[i].width - w) + ABS(self->icons[i].height - h);
3981         if (diff < min_diff) {
3982             min_diff = diff;
3983             min_i = i;
3984         }
3985     }
3986     return &self->icons[min_i];
3987 }
3988
3989 const ObClientIcon* client_icon(ObClient *self, gint w, gint h)
3990 {
3991     ObClientIcon *ret;
3992     static ObClientIcon deficon;
3993
3994     if (!(ret = client_icon_recursive(self, w, h))) {
3995         deficon.width = deficon.height = 48;
3996         deficon.data = ob_rr_theme->def_win_icon;
3997         ret = &deficon;
3998     }
3999     return ret;
4000 }
4001
4002 void client_set_layer(ObClient *self, gint layer)
4003 {
4004     if (layer < 0) {
4005         self->below = TRUE;
4006         self->above = FALSE;
4007     } else if (layer == 0) {
4008         self->below = self->above = FALSE;
4009     } else {
4010         self->below = FALSE;
4011         self->above = TRUE;
4012     }
4013     client_calc_layer(self);
4014     client_change_state(self); /* reflect this in the state hints */
4015 }
4016
4017 void client_set_undecorated(ObClient *self, gboolean undecorated)
4018 {
4019     if (self->undecorated != undecorated &&
4020         /* don't let it undecorate if the function is missing, but let
4021            it redecorate */
4022         (self->functions & OB_CLIENT_FUNC_UNDECORATE || !undecorated))
4023     {
4024         self->undecorated = undecorated;
4025         client_setup_decor_and_functions(self, TRUE);
4026         client_change_state(self); /* reflect this in the state hints */
4027     }
4028 }
4029
4030 guint client_monitor(ObClient *self)
4031 {
4032     Rect area;
4033     frame_engine->frame_get_window_area(self->frame, &area);
4034     return screen_find_monitor(&area);
4035 }
4036
4037 ObClient *client_direct_parent(ObClient *self)
4038 {
4039     if (!self->parents) return NULL;
4040     if (self->transient_for_group) return NULL;
4041     return self->parents->data;
4042 }
4043
4044 ObClient *client_search_top_direct_parent(ObClient *self)
4045 {
4046     ObClient *p;
4047     while ((p = client_direct_parent(self))) self = p;
4048     return self;
4049 }
4050
4051 static GSList *client_search_all_top_parents_internal(ObClient *self,
4052                                                       gboolean bylayer,
4053                                                       ObStackingLayer layer)
4054 {
4055     GSList *ret;
4056     ObClient *p;
4057
4058     /* move up the direct transient chain as far as possible */
4059     while ((p = client_direct_parent(self)) &&
4060            (!bylayer || p->layer == layer))
4061         self = p;
4062
4063     if (!self->parents)
4064         ret = g_slist_prepend(NULL, self);
4065     else
4066         ret = g_slist_copy(self->parents);
4067
4068     return ret;
4069 }
4070
4071 GSList *client_search_all_top_parents(ObClient *self)
4072 {
4073     return client_search_all_top_parents_internal(self, FALSE, 0);
4074 }
4075
4076 GSList *client_search_all_top_parents_layer(ObClient *self)
4077 {
4078     return client_search_all_top_parents_internal(self, TRUE, self->layer);
4079 }
4080
4081 ObClient *client_search_focus_parent(ObClient *self)
4082 {
4083     GSList *it;
4084
4085     for (it = self->parents; it; it = g_slist_next(it))
4086         if (client_focused(it->data)) return it->data;
4087
4088     return NULL;
4089 }
4090
4091 ObClient *client_search_parent(ObClient *self, ObClient *search)
4092 {
4093     GSList *it;
4094
4095     for (it = self->parents; it; it = g_slist_next(it))
4096         if (it->data == search) return search;
4097
4098     return NULL;
4099 }
4100
4101 ObClient *client_search_transient(ObClient *self, ObClient *search)
4102 {
4103     GSList *sit;
4104
4105     for (sit = self->transients; sit; sit = g_slist_next(sit)) {
4106         if (sit->data == search)
4107             return search;
4108         if (client_search_transient(sit->data, search))
4109             return search;
4110     }
4111     return NULL;
4112 }
4113
4114 static void detect_edge(Rect area, ObDirection dir,
4115                         gint my_head, gint my_size,
4116                         gint my_edge_start, gint my_edge_size,
4117                         gint *dest, gboolean *near_edge)
4118 {
4119     gint edge_start, edge_size, head, tail;
4120     gboolean skip_head = FALSE, skip_tail = FALSE;
4121
4122     switch (dir) {
4123         case OB_DIRECTION_NORTH:
4124         case OB_DIRECTION_SOUTH:
4125             edge_start = area.x;
4126             edge_size = area.width;
4127             break;
4128         case OB_DIRECTION_EAST:
4129         case OB_DIRECTION_WEST:
4130             edge_start = area.y;
4131             edge_size = area.height;
4132             break;
4133         default:
4134             g_assert_not_reached();
4135     }
4136
4137     /* do we collide with this window? */
4138     if (!RANGES_INTERSECT(my_edge_start, my_edge_size,
4139                 edge_start, edge_size))
4140         return;
4141
4142     switch (dir) {
4143         case OB_DIRECTION_NORTH:
4144             head = RECT_BOTTOM(area);
4145             tail = RECT_TOP(area);
4146             break;
4147         case OB_DIRECTION_SOUTH:
4148             head = RECT_TOP(area);
4149             tail = RECT_BOTTOM(area);
4150             break;
4151         case OB_DIRECTION_WEST:
4152             head = RECT_RIGHT(area);
4153             tail = RECT_LEFT(area);
4154             break;
4155         case OB_DIRECTION_EAST:
4156             head = RECT_LEFT(area);
4157             tail = RECT_RIGHT(area);
4158             break;
4159         default:
4160             g_assert_not_reached();
4161     }
4162     switch (dir) {
4163         case OB_DIRECTION_NORTH:
4164         case OB_DIRECTION_WEST:
4165             /* check if our window is past the head of this window */
4166             if (my_head <= head + 1)
4167                 skip_head = TRUE;
4168             /* check if our window's tail is past the tail of this window */
4169             if (my_head + my_size - 1 <= tail)
4170                 skip_tail = TRUE;
4171             /* check if the head of this window is closer than the previously
4172                chosen edge (take into account that the previously chosen
4173                edge might have been a tail, not a head) */
4174             if (head + (*near_edge ? 0 : my_size) < *dest)
4175                 skip_head = TRUE;
4176             /* check if the tail of this window is closer than the previously
4177                chosen edge (take into account that the previously chosen
4178                edge might have been a head, not a tail) */
4179             if (tail - (!*near_edge ? 0 : my_size) < *dest)
4180                 skip_tail = TRUE;
4181             break;
4182         case OB_DIRECTION_SOUTH:
4183         case OB_DIRECTION_EAST:
4184             /* check if our window is past the head of this window */
4185             if (my_head >= head - 1)
4186                 skip_head = TRUE;
4187             /* check if our window's tail is past the tail of this window */
4188             if (my_head - my_size + 1 >= tail)
4189                 skip_tail = TRUE;
4190             /* check if the head of this window is closer than the previously
4191                chosen edge (take into account that the previously chosen
4192                edge might have been a tail, not a head) */
4193             if (head - (*near_edge ? 0 : my_size) > *dest)
4194                 skip_head = TRUE;
4195             /* check if the tail of this window is closer than the previously
4196                chosen edge (take into account that the previously chosen
4197                edge might have been a head, not a tail) */
4198             if (tail + (!*near_edge ? 0 : my_size) > *dest)
4199                 skip_tail = TRUE;
4200             break;
4201         default:
4202             g_assert_not_reached();
4203     }
4204
4205     ob_debug("my head %d size %d", my_head, my_size);
4206     ob_debug("head %d tail %d deest %d", head, tail, *dest);
4207     if (!skip_head) {
4208         ob_debug("using near edge %d", head);
4209         *dest = head;
4210         *near_edge = TRUE;
4211     }
4212     else if (!skip_tail) {
4213         ob_debug("using far edge %d", tail);
4214         *dest = tail;
4215         *near_edge = FALSE;
4216     }
4217 }
4218
4219 void client_find_edge_directional(ObClient *self, ObDirection dir,
4220                                   gint my_head, gint my_size,
4221                                   gint my_edge_start, gint my_edge_size,
4222                                   gint *dest, gboolean *near_edge)
4223 {
4224     GList *it;
4225     Rect *a, *mon;
4226     Rect dock_area;
4227     gint edge;
4228
4229     Strut size;
4230     frame_engine->frame_get_size(self->frame, &size);
4231     Rect area;
4232     frame_engine->frame_get_window_area(self->frame, &area);
4233
4234     a = screen_area(self->desktop, SCREEN_AREA_ALL_MONITORS,
4235                     &area);
4236     mon = screen_area(self->desktop, SCREEN_AREA_ONE_MONITOR,
4237                       &area);
4238
4239     switch (dir) {
4240     case OB_DIRECTION_NORTH:
4241         if (my_head >= RECT_TOP(*mon) + 1)
4242             edge = RECT_TOP(*mon) - 1;
4243         else
4244             edge = RECT_TOP(*a) - 1;
4245         break;
4246     case OB_DIRECTION_SOUTH:
4247         if (my_head <= RECT_BOTTOM(*mon) - 1)
4248             edge = RECT_BOTTOM(*mon) + 1;
4249         else
4250             edge = RECT_BOTTOM(*a) + 1;
4251         break;
4252     case OB_DIRECTION_EAST:
4253         if (my_head <= RECT_RIGHT(*mon) - 1)
4254             edge = RECT_RIGHT(*mon) + 1;
4255         else
4256             edge = RECT_RIGHT(*a) + 1;
4257         break;
4258     case OB_DIRECTION_WEST:
4259         if (my_head >= RECT_LEFT(*mon) + 1)
4260             edge = RECT_LEFT(*mon) - 1;
4261         else
4262             edge = RECT_LEFT(*a) - 1;
4263         break;
4264     default:
4265         g_assert_not_reached();
4266     }
4267     /* default to the far edge, then narrow it down */
4268     *dest = edge;
4269     *near_edge = TRUE;
4270
4271     for (it = client_list; it; it = g_list_next(it)) {
4272         ObClient *cur = it->data;
4273
4274         /* skip windows to not bump into */
4275         if (cur == self)
4276             continue;
4277         if (cur->iconic)
4278             continue;
4279         if (self->desktop != cur->desktop && cur->desktop != DESKTOP_ALL &&
4280             cur->desktop != screen_desktop)
4281             continue;
4282
4283         ob_debug("trying window %s", cur->title);
4284
4285         Rect area;
4286         frame_engine->frame_get_window_area(self->frame, &area); 
4287         detect_edge(area, dir, my_head, my_size, my_edge_start,
4288                     my_edge_size, dest, near_edge);
4289     }
4290     dock_get_area(&dock_area);
4291     detect_edge(dock_area, dir, my_head, my_size, my_edge_start,
4292                 my_edge_size, dest, near_edge);
4293     g_free(a);
4294     g_free(mon);
4295 }
4296
4297 void client_find_move_directional(ObClient *self, ObDirection dir,
4298                                   gint *x, gint *y)
4299 {
4300     gint head, size;
4301     gint e, e_start, e_size;
4302     gboolean near;
4303
4304
4305     Rect area;
4306     frame_engine->frame_get_window_area(self->frame, &area);
4307
4308     switch (dir) {
4309     case OB_DIRECTION_EAST:
4310         head = RECT_RIGHT(area);
4311         size = area.width;
4312         e_start = RECT_TOP(area);
4313         e_size = area.height;
4314         break;
4315     case OB_DIRECTION_WEST:
4316         head = RECT_LEFT(area);
4317         size = area.width;
4318         e_start = RECT_TOP(area);
4319         e_size = area.height;
4320         break;
4321     case OB_DIRECTION_NORTH:
4322         head = RECT_TOP(area);
4323         size = area.height;
4324         e_start = RECT_LEFT(area);
4325         e_size = area.width;
4326         break;
4327     case OB_DIRECTION_SOUTH:
4328         head = RECT_BOTTOM(area);
4329         size = area.height;
4330         e_start = RECT_LEFT(area);
4331         e_size = area.width;
4332         break;
4333     default:
4334         g_assert_not_reached();
4335     }
4336
4337     client_find_edge_directional(self, dir, head, size,
4338                                  e_start, e_size, &e, &near);
4339     *x = area.x;
4340     *y = area.y;
4341     switch (dir) {
4342     case OB_DIRECTION_EAST:
4343         if (near) e -= area.width;
4344         else      e++;
4345         *x = e;
4346         break;
4347     case OB_DIRECTION_WEST:
4348         if (near) e++;
4349         else      e -= area.width;
4350         *x = e;
4351         break;
4352     case OB_DIRECTION_NORTH:
4353         if (near) e++;
4354         else      e -= area.height;
4355         *y = e;
4356         break;
4357     case OB_DIRECTION_SOUTH:
4358         if (near) e -= area.height;
4359         else      e++;
4360         *y = e;
4361         break;
4362     default:
4363         g_assert_not_reached();
4364     }
4365     frame_frame_gravity(self, x, y);
4366 }
4367
4368 void client_find_resize_directional(ObClient *self, ObDirection side,
4369                                     gboolean grow,
4370                                     gint *x, gint *y, gint *w, gint *h)
4371 {
4372     gint head;
4373     gint e, e_start, e_size, delta;
4374     gboolean near;
4375     ObDirection dir;
4376
4377     Rect area;
4378     frame_engine->frame_get_window_area(self->frame, &area);
4379     Strut size;
4380     frame_engine->frame_get_size(self->frame, &size);
4381
4382     switch (side) {
4383     case OB_DIRECTION_EAST:
4384         head = RECT_RIGHT(area) +
4385             (self->size_inc.width - 1) * (grow ? 1 : -1);
4386         e_start = RECT_TOP(area);
4387         e_size = area.height;
4388         dir = grow ? OB_DIRECTION_EAST : OB_DIRECTION_WEST;
4389         break;
4390     case OB_DIRECTION_WEST:
4391         head = RECT_LEFT(area) -
4392             (self->size_inc.width - 1) * (grow ? 1 : -1);
4393         e_start = RECT_TOP(area);
4394         e_size = area.height;
4395         dir = grow ? OB_DIRECTION_WEST : OB_DIRECTION_EAST;
4396         break;
4397     case OB_DIRECTION_NORTH:
4398         head = RECT_TOP(area) -
4399             (self->size_inc.height - 1) * (grow ? 1 : -1);
4400         e_start = RECT_LEFT(area);
4401         e_size = area.width;
4402         dir = grow ? OB_DIRECTION_NORTH : OB_DIRECTION_SOUTH;
4403         break;
4404     case OB_DIRECTION_SOUTH:
4405         head = RECT_BOTTOM(area) +
4406             (self->size_inc.height - 1) * (grow ? 1 : -1);
4407         e_start = RECT_LEFT(area);
4408         e_size = area.width;
4409         dir = grow ? OB_DIRECTION_SOUTH : OB_DIRECTION_NORTH;
4410         break;
4411     default:
4412         g_assert_not_reached();
4413     }
4414
4415     ob_debug("head %d dir %d", head, dir);
4416     client_find_edge_directional(self, dir, head, 1,
4417                                  e_start, e_size, &e, &near);
4418     ob_debug("edge %d", e);
4419     *x = area.x;
4420     *y = area.y;
4421     *w = area.width;
4422     *h = area.height;
4423     switch (side) {
4424     case OB_DIRECTION_EAST:
4425         if (grow == near) --e;
4426         delta = e - RECT_RIGHT(area);
4427         *w += delta;
4428         break;
4429     case OB_DIRECTION_WEST:
4430         if (grow == near) ++e;
4431         delta = RECT_LEFT(area) - e;
4432         *x -= delta;
4433         *w += delta;
4434         break;
4435     case OB_DIRECTION_NORTH:
4436         if (grow == near) ++e;
4437         delta = RECT_TOP(area) - e;
4438         *y -= delta;
4439         *h += delta;
4440         break;
4441     case OB_DIRECTION_SOUTH:
4442         if (grow == near) --e;
4443         delta = e - RECT_BOTTOM(area);
4444         *h += delta;
4445         break;
4446     default:
4447         g_assert_not_reached();
4448     }
4449     frame_frame_gravity(self, x, y);
4450     *w -= size.left + size.right;
4451     *h -= size.top + size.bottom;
4452 }
4453
4454 ObClient* client_under_pointer(void)
4455 {
4456     gint x, y;
4457     GList *it;
4458     ObClient *ret = NULL;
4459
4460     if (screen_pointer_pos(&x, &y)) {
4461         for (it = stacking_list; it; it = g_list_next(it)) {
4462             if (WINDOW_IS_CLIENT(it->data)) {
4463                 ObClient *c = WINDOW_AS_CLIENT(it->data);
4464                 Rect area;
4465                 frame_engine->frame_get_window_area(c->frame, &area);
4466                 if (frame_engine->frame_is_visible(c->frame) &&
4467                     /* check the desktop, this is done during desktop
4468                        switching and windows are shown/hidden status is not
4469                        reliable */
4470                     (c->desktop == screen_desktop ||
4471                      c->desktop == DESKTOP_ALL) &&   
4472                     RECT_CONTAINS(area, x, y))
4473                 {
4474                     ret = c;
4475                     break;
4476                 }
4477             }
4478         }
4479     }
4480     return ret;
4481 }
4482
4483 gboolean client_has_group_siblings(ObClient *self)
4484 {
4485     return self->group && self->group->members->next;
4486 }
4487
4488 void client_show_frame(ObClient * self)
4489 {
4490     frame_engine->frame_set_is_visible(self->frame, TRUE);
4491     frame_engine->frame_update_layout(self->frame, self->area, FALSE, FALSE);
4492     frame_engine->frame_update_skin(self->frame);
4493     /* Grab the server to make sure that the frame window is mapped before
4494      the client gets its MapNotify, i.e. to make sure the client is
4495      _visible_ when it gets MapNotify. */
4496     grab_server(TRUE);
4497     XMapWindow(obt_display, self->w_client);
4498     XMapWindow(obt_display, self->w_frame);
4499     grab_server(FALSE);
4500 }
4501
4502 void client_hide_frame(ObClient * self)
4503 {
4504     frame_engine->frame_set_is_visible(self->frame, FALSE);
4505     XUnmapWindow(obt_display, self->w_frame);
4506     /* we unmap the client itself so that we can get MapRequest
4507      events, and because the ICCCM tells us to! */
4508     XUnmapWindow(obt_display, self->w_client);
4509     self->ignore_unmaps++;
4510 }