consistant glib type usage
[mikachu/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) 2003        Ben Jansens
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 GNU General Public License for more details.
15
16 See the COPYING file for a copy of the GNU General Public License.
17 */
18
19 #include "client.h"
20 #include "debug.h"
21 #include "startupnotify.h"
22 #include "dock.h"
23 #include "xerror.h"
24 #include "screen.h"
25 #include "moveresize.h"
26 #include "place.h"
27 #include "prop.h"
28 #include "extensions.h"
29 #include "frame.h"
30 #include "session.h"
31 #include "event.h"
32 #include "grab.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
43 #include <glib.h>
44 #include <X11/Xutil.h>
45
46 /*! The event mask to grab on client windows */
47 #define CLIENT_EVENTMASK (PropertyChangeMask | FocusChangeMask | \
48                           StructureNotifyMask)
49
50 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
51                                 ButtonMotionMask)
52
53 typedef struct
54 {
55     ObClientDestructor func;
56     gpointer data;
57 } Destructor;
58
59 GList      *client_list        = NULL;
60 GSList     *client_destructors = NULL;
61
62 static void client_get_all(ObClient *self);
63 static void client_toggle_border(ObClient *self, gboolean show);
64 static void client_get_startup_id(ObClient *self);
65 static void client_get_area(ObClient *self);
66 static void client_get_desktop(ObClient *self);
67 static void client_get_state(ObClient *self);
68 static void client_get_shaped(ObClient *self);
69 static void client_get_mwm_hints(ObClient *self);
70 static void client_get_gravity(ObClient *self);
71 static void client_showhide(ObClient *self);
72 static void client_change_allowed_actions(ObClient *self);
73 static void client_change_state(ObClient *self);
74 static void client_apply_startup_state(ObClient *self);
75 static void client_restore_session_state(ObClient *self);
76 static void client_restore_session_stacking(ObClient *self);
77 static void client_urgent_notify(ObClient *self);
78
79 void client_startup(gboolean reconfig)
80 {
81     if (reconfig) return;
82
83     client_set_list();
84 }
85
86 void client_shutdown(gboolean reconfig)
87 {
88 }
89
90 void client_add_destructor(ObClientDestructor func, gpointer data)
91 {
92     Destructor *d = g_new(Destructor, 1);
93     d->func = func;
94     d->data = data;
95     client_destructors = g_slist_prepend(client_destructors, d);
96 }
97
98 void client_remove_destructor(ObClientDestructor func)
99 {
100     GSList *it;
101
102     for (it = client_destructors; it; it = g_slist_next(it)) {
103         Destructor *d = it->data;
104         if (d->func == func) {
105             g_free(d);
106             client_destructors = g_slist_delete_link(client_destructors, it);
107             break;
108         }
109     }
110 }
111
112 void client_set_list()
113 {
114     Window *windows, *win_it;
115     GList *it;
116     guint size = g_list_length(client_list);
117
118     /* create an array of the window ids */
119     if (size > 0) {
120         windows = g_new(Window, size);
121         win_it = windows;
122         for (it = client_list; it != NULL; it = it->next, ++win_it)
123             *win_it = ((ObClient*)it->data)->window;
124     } else
125         windows = NULL;
126
127     PROP_SETA32(RootWindow(ob_display, ob_screen),
128                 net_client_list, window, (guint32*)windows, size);
129
130     if (windows)
131         g_free(windows);
132
133     stacking_set_list();
134 }
135
136 /*
137   void client_foreach_transient(ObClient *self, ObClientForeachFunc func, void *data)
138   {
139   GSList *it;
140
141   for (it = self->transients; it; it = it->next) {
142   if (!func(it->data, data)) return;
143   client_foreach_transient(it->data, func, data);
144   }
145   }
146
147   void client_foreach_ancestor(ObClient *self, ObClientForeachFunc func, void *data)
148   {
149   if (self->transient_for) {
150   if (self->transient_for != OB_TRAN_GROUP) {
151   if (!func(self->transient_for, data)) return;
152   client_foreach_ancestor(self->transient_for, func, data);
153   } else {
154   GSList *it;
155
156   for (it = self->group->members; it; it = it->next)
157   if (it->data != self &&
158   !((ObClient*)it->data)->transient_for) {
159   if (!func(it->data, data)) return;
160   client_foreach_ancestor(it->data, func, data);
161   }
162   }
163   }
164   }
165 */
166
167 void client_manage_all()
168 {
169     guint i, j, nchild;
170     Window w, *children;
171     XWMHints *wmhints;
172     XWindowAttributes attrib;
173
174     XQueryTree(ob_display, RootWindow(ob_display, ob_screen),
175                &w, &w, &children, &nchild);
176
177     /* remove all icon windows from the list */
178     for (i = 0; i < nchild; i++) {
179         if (children[i] == None) continue;
180         wmhints = XGetWMHints(ob_display, children[i]);
181         if (wmhints) {
182             if ((wmhints->flags & IconWindowHint) &&
183                 (wmhints->icon_window != children[i]))
184                 for (j = 0; j < nchild; j++)
185                     if (children[j] == wmhints->icon_window) {
186                         children[j] = None;
187                         break;
188                     }
189             XFree(wmhints);
190         }
191     }
192
193     for (i = 0; i < nchild; ++i) {
194         if (children[i] == None)
195             continue;
196         if (XGetWindowAttributes(ob_display, children[i], &attrib)) {
197             if (attrib.override_redirect) continue;
198
199             if (attrib.map_state != IsUnmapped)
200                 client_manage(children[i]);
201         }
202     }
203     XFree(children);
204 }
205
206 void client_manage(Window window)
207 {
208     ObClient *self;
209     XEvent e;
210     XWindowAttributes attrib;
211     XSetWindowAttributes attrib_set;
212     XWMHints *wmhint;
213     gboolean activate = FALSE;
214
215     grab_server(TRUE);
216
217     /* check if it has already been unmapped by the time we started mapping
218        the grab does a sync so we don't have to here */
219     if (XCheckTypedWindowEvent(ob_display, window, DestroyNotify, &e) ||
220         XCheckTypedWindowEvent(ob_display, window, UnmapNotify, &e)) {
221         XPutBackEvent(ob_display, &e);
222
223         grab_server(FALSE);
224         return; /* don't manage it */
225     }
226
227     /* make sure it isn't an override-redirect window */
228     if (!XGetWindowAttributes(ob_display, window, &attrib) ||
229         attrib.override_redirect) {
230         grab_server(FALSE);
231         return; /* don't manage it */
232     }
233   
234     /* is the window a docking app */
235     if ((wmhint = XGetWMHints(ob_display, window))) {
236         if ((wmhint->flags & StateHint) &&
237             wmhint->initial_state == WithdrawnState) {
238             dock_add(window, wmhint);
239             grab_server(FALSE);
240             XFree(wmhint);
241             return;
242         }
243         XFree(wmhint);
244     }
245
246     ob_debug("Managing window: %lx\n", window);
247
248     /* choose the events we want to receive on the CLIENT window */
249     attrib_set.event_mask = CLIENT_EVENTMASK;
250     attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK;
251     XChangeWindowAttributes(ob_display, window,
252                             CWEventMask|CWDontPropagate, &attrib_set);
253
254
255     /* create the ObClient struct, and populate it from the hints on the
256        window */
257     self = g_new0(ObClient, 1);
258     self->obwin.type = Window_Client;
259     self->window = window;
260
261     /* non-zero defaults */
262     self->title_count = 1;
263     self->wmstate = NormalState;
264     self->layer = -1;
265     self->desktop = screen_num_desktops; /* always an invalid value */
266
267     client_get_all(self);
268     client_restore_session_state(self);
269
270     sn_app_started(self->class);
271
272     client_change_state(self);
273
274     /* remove the client's border (and adjust re gravity) */
275     client_toggle_border(self, FALSE);
276      
277     /* specify that if we exit, the window should not be destroyed and should
278        be reparented back to root automatically */
279     XChangeSaveSet(ob_display, window, SetModeInsert);
280
281     /* create the decoration frame for the client window */
282     self->frame = frame_new();
283
284     frame_grab_client(self->frame, self);
285
286     grab_server(FALSE);
287
288     client_apply_startup_state(self);
289
290     /* update the focus lists */
291     focus_order_add_new(self);
292
293     stacking_add(CLIENT_AS_WINDOW(self));
294     client_restore_session_stacking(self);
295
296     /* focus the new window? */
297     if (ob_state() != OB_STATE_STARTING &&
298         (config_focus_new || client_search_focus_parent(self)) &&
299         /* note the check against Type_Normal/Dialog, not client_normal(self),
300            which would also include other types. in this case we want more
301            strict rules for focus */
302         (self->type == OB_CLIENT_TYPE_NORMAL ||
303          self->type == OB_CLIENT_TYPE_DIALOG))
304     {        
305         activate = TRUE;
306 #if 0
307         if (self->desktop != screen_desktop) {
308             /* activate the window */
309             activate = TRUE;
310         } else {
311             gboolean group_foc = FALSE;
312
313             if (self->group) {
314                 GSList *it;
315
316                 for (it = self->group->members; it; it = it->next)
317                 {
318                     if (client_focused(it->data))
319                     {
320                         group_foc = TRUE;
321                         break;
322                     }
323                 }
324             }
325             if ((group_foc ||
326                  (!self->transient_for && (!self->group ||
327                                            !self->group->members->next))) ||
328                 client_search_focus_tree_full(self) ||
329                 !focus_client ||
330                 !client_normal(focus_client))
331             {
332                 /* activate the window */
333                 activate = TRUE;
334             }
335         }
336 #endif
337     }
338
339     if (ob_state() == OB_STATE_RUNNING) {
340         gint x = self->area.x, ox = x;
341         gint y = self->area.y, oy = y;
342
343         place_client(self, &x, &y);
344
345         /* make sure the window is visible */
346         client_find_onscreen(self, &x, &y,
347                              self->frame->area.width,
348                              self->frame->area.height,
349                              /* non-normal clients has less rules, and
350                                 windows that are being restored from a session
351                                 do also. we can assume you want it back where
352                                 you saved it */
353                              client_normal(self) && !self->session);
354
355         if (x != ox || y != oy)
356             client_move(self, x, y);
357     }
358
359     client_showhide(self);
360
361     /* use client_focus instead of client_activate cuz client_activate does
362        stuff like switch desktops etc and I'm not interested in all that when
363        a window maps since its not based on an action from the user like
364        clicking a window to activate is. so keep the new window out of the way
365        but do focus it. */
366     if (activate) {
367         /* if using focus_delay, stop the timer now so that focus doesn't go
368            moving on us */
369         event_halt_focus_delay();
370
371         client_focus(self);
372         /* since focus can change the stacking orders, if we focus the window
373            then the standard raise it gets is not enough, we need to queue one
374            for after the focus change takes place */
375         client_raise(self);
376     }
377
378     /* client_activate does this but we aret using it so we have to do it
379        here as well */
380     if (screen_showing_desktop)
381         screen_show_desktop(FALSE);
382
383     /* add to client list/map */
384     client_list = g_list_append(client_list, self);
385     g_hash_table_insert(window_map, &self->window, self);
386
387     /* this has to happen after we're in the client_list */
388     screen_update_areas();
389
390     /* update the list hints */
391     client_set_list();
392
393     keyboard_grab_for_client(self, TRUE);
394     mouse_grab_for_client(self, TRUE);
395
396     ob_debug("Managed window 0x%lx (%s)\n", window, self->class);
397 }
398
399 void client_unmanage_all()
400 {
401     while (client_list != NULL)
402         client_unmanage(client_list->data);
403 }
404
405 void client_unmanage(ObClient *self)
406 {
407     guint j;
408     GSList *it;
409
410     ob_debug("Unmanaging window: %lx (%s)\n", self->window, self->class);
411
412     g_assert(self != NULL);
413
414     keyboard_grab_for_client(self, FALSE);
415     mouse_grab_for_client(self, FALSE);
416
417     /* remove the window from our save set */
418     XChangeSaveSet(ob_display, self->window, SetModeDelete);
419
420     /* we dont want events no more */
421     XSelectInput(ob_display, self->window, NoEventMask);
422
423     frame_hide(self->frame);
424
425     client_list = g_list_remove(client_list, self);
426     stacking_remove(self);
427     g_hash_table_remove(window_map, &self->window);
428
429     /* update the focus lists */
430     focus_order_remove(self);
431
432     /* once the client is out of the list, update the struts to remove it's
433        influence */
434     screen_update_areas();
435
436     for (it = client_destructors; it; it = g_slist_next(it)) {
437         Destructor *d = it->data;
438         d->func(self, d->data);
439     }
440         
441     if (focus_client == self) {
442         XEvent e;
443
444         /* focus the last focused window on the desktop, and ignore enter
445            events from the unmap so it doesnt mess with the focus */
446         while (XCheckTypedEvent(ob_display, EnterNotify, &e));
447         /* remove these flags so we don't end up getting focused in the
448            fallback! */
449         self->can_focus = FALSE;
450         self->focus_notify = FALSE;
451         self->modal = FALSE;
452         client_unfocus(self);
453     }
454
455     /* tell our parent(s) that we're gone */
456     if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */
457         GSList *it;
458
459         for (it = self->group->members; it; it = it->next)
460             if (it->data != self)
461                 ((ObClient*)it->data)->transients =
462                     g_slist_remove(((ObClient*)it->data)->transients, self);
463     } else if (self->transient_for) {        /* transient of window */
464         self->transient_for->transients =
465             g_slist_remove(self->transient_for->transients, self);
466     }
467
468     /* tell our transients that we're gone */
469     for (it = self->transients; it != NULL; it = it->next) {
470         if (((ObClient*)it->data)->transient_for != OB_TRAN_GROUP) {
471             ((ObClient*)it->data)->transient_for = NULL;
472             client_calc_layer(it->data);
473         }
474     }
475
476     /* remove from its group */
477     if (self->group) {
478         group_remove(self->group, self);
479         self->group = NULL;
480     }
481
482     /* give the client its border back */
483     client_toggle_border(self, TRUE);
484
485     /* reparent the window out of the frame, and free the frame */
486     frame_release_client(self->frame, self);
487     self->frame = NULL;
488      
489     if (ob_state() != OB_STATE_EXITING) {
490         /* these values should not be persisted across a window
491            unmapping/mapping */
492         PROP_ERASE(self->window, net_wm_desktop);
493         PROP_ERASE(self->window, net_wm_state);
494         PROP_ERASE(self->window, wm_state);
495     } else {
496         /* if we're left in an unmapped state, the client wont be mapped. this
497            is bad, since we will no longer be managing the window on restart */
498         XMapWindow(ob_display, self->window);
499     }
500
501
502     ob_debug("Unmanaged window 0x%lx\n", self->window);
503
504     /* free all data allocated in the client struct */
505     g_slist_free(self->transients);
506     for (j = 0; j < self->nicons; ++j)
507         g_free(self->icons[j].data);
508     if (self->nicons > 0)
509         g_free(self->icons);
510     g_free(self->title);
511     g_free(self->icon_title);
512     g_free(self->name);
513     g_free(self->class);
514     g_free(self->role);
515     g_free(self->sm_client_id);
516     g_free(self);
517      
518     /* update the list hints */
519     client_set_list();
520 }
521
522 static void client_urgent_notify(ObClient *self)
523 {
524     if (self->urgent)
525         frame_flash_start(self->frame);
526     else
527         frame_flash_stop(self->frame);
528 }
529
530 static void client_restore_session_state(ObClient *self)
531 {
532     GList *it;
533
534     if (!(it = session_state_find(self)))
535         return;
536
537     self->session = it->data;
538
539     RECT_SET_POINT(self->area, self->session->x, self->session->y);
540     self->positioned = TRUE;
541     if (self->session->w > 0)
542         self->area.width = self->session->w;
543     if (self->session->h > 0)
544         self->area.height = self->session->h;
545     XResizeWindow(ob_display, self->window,
546                   self->area.width, self->area.height);
547
548     self->desktop = (self->session->desktop == DESKTOP_ALL ?
549                      self->session->desktop :
550                      MIN(screen_num_desktops - 1, self->session->desktop));
551     PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop);
552
553     self->shaded = self->session->shaded;
554     self->iconic = self->session->iconic;
555     self->skip_pager = self->session->skip_pager;
556     self->skip_taskbar = self->session->skip_taskbar;
557     self->fullscreen = self->session->fullscreen;
558     self->above = self->session->above;
559     self->below = self->session->below;
560     self->max_horz = self->session->max_horz;
561     self->max_vert = self->session->max_vert;
562 }
563
564 static void client_restore_session_stacking(ObClient *self)
565 {
566     GList *it;
567
568     if (!self->session) return;
569
570     it = g_list_find(session_saved_state, self->session);
571     for (it = g_list_previous(it); it; it = g_list_previous(it)) {
572         GList *cit;
573
574         for (cit = client_list; cit; cit = g_list_next(cit))
575             if (session_state_cmp(it->data, cit->data))
576                 break;
577         if (cit) {
578             client_calc_layer(self);
579             stacking_below(CLIENT_AS_WINDOW(self),
580                            CLIENT_AS_WINDOW(cit->data));
581             break;
582         }
583     }
584 }
585
586 void client_move_onscreen(ObClient *self, gboolean rude)
587 {
588     gint x = self->area.x;
589     gint y = self->area.y;
590     if (client_find_onscreen(self, &x, &y,
591                              self->frame->area.width,
592                              self->frame->area.height, rude)) {
593         client_move(self, x, y);
594     }
595 }
596
597 gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
598                               gboolean rude)
599 {
600     Rect *a;
601     gint ox = *x, oy = *y;
602
603     frame_client_gravity(self->frame, x, y); /* get where the frame
604                                                 would be */
605
606     /* XXX watch for xinerama dead areas */
607
608     a = screen_area(self->desktop);
609     if (client_normal(self)) {
610         if (!self->strut.right && *x >= a->x + a->width - 1)
611             *x = a->x + a->width - self->frame->area.width;
612         if (!self->strut.bottom && *y >= a->y + a->height - 1)
613             *y = a->y + a->height - self->frame->area.height;
614         if (!self->strut.left && *x + self->frame->area.width - 1 < a->x)
615             *x = a->x;
616         if (!self->strut.top && *y + self->frame->area.height - 1 < a->y)
617             *y = a->y;
618     }
619
620     if (rude) {
621         /* this is my MOZILLA BITCHSLAP. oh ya it fucking feels good.
622            Java can suck it too. */
623
624         /* dont let windows map/move into the strut unless they
625            are bigger than the available area */
626         if (w <= a->width) {
627             if (!self->strut.left && *x < a->x) *x = a->x;
628             if (!self->strut.right && *x + w > a->x + a->width)
629                 *x = a->x + a->width - w;
630         }
631         if (h <= a->height) {
632             if (!self->strut.top && *y < a->y) *y = a->y;
633             if (!self->strut.bottom && *y + h > a->y + a->height)
634                 *y = a->y + a->height - h;
635         }
636     }
637
638     frame_frame_gravity(self->frame, x, y); /* get where the client
639                                                should be */
640
641     return ox != *x || oy != *y;
642 }
643
644 static void client_toggle_border(ObClient *self, gboolean show)
645 {
646     /* adjust our idea of where the client is, based on its border. When the
647        border is removed, the client should now be considered to be in a
648        different position.
649        when re-adding the border to the client, the same operation needs to be
650        reversed. */
651     gint oldx = self->area.x, oldy = self->area.y;
652     gint x = oldx, y = oldy;
653     switch(self->gravity) {
654     default:
655     case NorthWestGravity:
656     case WestGravity:
657     case SouthWestGravity:
658         break;
659     case NorthEastGravity:
660     case EastGravity:
661     case SouthEastGravity:
662         if (show) x -= self->border_width * 2;
663         else      x += self->border_width * 2;
664         break;
665     case NorthGravity:
666     case SouthGravity:
667     case CenterGravity:
668     case ForgetGravity:
669     case StaticGravity:
670         if (show) x -= self->border_width;
671         else      x += self->border_width;
672         break;
673     }
674     switch(self->gravity) {
675     default:
676     case NorthWestGravity:
677     case NorthGravity:
678     case NorthEastGravity:
679         break;
680     case SouthWestGravity:
681     case SouthGravity:
682     case SouthEastGravity:
683         if (show) y -= self->border_width * 2;
684         else      y += self->border_width * 2;
685         break;
686     case WestGravity:
687     case EastGravity:
688     case CenterGravity:
689     case ForgetGravity:
690     case StaticGravity:
691         if (show) y -= self->border_width;
692         else      y += self->border_width;
693         break;
694     }
695     self->area.x = x;
696     self->area.y = y;
697
698     if (show) {
699         XSetWindowBorderWidth(ob_display, self->window, self->border_width);
700
701         /* move the client so it is back it the right spot _with_ its
702            border! */
703         if (x != oldx || y != oldy)
704             XMoveWindow(ob_display, self->window, x, y);
705     } else
706         XSetWindowBorderWidth(ob_display, self->window, 0);
707 }
708
709
710 static void client_get_all(ObClient *self)
711 {
712     client_get_area(self);
713     client_update_transient_for(self);
714     client_update_wmhints(self);
715     client_get_startup_id(self);
716     client_get_desktop(self);
717     client_get_state(self);
718     client_get_shaped(self);
719
720     client_get_mwm_hints(self);
721     client_get_type(self);/* this can change the mwmhints for special cases */
722
723     {
724         /* a couple type-based defaults for new windows */
725
726         /* this makes sure that these windows appear on all desktops */
727         if (self->type == OB_CLIENT_TYPE_DESKTOP)
728             self->desktop = DESKTOP_ALL;
729
730         /* dock windows default to ABOVE */
731         if (self->type == OB_CLIENT_TYPE_DOCK && !self->below)
732             self->above = TRUE;
733     }
734
735     client_update_protocols(self);
736
737     client_get_gravity(self); /* get the attribute gravity */
738     client_update_normal_hints(self); /* this may override the attribute
739                                          gravity */
740
741     /* got the type, the mwmhints, the protocols, and the normal hints
742        (min/max sizes), so we're ready to set up the decorations/functions */
743     client_setup_decor_and_functions(self);
744   
745     client_update_title(self);
746     client_update_class(self);
747     client_update_sm_client_id(self);
748     client_update_strut(self);
749     client_update_icons(self);
750 }
751
752 static void client_get_startup_id(ObClient *self)
753 {
754     if (!(PROP_GETS(self->window, net_startup_id, utf8, &self->startup_id)))
755         if (self->group)
756             PROP_GETS(self->group->leader,
757                       net_startup_id, utf8, &self->startup_id);
758 }
759
760 static void client_get_area(ObClient *self)
761 {
762     XWindowAttributes wattrib;
763     Status ret;
764   
765     ret = XGetWindowAttributes(ob_display, self->window, &wattrib);
766     g_assert(ret != BadWindow);
767
768     RECT_SET(self->area, wattrib.x, wattrib.y, wattrib.width, wattrib.height);
769     self->border_width = wattrib.border_width;
770 }
771
772 static void client_get_desktop(ObClient *self)
773 {
774     guint32 d = screen_num_desktops; /* an always-invalid value */
775
776     if (PROP_GET32(self->window, net_wm_desktop, cardinal, &d)) {
777         if (d >= screen_num_desktops && d != DESKTOP_ALL)
778             self->desktop = screen_num_desktops - 1;
779         else
780             self->desktop = d;
781     } else {
782         gboolean trdesk = FALSE;
783
784         if (self->transient_for) {
785             if (self->transient_for != OB_TRAN_GROUP) {
786                 self->desktop = self->transient_for->desktop;
787                 trdesk = TRUE;
788             } else {
789                 GSList *it;
790
791                 for (it = self->group->members; it; it = it->next)
792                     if (it->data != self &&
793                         !((ObClient*)it->data)->transient_for) {
794                         self->desktop = ((ObClient*)it->data)->desktop;
795                         trdesk = TRUE;
796                         break;
797                     }
798             }
799         }
800         if (!trdesk) {
801             /* try get from the startup-notification protocol */
802             if (sn_get_desktop(self->startup_id, &self->desktop)) {
803                 if (self->desktop >= screen_num_desktops &&
804                     self->desktop != DESKTOP_ALL)
805                     self->desktop = screen_num_desktops - 1;
806             } else
807                 /* defaults to the current desktop */
808                 self->desktop = screen_desktop;
809         }
810     }
811     if (self->desktop != d) {
812         /* set the desktop hint, to make sure that it always exists */
813         PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop);
814     }
815 }
816
817 static void client_get_state(ObClient *self)
818 {
819     guint32 *state;
820     guint num;
821   
822     if (PROP_GETA32(self->window, net_wm_state, atom, &state, &num)) {
823         gulong i;
824         for (i = 0; i < num; ++i) {
825             if (state[i] == prop_atoms.net_wm_state_modal)
826                 self->modal = TRUE;
827             else if (state[i] == prop_atoms.net_wm_state_shaded)
828                 self->shaded = TRUE;
829             else if (state[i] == prop_atoms.net_wm_state_hidden)
830                 self->iconic = TRUE;
831             else if (state[i] == prop_atoms.net_wm_state_skip_taskbar)
832                 self->skip_taskbar = TRUE;
833             else if (state[i] == prop_atoms.net_wm_state_skip_pager)
834                 self->skip_pager = TRUE;
835             else if (state[i] == prop_atoms.net_wm_state_fullscreen)
836                 self->fullscreen = TRUE;
837             else if (state[i] == prop_atoms.net_wm_state_maximized_vert)
838                 self->max_vert = TRUE;
839             else if (state[i] == prop_atoms.net_wm_state_maximized_horz)
840                 self->max_horz = TRUE;
841             else if (state[i] == prop_atoms.net_wm_state_above)
842                 self->above = TRUE;
843             else if (state[i] == prop_atoms.net_wm_state_below)
844                 self->below = TRUE;
845             else if (state[i] == prop_atoms.ob_wm_state_undecorated)
846                 self->undecorated = TRUE;
847         }
848
849         g_free(state);
850     }
851
852     if (!(self->above || self->below)) {
853         if (self->group) {
854             /* apply stuff from the group */
855             GSList *it;
856             gint layer = -2;
857
858             for (it = self->group->members; it; it = g_slist_next(it)) {
859                 ObClient *c = it->data;
860                 if (c != self && !client_search_transient(self, c))
861                 {
862                     layer = MAX(layer,
863                                 (c->above ? 1 : (c->below ? -1 : 0)));
864                 }
865             }
866             switch (layer) {
867             case -1:
868                 self->below = TRUE;
869                 break;
870             case -2:
871             case 0:
872                 break;
873             case 1:
874                 self->above = TRUE;
875                 break;
876             default:
877                 g_assert_not_reached();
878                 break;
879             }
880         }
881     }
882 }
883
884 static void client_get_shaped(ObClient *self)
885 {
886     self->shaped = FALSE;
887 #ifdef   SHAPE
888     if (extensions_shape) {
889         gint foo;
890         guint ufoo;
891         gint s;
892
893         XShapeSelectInput(ob_display, self->window, ShapeNotifyMask);
894
895         XShapeQueryExtents(ob_display, self->window, &s, &foo,
896                            &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo,
897                            &ufoo);
898         self->shaped = (s != 0);
899     }
900 #endif
901 }
902
903 void client_update_transient_for(ObClient *self)
904 {
905     Window t = None;
906     ObClient *target = NULL;
907
908     if (XGetTransientForHint(ob_display, self->window, &t)) {
909         self->transient = TRUE;
910         if (t != self->window) { /* cant be transient to itself! */
911             target = g_hash_table_lookup(window_map, &t);
912             /* if this happens then we need to check for it*/
913             g_assert(target != self);
914             if (target && !WINDOW_IS_CLIENT(target)) {
915                 /* this can happen when a dialog is a child of
916                    a dockapp, for example */
917                 target = NULL;
918             }
919             
920             if (!target && self->group) {
921                 /* not transient to a client, see if it is transient for a
922                    group */
923                 if (t == self->group->leader ||
924                     t == None ||
925                     t == RootWindow(ob_display, ob_screen))
926                 {
927                     /* window is a transient for its group! */
928                     target = OB_TRAN_GROUP;
929                 }
930             }
931         }
932     } else
933         self->transient = FALSE;
934
935     /* if anything has changed... */
936     if (target != self->transient_for) {
937         if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */
938             GSList *it;
939
940             /* remove from old parents */
941             for (it = self->group->members; it; it = g_slist_next(it)) {
942                 ObClient *c = it->data;
943                 if (c != self && !c->transient_for)
944                     c->transients = g_slist_remove(c->transients, self);
945             }
946         } else if (self->transient_for != NULL) { /* transient of window */
947             /* remove from old parent */
948             self->transient_for->transients =
949                 g_slist_remove(self->transient_for->transients, self);
950         }
951         self->transient_for = target;
952         if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */
953             GSList *it;
954
955             /* add to new parents */
956             for (it = self->group->members; it; it = g_slist_next(it)) {
957                 ObClient *c = it->data;
958                 if (c != self && !c->transient_for)
959                     c->transients = g_slist_append(c->transients, self);
960             }
961
962             /* remove all transients which are in the group, that causes
963                circlular pointer hell of doom */
964             for (it = self->group->members; it; it = g_slist_next(it)) {
965                 GSList *sit, *next;
966                 for (sit = self->transients; sit; sit = next) {
967                     next = g_slist_next(sit);
968                     if (sit->data == it->data)
969                         self->transients =
970                             g_slist_delete_link(self->transients, sit);
971                 }
972             }
973         } else if (self->transient_for != NULL) { /* transient of window */
974             /* add to new parent */
975             self->transient_for->transients =
976                 g_slist_append(self->transient_for->transients, self);
977         }
978     }
979 }
980
981 static void client_get_mwm_hints(ObClient *self)
982 {
983     guint num;
984     guint32 *hints;
985
986     self->mwmhints.flags = 0; /* default to none */
987
988     if (PROP_GETA32(self->window, motif_wm_hints, motif_wm_hints,
989                     &hints, &num)) {
990         if (num >= OB_MWM_ELEMENTS) {
991             self->mwmhints.flags = hints[0];
992             self->mwmhints.functions = hints[1];
993             self->mwmhints.decorations = hints[2];
994         }
995         g_free(hints);
996     }
997 }
998
999 void client_get_type(ObClient *self)
1000 {
1001     guint num, i;
1002     guint32 *val;
1003
1004     self->type = -1;
1005   
1006     if (PROP_GETA32(self->window, net_wm_window_type, atom, &val, &num)) {
1007         /* use the first value that we know about in the array */
1008         for (i = 0; i < num; ++i) {
1009             if (val[i] == prop_atoms.net_wm_window_type_desktop)
1010                 self->type = OB_CLIENT_TYPE_DESKTOP;
1011             else if (val[i] == prop_atoms.net_wm_window_type_dock)
1012                 self->type = OB_CLIENT_TYPE_DOCK;
1013             else if (val[i] == prop_atoms.net_wm_window_type_toolbar)
1014                 self->type = OB_CLIENT_TYPE_TOOLBAR;
1015             else if (val[i] == prop_atoms.net_wm_window_type_menu)
1016                 self->type = OB_CLIENT_TYPE_MENU;
1017             else if (val[i] == prop_atoms.net_wm_window_type_utility)
1018                 self->type = OB_CLIENT_TYPE_UTILITY;
1019             else if (val[i] == prop_atoms.net_wm_window_type_splash)
1020                 self->type = OB_CLIENT_TYPE_SPLASH;
1021             else if (val[i] == prop_atoms.net_wm_window_type_dialog)
1022                 self->type = OB_CLIENT_TYPE_DIALOG;
1023             else if (val[i] == prop_atoms.net_wm_window_type_normal)
1024                 self->type = OB_CLIENT_TYPE_NORMAL;
1025             else if (val[i] == prop_atoms.kde_net_wm_window_type_override) {
1026                 /* prevent this window from getting any decor or
1027                    functionality */
1028                 self->mwmhints.flags &= (OB_MWM_FLAG_FUNCTIONS |
1029                                          OB_MWM_FLAG_DECORATIONS);
1030                 self->mwmhints.decorations = 0;
1031                 self->mwmhints.functions = 0;
1032             }
1033             if (self->type != (ObClientType) -1)
1034                 break; /* grab the first legit type */
1035         }
1036         g_free(val);
1037     }
1038     
1039     if (self->type == (ObClientType) -1) {
1040         /*the window type hint was not set, which means we either classify
1041           ourself as a normal window or a dialog, depending on if we are a
1042           transient. */
1043         if (self->transient)
1044             self->type = OB_CLIENT_TYPE_DIALOG;
1045         else
1046             self->type = OB_CLIENT_TYPE_NORMAL;
1047     }
1048 }
1049
1050 void client_update_protocols(ObClient *self)
1051 {
1052     guint32 *proto;
1053     guint num_return, i;
1054
1055     self->focus_notify = FALSE;
1056     self->delete_window = FALSE;
1057
1058     if (PROP_GETA32(self->window, wm_protocols, atom, &proto, &num_return)) {
1059         for (i = 0; i < num_return; ++i) {
1060             if (proto[i] == prop_atoms.wm_delete_window) {
1061                 /* this means we can request the window to close */
1062                 self->delete_window = TRUE;
1063             } else if (proto[i] == prop_atoms.wm_take_focus)
1064                 /* if this protocol is requested, then the window will be
1065                    notified whenever we want it to receive focus */
1066                 self->focus_notify = TRUE;
1067         }
1068         g_free(proto);
1069     }
1070 }
1071
1072 static void client_get_gravity(ObClient *self)
1073 {
1074     XWindowAttributes wattrib;
1075     Status ret;
1076
1077     ret = XGetWindowAttributes(ob_display, self->window, &wattrib);
1078     g_assert(ret != BadWindow);
1079     self->gravity = wattrib.win_gravity;
1080 }
1081
1082 void client_update_normal_hints(ObClient *self)
1083 {
1084     XSizeHints size;
1085     glong ret;
1086     gint oldgravity = self->gravity;
1087
1088     /* defaults */
1089     self->min_ratio = 0.0f;
1090     self->max_ratio = 0.0f;
1091     SIZE_SET(self->size_inc, 1, 1);
1092     SIZE_SET(self->base_size, 0, 0);
1093     SIZE_SET(self->min_size, 0, 0);
1094     SIZE_SET(self->max_size, G_MAXINT, G_MAXINT);
1095
1096     /* get the hints from the window */
1097     if (XGetWMNormalHints(ob_display, self->window, &size, &ret)) {
1098         /* normal windows can't request placement! har har
1099         if (!client_normal(self))
1100         */
1101         self->positioned = !!(size.flags & (PPosition|USPosition));
1102
1103         if (size.flags & PWinGravity) {
1104             self->gravity = size.win_gravity;
1105       
1106             /* if the client has a frame, i.e. has already been mapped and
1107                is changing its gravity */
1108             if (self->frame && self->gravity != oldgravity) {
1109                 /* move our idea of the client's position based on its new
1110                    gravity */
1111                 self->area.x = self->frame->area.x;
1112                 self->area.y = self->frame->area.y;
1113                 frame_frame_gravity(self->frame, &self->area.x, &self->area.y);
1114             }
1115         }
1116
1117         if (size.flags & PAspect) {
1118             if (size.min_aspect.y)
1119                 self->min_ratio =
1120                     (gfloat) size.min_aspect.x / size.min_aspect.y;
1121             if (size.max_aspect.y)
1122                 self->max_ratio =
1123                     (gfloat) size.max_aspect.x / size.max_aspect.y;
1124         }
1125
1126         if (size.flags & PMinSize)
1127             SIZE_SET(self->min_size, size.min_width, size.min_height);
1128     
1129         if (size.flags & PMaxSize)
1130             SIZE_SET(self->max_size, size.max_width, size.max_height);
1131     
1132         if (size.flags & PBaseSize)
1133             SIZE_SET(self->base_size, size.base_width, size.base_height);
1134     
1135         if (size.flags & PResizeInc)
1136             SIZE_SET(self->size_inc, size.width_inc, size.height_inc);
1137     }
1138 }
1139
1140 void client_setup_decor_and_functions(ObClient *self)
1141 {
1142     /* start with everything (cept fullscreen) */
1143     self->decorations =
1144         (OB_FRAME_DECOR_TITLEBAR |
1145          (ob_rr_theme->show_handle ? OB_FRAME_DECOR_HANDLE : 0) |
1146          OB_FRAME_DECOR_GRIPS |
1147          OB_FRAME_DECOR_BORDER |
1148          OB_FRAME_DECOR_ICON |
1149          OB_FRAME_DECOR_ALLDESKTOPS |
1150          OB_FRAME_DECOR_ICONIFY |
1151          OB_FRAME_DECOR_MAXIMIZE |
1152          OB_FRAME_DECOR_SHADE |
1153          OB_FRAME_DECOR_CLOSE);
1154     self->functions =
1155         (OB_CLIENT_FUNC_RESIZE |
1156          OB_CLIENT_FUNC_MOVE |
1157          OB_CLIENT_FUNC_ICONIFY |
1158          OB_CLIENT_FUNC_MAXIMIZE |
1159          OB_CLIENT_FUNC_SHADE |
1160          OB_CLIENT_FUNC_CLOSE);
1161
1162     if (!(self->min_size.width < self->max_size.width ||
1163           self->min_size.height < self->max_size.height))
1164         self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1165
1166     switch (self->type) {
1167     case OB_CLIENT_TYPE_NORMAL:
1168         /* normal windows retain all of the possible decorations and
1169            functionality, and are the only windows that you can fullscreen */
1170         self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
1171         break;
1172
1173     case OB_CLIENT_TYPE_DIALOG:
1174     case OB_CLIENT_TYPE_UTILITY:
1175         /* these windows cannot be maximized */
1176         self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1177         break;
1178
1179     case OB_CLIENT_TYPE_MENU:
1180     case OB_CLIENT_TYPE_TOOLBAR:
1181         /* these windows get less functionality */
1182         self->functions &= ~(OB_CLIENT_FUNC_ICONIFY | OB_CLIENT_FUNC_RESIZE);
1183         break;
1184
1185     case OB_CLIENT_TYPE_DESKTOP:
1186     case OB_CLIENT_TYPE_DOCK:
1187     case OB_CLIENT_TYPE_SPLASH:
1188         /* none of these windows are manipulated by the window manager */
1189         self->decorations = 0;
1190         self->functions = 0;
1191         break;
1192     }
1193
1194     /* Mwm Hints are applied subtractively to what has already been chosen for
1195        decor and functionality */
1196     if (self->mwmhints.flags & OB_MWM_FLAG_DECORATIONS) {
1197         if (! (self->mwmhints.decorations & OB_MWM_DECOR_ALL)) {
1198             if (! ((self->mwmhints.decorations & OB_MWM_DECOR_HANDLE) ||
1199                    (self->mwmhints.decorations & OB_MWM_DECOR_TITLE)))
1200                 /* if the mwm hints request no handle or title, then all
1201                    decorations are disabled */
1202                 self->decorations = 0;
1203         }
1204     }
1205
1206     if (self->mwmhints.flags & OB_MWM_FLAG_FUNCTIONS) {
1207         if (! (self->mwmhints.functions & OB_MWM_FUNC_ALL)) {
1208             if (! (self->mwmhints.functions & OB_MWM_FUNC_RESIZE))
1209                 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1210             if (! (self->mwmhints.functions & OB_MWM_FUNC_MOVE))
1211                 self->functions &= ~OB_CLIENT_FUNC_MOVE;
1212             /* dont let mwm hints kill any buttons
1213                if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1214                self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1215                if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1216                self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1217             */
1218             /* dont let mwm hints kill the close button
1219                if (! (self->mwmhints.functions & MwmFunc_Close))
1220                self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1221             }
1222     }
1223
1224     if (!(self->functions & OB_CLIENT_FUNC_SHADE))
1225         self->decorations &= ~OB_FRAME_DECOR_SHADE;
1226     if (!(self->functions & OB_CLIENT_FUNC_ICONIFY))
1227         self->decorations &= ~OB_FRAME_DECOR_ICONIFY;
1228     if (!(self->functions & OB_CLIENT_FUNC_RESIZE))
1229         self->decorations &= ~OB_FRAME_DECOR_GRIPS;
1230
1231     /* can't maximize without moving/resizing */
1232     if (!((self->functions & OB_CLIENT_FUNC_MAXIMIZE) &&
1233           (self->functions & OB_CLIENT_FUNC_MOVE) &&
1234           (self->functions & OB_CLIENT_FUNC_RESIZE))) {
1235         self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1236         self->decorations &= ~OB_FRAME_DECOR_MAXIMIZE;
1237     }
1238
1239     /* kill the handle on fully maxed windows */
1240     if (self->max_vert && self->max_horz)
1241         self->decorations &= ~OB_FRAME_DECOR_HANDLE;
1242
1243     /* finally, the user can have requested no decorations, which overrides
1244        everything (but doesnt give it a border if it doesnt have one) */
1245     if (self->undecorated)
1246         self->decorations &= OB_FRAME_DECOR_BORDER;
1247
1248     /* if we don't have a titlebar, then we cannot shade! */
1249     if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
1250         self->functions &= ~OB_CLIENT_FUNC_SHADE;
1251
1252     /* now we need to check against rules for the client's current state */
1253     if (self->fullscreen) {
1254         self->functions &= (OB_CLIENT_FUNC_CLOSE |
1255                             OB_CLIENT_FUNC_FULLSCREEN |
1256                             OB_CLIENT_FUNC_ICONIFY);
1257         self->decorations = 0;
1258     }
1259
1260     client_change_allowed_actions(self);
1261
1262     if (self->frame) {
1263         /* adjust the client's decorations, etc. */
1264         client_reconfigure(self);
1265     }
1266 }
1267
1268 static void client_change_allowed_actions(ObClient *self)
1269 {
1270     guint32 actions[9];
1271     gint num = 0;
1272
1273     /* desktop windows are kept on all desktops */
1274     if (self->type != OB_CLIENT_TYPE_DESKTOP)
1275         actions[num++] = prop_atoms.net_wm_action_change_desktop;
1276
1277     if (self->functions & OB_CLIENT_FUNC_SHADE)
1278         actions[num++] = prop_atoms.net_wm_action_shade;
1279     if (self->functions & OB_CLIENT_FUNC_CLOSE)
1280         actions[num++] = prop_atoms.net_wm_action_close;
1281     if (self->functions & OB_CLIENT_FUNC_MOVE)
1282         actions[num++] = prop_atoms.net_wm_action_move;
1283     if (self->functions & OB_CLIENT_FUNC_ICONIFY)
1284         actions[num++] = prop_atoms.net_wm_action_minimize;
1285     if (self->functions & OB_CLIENT_FUNC_RESIZE)
1286         actions[num++] = prop_atoms.net_wm_action_resize;
1287     if (self->functions & OB_CLIENT_FUNC_FULLSCREEN)
1288         actions[num++] = prop_atoms.net_wm_action_fullscreen;
1289     if (self->functions & OB_CLIENT_FUNC_MAXIMIZE) {
1290         actions[num++] = prop_atoms.net_wm_action_maximize_horz;
1291         actions[num++] = prop_atoms.net_wm_action_maximize_vert;
1292     }
1293
1294     PROP_SETA32(self->window, net_wm_allowed_actions, atom, actions, num);
1295
1296     /* make sure the window isn't breaking any rules now */
1297
1298     if (!(self->functions & OB_CLIENT_FUNC_SHADE) && self->shaded) {
1299         if (self->frame) client_shade(self, FALSE);
1300         else self->shaded = FALSE;
1301     }
1302     if (!(self->functions & OB_CLIENT_FUNC_ICONIFY) && self->iconic) {
1303         if (self->frame) client_iconify(self, FALSE, TRUE);
1304         else self->iconic = FALSE;
1305     }
1306     if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) && self->fullscreen) {
1307         if (self->frame) client_fullscreen(self, FALSE, TRUE);
1308         else self->fullscreen = FALSE;
1309     }
1310     if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && (self->max_horz ||
1311                                                          self->max_vert)) {
1312         if (self->frame) client_maximize(self, FALSE, 0, TRUE);
1313         else self->max_vert = self->max_horz = FALSE;
1314     }
1315 }
1316
1317 void client_reconfigure(ObClient *self)
1318 {
1319     /* by making this pass FALSE for user, we avoid the emacs event storm where
1320        every configurenotify causes an update in its normal hints, i think this
1321        is generally what we want anyways... */
1322     client_configure(self, OB_CORNER_TOPLEFT, self->area.x, self->area.y,
1323                      self->area.width, self->area.height, FALSE, TRUE);
1324 }
1325
1326 void client_update_wmhints(ObClient *self)
1327 {
1328     XWMHints *hints;
1329     gboolean ur = FALSE;
1330     GSList *it;
1331
1332     /* assume a window takes input if it doesnt specify */
1333     self->can_focus = TRUE;
1334   
1335     if ((hints = XGetWMHints(ob_display, self->window)) != NULL) {
1336         if (hints->flags & InputHint)
1337             self->can_focus = hints->input;
1338
1339         /* only do this when first managing the window *AND* when we aren't
1340            starting up! */
1341         if (ob_state() != OB_STATE_STARTING && self->frame == NULL)
1342             if (hints->flags & StateHint)
1343                 self->iconic = hints->initial_state == IconicState;
1344
1345         if (hints->flags & XUrgencyHint)
1346             ur = TRUE;
1347
1348         if (!(hints->flags & WindowGroupHint))
1349             hints->window_group = None;
1350
1351         /* did the group state change? */
1352         if (hints->window_group !=
1353             (self->group ? self->group->leader : None)) {
1354             /* remove from the old group if there was one */
1355             if (self->group != NULL) {
1356                 /* remove transients of the group */
1357                 for (it = self->group->members; it; it = it->next)
1358                     self->transients = g_slist_remove(self->transients,
1359                                                       it->data);
1360
1361                 /* remove myself from parents in the group */
1362                 if (self->transient_for == OB_TRAN_GROUP) {
1363                     for (it = self->group->members; it; it = it->next) {
1364                         ObClient *c = it->data;
1365
1366                         if (c != self && !c->transient_for)
1367                             c->transients = g_slist_remove(c->transients,
1368                                                            self);
1369                     }
1370                 }
1371
1372                 group_remove(self->group, self);
1373                 self->group = NULL;
1374             }
1375             if (hints->window_group != None) {
1376                 self->group = group_add(hints->window_group, self);
1377
1378                 /* i can only have transients from the group if i am not
1379                    transient myself */
1380                 if (!self->transient_for) {
1381                     /* add other transients of the group that are already
1382                        set up */
1383                     for (it = self->group->members; it; it = it->next) {
1384                         ObClient *c = it->data;
1385                         if (c != self && c->transient_for == OB_TRAN_GROUP)
1386                             self->transients =
1387                                 g_slist_append(self->transients, c);
1388                     }
1389                 }
1390             }
1391
1392             /* because the self->transient flag wont change from this call,
1393                we don't need to update the window's type and such, only its
1394                transient_for, and the transients lists of other windows in
1395                the group may be affected */
1396             client_update_transient_for(self);
1397         }
1398
1399         /* the WM_HINTS can contain an icon */
1400         client_update_icons(self);
1401
1402         XFree(hints);
1403     }
1404
1405     if (ur != self->urgent) {
1406         self->urgent = ur;
1407         /* fire the urgent callback if we're mapped, otherwise, wait until
1408            after we're mapped */
1409         if (self->frame)
1410             client_urgent_notify(self);
1411     }
1412 }
1413
1414 void client_update_title(ObClient *self)
1415 {
1416     GList *it;
1417     guint32 nums;
1418     guint i;
1419     gchar *data = NULL;
1420     gboolean read_title;
1421     gchar *old_title;
1422
1423     old_title = self->title;
1424      
1425     /* try netwm */
1426     if (!PROP_GETS(self->window, net_wm_name, utf8, &data))
1427         /* try old x stuff */
1428         if (!PROP_GETS(self->window, wm_name, locale, &data))
1429             data = g_strdup("Unnamed Window");
1430
1431     /* did the title change? then reset the title_count */
1432     if (old_title && 0 != strncmp(old_title, data, strlen(data)))
1433         self->title_count = 1;
1434
1435     /* look for duplicates and append a number */
1436     nums = 0;
1437     for (it = client_list; it; it = it->next)
1438         if (it->data != self) {
1439             ObClient *c = it->data;
1440             if (0 == strncmp(c->title, data, strlen(data)))
1441                 nums |= 1 << c->title_count;
1442         }
1443     /* find first free number */
1444     for (i = 1; i <= 32; ++i)
1445         if (!(nums & (1 << i))) {
1446             if (self->title_count == 1 || i == 1)
1447                 self->title_count = i;
1448             break;
1449         }
1450     /* dont display the number for the first window */
1451     if (self->title_count > 1) {
1452         gchar *ndata;
1453         ndata = g_strdup_printf("%s - [%u]", data, self->title_count);
1454         g_free(data);
1455         data = ndata;
1456     }
1457
1458     PROP_SETS(self->window, net_wm_visible_name, data);
1459
1460     self->title = data;
1461
1462     if (self->frame)
1463         frame_adjust_title(self->frame);
1464
1465     g_free(old_title);
1466
1467     /* update the icon title */
1468     data = NULL;
1469     g_free(self->icon_title);
1470
1471     read_title = TRUE;
1472     /* try netwm */
1473     if (!PROP_GETS(self->window, net_wm_icon_name, utf8, &data))
1474         /* try old x stuff */
1475         if (!PROP_GETS(self->window, wm_icon_name, locale, &data)) {
1476             data = g_strdup(self->title);
1477             read_title = FALSE;
1478         }
1479
1480     /* append the title count, dont display the number for the first window */
1481     if (read_title && self->title_count > 1) {
1482         gchar *vdata, *ndata;
1483         ndata = g_strdup_printf(" - [%u]", self->title_count);
1484         vdata = g_strconcat(data, ndata, NULL);
1485         g_free(ndata);
1486         g_free(data);
1487         data = vdata;
1488     }
1489
1490     PROP_SETS(self->window, net_wm_visible_icon_name, data);
1491
1492     self->icon_title = data;
1493 }
1494
1495 void client_update_class(ObClient *self)
1496 {
1497     gchar **data;
1498     gchar *s;
1499
1500     if (self->name) g_free(self->name);
1501     if (self->class) g_free(self->class);
1502     if (self->role) g_free(self->role);
1503
1504     self->name = self->class = self->role = NULL;
1505
1506     if (PROP_GETSS(self->window, wm_class, locale, &data)) {
1507         if (data[0]) {
1508             self->name = g_strdup(data[0]);
1509             if (data[1])
1510                 self->class = g_strdup(data[1]);
1511         }
1512         g_strfreev(data);     
1513     }
1514
1515     if (PROP_GETS(self->window, wm_window_role, locale, &s))
1516         self->role = s;
1517
1518     if (self->name == NULL) self->name = g_strdup("");
1519     if (self->class == NULL) self->class = g_strdup("");
1520     if (self->role == NULL) self->role = g_strdup("");
1521 }
1522
1523 void client_update_strut(ObClient *self)
1524 {
1525     guint num;
1526     guint32 *data;
1527     gboolean got = FALSE;
1528     StrutPartial strut;
1529
1530     if (PROP_GETA32(self->window, net_wm_strut_partial, cardinal,
1531                     &data, &num)) {
1532         if (num == 12) {
1533             got = TRUE;
1534             STRUT_PARTIAL_SET(strut,
1535                               data[0], data[2], data[1], data[3],
1536                               data[4], data[5], data[8], data[9],
1537                               data[6], data[7], data[10], data[11]);
1538         }
1539         g_free(data);
1540     }
1541
1542     if (!got &&
1543         PROP_GETA32(self->window, net_wm_strut, cardinal, &data, &num)) {
1544         if (num == 4) {
1545             const Rect *a;
1546
1547             got = TRUE;
1548
1549             /* use the screen's width/height */
1550             a = screen_physical_area();
1551
1552             STRUT_PARTIAL_SET(strut,
1553                               data[0], data[2], data[1], data[3],
1554                               a->y, a->y + a->height - 1,
1555                               a->x, a->x + a->width - 1,
1556                               a->y, a->y + a->height - 1,
1557                               a->x, a->x + a->width - 1);
1558         }
1559         g_free(data);
1560     }
1561
1562     if (!got)
1563         STRUT_PARTIAL_SET(strut, 0, 0, 0, 0,
1564                           0, 0, 0, 0, 0, 0, 0, 0);
1565
1566     if (!STRUT_EQUAL(strut, self->strut)) {
1567         self->strut = strut;
1568
1569         /* updating here is pointless while we're being mapped cuz we're not in
1570            the client list yet */
1571         if (self->frame)
1572             screen_update_areas();
1573     }
1574 }
1575
1576 void client_update_icons(ObClient *self)
1577 {
1578     guint num;
1579     guint32 *data;
1580     guint w, h, i, j;
1581
1582     for (i = 0; i < self->nicons; ++i)
1583         g_free(self->icons[i].data);
1584     if (self->nicons > 0)
1585         g_free(self->icons);
1586     self->nicons = 0;
1587
1588     if (PROP_GETA32(self->window, net_wm_icon, cardinal, &data, &num)) {
1589         /* figure out how many valid icons are in here */
1590         i = 0;
1591         while (num - i > 2) {
1592             w = data[i++];
1593             h = data[i++];
1594             i += w * h;
1595             if (i > num || w*h == 0) break;
1596             ++self->nicons;
1597         }
1598
1599         self->icons = g_new(ObClientIcon, self->nicons);
1600     
1601         /* store the icons */
1602         i = 0;
1603         for (j = 0; j < self->nicons; ++j) {
1604             guint x, y, t;
1605
1606             w = self->icons[j].width = data[i++];
1607             h = self->icons[j].height = data[i++];
1608
1609             if (w*h == 0) continue;
1610
1611             self->icons[j].data = g_new(RrPixel32, w * h);
1612             for (x = 0, y = 0, t = 0; t < w * h; ++t, ++x, ++i) {
1613                 if (x >= w) {
1614                     x = 0;
1615                     ++y;
1616                 }
1617                 self->icons[j].data[t] =
1618                     (((data[i] >> 24) & 0xff) << RrDefaultAlphaOffset) +
1619                     (((data[i] >> 16) & 0xff) << RrDefaultRedOffset) +
1620                     (((data[i] >> 8) & 0xff) << RrDefaultGreenOffset) +
1621                     (((data[i] >> 0) & 0xff) << RrDefaultBlueOffset);
1622             }
1623             g_assert(i <= num);
1624         }
1625
1626         g_free(data);
1627     } else if (PROP_GETA32(self->window, kwm_win_icon,
1628                            kwm_win_icon, &data, &num)) {
1629         if (num == 2) {
1630             self->nicons++;
1631             self->icons = g_new(ObClientIcon, self->nicons);
1632             xerror_set_ignore(TRUE);
1633             if (!RrPixmapToRGBA(ob_rr_inst,
1634                                 data[0], data[1],
1635                                 &self->icons[self->nicons-1].width,
1636                                 &self->icons[self->nicons-1].height,
1637                                 &self->icons[self->nicons-1].data)) {
1638                 g_free(&self->icons[self->nicons-1]);
1639                 self->nicons--;
1640             }
1641             xerror_set_ignore(FALSE);
1642         }
1643         g_free(data);
1644     } else {
1645         XWMHints *hints;
1646
1647         if ((hints = XGetWMHints(ob_display, self->window))) {
1648             if (hints->flags & IconPixmapHint) {
1649                 self->nicons++;
1650                 self->icons = g_new(ObClientIcon, self->nicons);
1651                 xerror_set_ignore(TRUE);
1652                 if (!RrPixmapToRGBA(ob_rr_inst,
1653                                     hints->icon_pixmap,
1654                                     (hints->flags & IconMaskHint ?
1655                                      hints->icon_mask : None),
1656                                     &self->icons[self->nicons-1].width,
1657                                     &self->icons[self->nicons-1].height,
1658                                     &self->icons[self->nicons-1].data)){
1659                     g_free(&self->icons[self->nicons-1]);
1660                     self->nicons--;
1661                 }
1662                 xerror_set_ignore(FALSE);
1663             }
1664             XFree(hints);
1665         }
1666     }
1667
1668     if (self->frame)
1669         frame_adjust_icon(self->frame);
1670 }
1671
1672 static void client_change_state(ObClient *self)
1673 {
1674     guint32 state[2];
1675     guint32 netstate[11];
1676     guint num;
1677
1678     state[0] = self->wmstate;
1679     state[1] = None;
1680     PROP_SETA32(self->window, wm_state, wm_state, state, 2);
1681
1682     num = 0;
1683     if (self->modal)
1684         netstate[num++] = prop_atoms.net_wm_state_modal;
1685     if (self->shaded)
1686         netstate[num++] = prop_atoms.net_wm_state_shaded;
1687     if (self->iconic)
1688         netstate[num++] = prop_atoms.net_wm_state_hidden;
1689     if (self->skip_taskbar)
1690         netstate[num++] = prop_atoms.net_wm_state_skip_taskbar;
1691     if (self->skip_pager)
1692         netstate[num++] = prop_atoms.net_wm_state_skip_pager;
1693     if (self->fullscreen)
1694         netstate[num++] = prop_atoms.net_wm_state_fullscreen;
1695     if (self->max_vert)
1696         netstate[num++] = prop_atoms.net_wm_state_maximized_vert;
1697     if (self->max_horz)
1698         netstate[num++] = prop_atoms.net_wm_state_maximized_horz;
1699     if (self->above)
1700         netstate[num++] = prop_atoms.net_wm_state_above;
1701     if (self->below)
1702         netstate[num++] = prop_atoms.net_wm_state_below;
1703     if (self->undecorated)
1704         netstate[num++] = prop_atoms.ob_wm_state_undecorated;
1705     PROP_SETA32(self->window, net_wm_state, atom, netstate, num);
1706
1707     client_calc_layer(self);
1708
1709     if (self->frame)
1710         frame_adjust_state(self->frame);
1711 }
1712
1713 ObClient *client_search_focus_tree(ObClient *self)
1714 {
1715     GSList *it;
1716     ObClient *ret;
1717
1718     for (it = self->transients; it != NULL; it = it->next) {
1719         if (client_focused(it->data)) return it->data;
1720         if ((ret = client_search_focus_tree(it->data))) return ret;
1721     }
1722     return NULL;
1723 }
1724
1725 ObClient *client_search_focus_tree_full(ObClient *self)
1726 {
1727     if (self->transient_for) {
1728         if (self->transient_for != OB_TRAN_GROUP) {
1729             return client_search_focus_tree_full(self->transient_for);
1730         } else {
1731             GSList *it;
1732             gboolean recursed = FALSE;
1733         
1734             for (it = self->group->members; it; it = it->next)
1735                 if (!((ObClient*)it->data)->transient_for) {
1736                     ObClient *c;
1737                     if ((c = client_search_focus_tree_full(it->data)))
1738                         return c;
1739                     recursed = TRUE;
1740                 }
1741             if (recursed)
1742                 return NULL;
1743         }
1744     }
1745
1746     /* this function checks the whole tree, the client_search_focus_tree~
1747        does not, so we need to check this window */
1748     if (client_focused(self))
1749         return self;
1750     return client_search_focus_tree(self);
1751 }
1752
1753 static ObStackingLayer calc_layer(ObClient *self)
1754 {
1755     ObStackingLayer l;
1756
1757     if (self->fullscreen &&
1758         (client_focused(self) || client_search_focus_tree(self)))
1759         l = OB_STACKING_LAYER_FULLSCREEN;
1760     else if (self->type == OB_CLIENT_TYPE_DESKTOP)
1761         l = OB_STACKING_LAYER_DESKTOP;
1762     else if (self->type == OB_CLIENT_TYPE_DOCK) {
1763         if (self->above) l = OB_STACKING_LAYER_DOCK_ABOVE;
1764         else if (self->below) l = OB_STACKING_LAYER_DOCK_BELOW;
1765         else l = OB_STACKING_LAYER_NORMAL;
1766     }
1767     else if (self->above) l = OB_STACKING_LAYER_ABOVE;
1768     else if (self->below) l = OB_STACKING_LAYER_BELOW;
1769     else l = OB_STACKING_LAYER_NORMAL;
1770
1771     return l;
1772 }
1773
1774 static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
1775                                         ObStackingLayer l, gboolean raised)
1776 {
1777     ObStackingLayer old, own;
1778     GSList *it;
1779
1780     old = self->layer;
1781     own = calc_layer(self);
1782     self->layer = l > own ? l : own;
1783
1784     for (it = self->transients; it; it = it->next)
1785         client_calc_layer_recursive(it->data, orig,
1786                                     l, raised ? raised : l != old);
1787
1788     if (!raised && l != old)
1789         if (orig->frame) { /* only restack if the original window is managed */
1790             stacking_remove(CLIENT_AS_WINDOW(self));
1791             stacking_add(CLIENT_AS_WINDOW(self));
1792         }
1793 }
1794
1795 void client_calc_layer(ObClient *self)
1796 {
1797     ObStackingLayer l;
1798     ObClient *orig;
1799
1800     orig = self;
1801
1802     /* transients take on the layer of their parents */
1803     self = client_search_top_transient(self);
1804
1805     l = calc_layer(self);
1806
1807     client_calc_layer_recursive(self, orig, l, FALSE);
1808 }
1809
1810 gboolean client_should_show(ObClient *self)
1811 {
1812     if (self->iconic)
1813         return FALSE;
1814     if (client_normal(self) && screen_showing_desktop)
1815         return FALSE;
1816     /*
1817     if (self->transient_for) {
1818         if (self->transient_for != OB_TRAN_GROUP)
1819             return client_should_show(self->transient_for);
1820         else {
1821             GSList *it;
1822
1823             for (it = self->group->members; it; it = g_slist_next(it)) {
1824                 ObClient *c = it->data;
1825                 if (c != self && !c->transient_for) {
1826                     if (client_should_show(c))
1827                         return TRUE;
1828                 }
1829             }
1830         }
1831     }
1832     */
1833     if (self->desktop == screen_desktop || self->desktop == DESKTOP_ALL)
1834         return TRUE;
1835     
1836     return FALSE;
1837 }
1838
1839 static void client_showhide(ObClient *self)
1840 {
1841
1842     if (client_should_show(self))
1843         frame_show(self->frame);
1844     else
1845         frame_hide(self->frame);
1846 }
1847
1848 gboolean client_normal(ObClient *self) {
1849     return ! (self->type == OB_CLIENT_TYPE_DESKTOP ||
1850               self->type == OB_CLIENT_TYPE_DOCK ||
1851               self->type == OB_CLIENT_TYPE_SPLASH);
1852 }
1853
1854 static void client_apply_startup_state(ObClient *self)
1855 {
1856     /* these are in a carefully crafted order.. */
1857
1858     if (self->iconic) {
1859         self->iconic = FALSE;
1860         client_iconify(self, TRUE, FALSE);
1861     }
1862     if (self->fullscreen) {
1863         self->fullscreen = FALSE;
1864         client_fullscreen(self, TRUE, FALSE);
1865     }
1866     if (self->undecorated) {
1867         self->undecorated = FALSE;
1868         client_set_undecorated(self, TRUE);
1869     }
1870     if (self->shaded) {
1871         self->shaded = FALSE;
1872         client_shade(self, TRUE);
1873     }
1874     if (self->urgent)
1875         client_urgent_notify(self);
1876   
1877     if (self->max_vert && self->max_horz) {
1878         self->max_vert = self->max_horz = FALSE;
1879         client_maximize(self, TRUE, 0, FALSE);
1880     } else if (self->max_vert) {
1881         self->max_vert = FALSE;
1882         client_maximize(self, TRUE, 2, FALSE);
1883     } else if (self->max_horz) {
1884         self->max_horz = FALSE;
1885         client_maximize(self, TRUE, 1, FALSE);
1886     }
1887
1888     /* nothing to do for the other states:
1889        skip_taskbar
1890        skip_pager
1891        modal
1892        above
1893        below
1894     */
1895 }
1896
1897 void client_configure_full(ObClient *self, ObCorner anchor,
1898                            gint x, gint y, gint w, gint h,
1899                            gboolean user, gboolean final,
1900                            gboolean force_reply)
1901 {
1902     gint oldw, oldh;
1903     gboolean send_resize_client;
1904     gboolean moved = FALSE, resized = FALSE;
1905     guint fdecor = self->frame->decorations;
1906     gboolean fhorz = self->frame->max_horz;
1907
1908     /* make the frame recalculate its dimentions n shit without changing
1909        anything visible for real, this way the constraints below can work with
1910        the updated frame dimensions. */
1911     frame_adjust_area(self->frame, TRUE, TRUE, TRUE);
1912
1913     /* gets the frame's position */
1914     frame_client_gravity(self->frame, &x, &y);
1915
1916     /* these positions are frame positions, not client positions */
1917
1918     /* set the size and position if fullscreen */
1919     if (self->fullscreen) {
1920 #ifdef VIDMODE
1921         gint dot;
1922         XF86VidModeModeLine mode;
1923 #endif
1924         Rect *a;
1925         guint i;
1926
1927         i = client_monitor(self);
1928         a = screen_physical_area_monitor(i);
1929
1930 #ifdef VIDMODE
1931         if (i == 0 && /* primary head */
1932             extensions_vidmode &&
1933             XF86VidModeGetViewPort(ob_display, ob_screen, &x, &y) &&
1934             /* get the mode last so the mode.privsize isnt freed incorrectly */
1935             XF86VidModeGetModeLine(ob_display, ob_screen, &dot, &mode)) {
1936             x += a->x;
1937             y += a->y;
1938             w = mode.hdisplay;
1939             h = mode.vdisplay;
1940             if (mode.privsize) XFree(mode.private);
1941         } else
1942 #endif
1943         {
1944             x = a->x;
1945             y = a->y;
1946             w = a->width;
1947             h = a->height;
1948         }
1949
1950         user = FALSE; /* ignore that increment etc shit when in fullscreen */
1951     } else {
1952         Rect *a;
1953
1954         a = screen_area_monitor(self->desktop, client_monitor(self));
1955
1956         /* set the size and position if maximized */
1957         if (self->max_horz) {
1958             x = a->x;
1959             w = a->width - self->frame->size.left - self->frame->size.right;
1960         }
1961         if (self->max_vert) {
1962             y = a->y;
1963             h = a->height - self->frame->size.top - self->frame->size.bottom;
1964         }
1965     }
1966
1967     /* gets the client's position */
1968     frame_frame_gravity(self->frame, &x, &y);
1969
1970     /* these override the above states! if you cant move you can't move! */
1971     if (user) {
1972         if (!(self->functions & OB_CLIENT_FUNC_MOVE)) {
1973             x = self->area.x;
1974             y = self->area.y;
1975         }
1976         if (!(self->functions & OB_CLIENT_FUNC_RESIZE)) {
1977             w = self->area.width;
1978             h = self->area.height;
1979         }
1980     }
1981
1982     if (!(w == self->area.width && h == self->area.height)) {
1983         gint basew, baseh, minw, minh;
1984
1985         /* base size is substituted with min size if not specified */
1986         if (self->base_size.width || self->base_size.height) {
1987             basew = self->base_size.width;
1988             baseh = self->base_size.height;
1989         } else {
1990             basew = self->min_size.width;
1991             baseh = self->min_size.height;
1992         }
1993         /* min size is substituted with base size if not specified */
1994         if (self->min_size.width || self->min_size.height) {
1995             minw = self->min_size.width;
1996             minh = self->min_size.height;
1997         } else {
1998             minw = self->base_size.width;
1999             minh = self->base_size.height;
2000         }
2001
2002         /* if this is a user-requested resize, then check against min/max
2003            sizes */
2004
2005         /* smaller than min size or bigger than max size? */
2006         if (w > self->max_size.width) w = self->max_size.width;
2007         if (w < minw) w = minw;
2008         if (h > self->max_size.height) h = self->max_size.height;
2009         if (h < minh) h = minh;
2010
2011         w -= basew;
2012         h -= baseh;
2013
2014         /* keep to the increments */
2015         w /= self->size_inc.width;
2016         h /= self->size_inc.height;
2017
2018         /* you cannot resize to nothing */
2019         if (basew + w < 1) w = 1 - basew;
2020         if (baseh + h < 1) h = 1 - baseh;
2021   
2022         /* store the logical size */
2023         SIZE_SET(self->logical_size,
2024                  self->size_inc.width > 1 ? w : w + basew,
2025                  self->size_inc.height > 1 ? h : h + baseh);
2026
2027         w *= self->size_inc.width;
2028         h *= self->size_inc.height;
2029
2030         w += basew;
2031         h += baseh;
2032
2033         /* adjust the height to match the width for the aspect ratios.
2034            for this, min size is not substituted for base size ever. */
2035         w -= self->base_size.width;
2036         h -= self->base_size.height;
2037
2038         if (self->min_ratio)
2039             if (h * self->min_ratio > w) {
2040                 h = (gint)(w / self->min_ratio);
2041
2042                 /* you cannot resize to nothing */
2043                 if (h < 1) {
2044                     h = 1;
2045                     w = (gint)(h * self->min_ratio);
2046                 }
2047             }
2048         if (self->max_ratio)
2049             if (h * self->max_ratio < w) {
2050                 h = (gint)(w / self->max_ratio);
2051
2052                 /* you cannot resize to nothing */
2053                 if (h < 1) {
2054                     h = 1;
2055                     w = (gint)(h * self->min_ratio);
2056                 }
2057             }
2058
2059         w += self->base_size.width;
2060         h += self->base_size.height;
2061     }
2062
2063     g_assert(w > 0);
2064     g_assert(h > 0);
2065
2066     switch (anchor) {
2067     case OB_CORNER_TOPLEFT:
2068         break;
2069     case OB_CORNER_TOPRIGHT:
2070         x -= w - self->area.width;
2071         break;
2072     case OB_CORNER_BOTTOMLEFT:
2073         y -= h - self->area.height;
2074         break;
2075     case OB_CORNER_BOTTOMRIGHT:
2076         x -= w - self->area.width;
2077         y -= h - self->area.height;
2078         break;
2079     }
2080
2081     moved = x != self->area.x || y != self->area.y;
2082     resized = w != self->area.width || h != self->area.height;
2083
2084     oldw = self->area.width;
2085     oldh = self->area.height;
2086     RECT_SET(self->area, x, y, w, h);
2087
2088     /* for app-requested resizes, always resize if 'resized' is true.
2089        for user-requested ones, only resize if final is true, or when
2090        resizing in redraw mode */
2091     send_resize_client = ((!user && resized) ||
2092                           (user && (final ||
2093                                     (resized && config_redraw_resize))));
2094
2095     /* if the client is enlarging, the resize the client before the frame */
2096     if (send_resize_client && user && (w > oldw || h > oldh))
2097         XResizeWindow(ob_display, self->window, MAX(w, oldw), MAX(h, oldh));
2098
2099     /* move/resize the frame to match the request */
2100     if (self->frame) {
2101         if (self->decorations != fdecor || self->max_horz != fhorz)
2102             moved = resized = TRUE;
2103
2104         if (moved || resized)
2105             frame_adjust_area(self->frame, moved, resized, FALSE);
2106
2107         if (!resized && (force_reply || ((!user && moved) || (user && final))))
2108         {
2109             XEvent event;
2110             event.type = ConfigureNotify;
2111             event.xconfigure.display = ob_display;
2112             event.xconfigure.event = self->window;
2113             event.xconfigure.window = self->window;
2114
2115             /* root window real coords */
2116             event.xconfigure.x = self->frame->area.x + self->frame->size.left -
2117                 self->border_width;
2118             event.xconfigure.y = self->frame->area.y + self->frame->size.top -
2119                 self->border_width;
2120             event.xconfigure.width = w;
2121             event.xconfigure.height = h;
2122             event.xconfigure.border_width = 0;
2123             event.xconfigure.above = self->frame->plate;
2124             event.xconfigure.override_redirect = FALSE;
2125             XSendEvent(event.xconfigure.display, event.xconfigure.window,
2126                        FALSE, StructureNotifyMask, &event);
2127         }
2128     }
2129
2130     /* if the client is shrinking, then resize the frame before the client */
2131     if (send_resize_client && (!user || (w <= oldw || h <= oldh)))
2132         XResizeWindow(ob_display, self->window, w, h);
2133
2134     XFlush(ob_display);
2135 }
2136
2137 void client_fullscreen(ObClient *self, gboolean fs, gboolean savearea)
2138 {
2139     gint x, y, w, h;
2140
2141     if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) || /* can't */
2142         self->fullscreen == fs) return;                   /* already done */
2143
2144     self->fullscreen = fs;
2145     client_change_state(self); /* change the state hints on the client,
2146                                   and adjust out layer/stacking */
2147
2148     if (fs) {
2149         if (savearea)
2150             self->pre_fullscreen_area = self->area;
2151
2152         /* these are not actually used cuz client_configure will set them
2153            as appropriate when the window is fullscreened */
2154         x = y = w = h = 0;
2155     } else {
2156         Rect *a;
2157
2158         if (self->pre_fullscreen_area.width > 0 &&
2159             self->pre_fullscreen_area.height > 0)
2160         {
2161             x = self->pre_fullscreen_area.x;
2162             y = self->pre_fullscreen_area.y;
2163             w = self->pre_fullscreen_area.width;
2164             h = self->pre_fullscreen_area.height;
2165             RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0);
2166         } else {
2167             /* pick some fallbacks... */
2168             a = screen_area_monitor(self->desktop, 0);
2169             x = a->x + a->width / 4;
2170             y = a->y + a->height / 4;
2171             w = a->width / 2;
2172             h = a->height / 2;
2173         }
2174     }
2175
2176     client_setup_decor_and_functions(self);
2177
2178     client_move_resize(self, x, y, w, h);
2179
2180     /* try focus us when we go into fullscreen mode */
2181     client_focus(self);
2182 }
2183
2184 static void client_iconify_recursive(ObClient *self,
2185                                      gboolean iconic, gboolean curdesk)
2186 {
2187     GSList *it;
2188     gboolean changed = FALSE;
2189
2190
2191     if (self->iconic != iconic) {
2192         ob_debug("%sconifying window: 0x%lx\n", (iconic ? "I" : "Uni"),
2193                  self->window);
2194
2195         self->iconic = iconic;
2196
2197         if (iconic) {
2198             if (self->functions & OB_CLIENT_FUNC_ICONIFY) {
2199                 glong old;
2200
2201                 old = self->wmstate;
2202                 self->wmstate = IconicState;
2203                 if (old != self->wmstate)
2204                     PROP_MSG(self->window, kde_wm_change_state,
2205                              self->wmstate, 1, 0, 0);
2206
2207                 /* update the focus lists.. iconic windows go to the bottom of
2208                    the list, put the new iconic window at the 'top of the
2209                    bottom'. */
2210                 focus_order_to_top(self);
2211
2212                 changed = TRUE;
2213             }
2214         } else {
2215             glong old;
2216
2217             if (curdesk)
2218                 client_set_desktop(self, screen_desktop, FALSE);
2219
2220             old = self->wmstate;
2221             self->wmstate = self->shaded ? IconicState : NormalState;
2222             if (old != self->wmstate)
2223                 PROP_MSG(self->window, kde_wm_change_state,
2224                          self->wmstate, 1, 0, 0);
2225
2226             /* this puts it after the current focused window */
2227             focus_order_remove(self);
2228             focus_order_add_new(self);
2229
2230             /* this is here cuz with the VIDMODE extension, the viewport can
2231                change while a fullscreen window is iconic, and when it
2232                uniconifies, it would be nice if it did so to the new position
2233                of the viewport */
2234             client_reconfigure(self);
2235
2236             changed = TRUE;
2237         }
2238     }
2239
2240     if (changed) {
2241         client_change_state(self);
2242         client_showhide(self);
2243         screen_update_areas();
2244     }
2245
2246     /* iconify all transients */
2247     for (it = self->transients; it != NULL; it = it->next)
2248         if (it->data != self) client_iconify_recursive(it->data,
2249                                                        iconic, curdesk);
2250 }
2251
2252 void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk)
2253 {
2254     /* move up the transient chain as far as possible first */
2255     self = client_search_top_transient(self);
2256
2257     client_iconify_recursive(client_search_top_transient(self),
2258                              iconic, curdesk);
2259 }
2260
2261 void client_maximize(ObClient *self, gboolean max, gint dir, gboolean savearea)
2262 {
2263     gint x, y, w, h;
2264      
2265     g_assert(dir == 0 || dir == 1 || dir == 2);
2266     if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE)) return; /* can't */
2267
2268     /* check if already done */
2269     if (max) {
2270         if (dir == 0 && self->max_horz && self->max_vert) return;
2271         if (dir == 1 && self->max_horz) return;
2272         if (dir == 2 && self->max_vert) return;
2273     } else {
2274         if (dir == 0 && !self->max_horz && !self->max_vert) return;
2275         if (dir == 1 && !self->max_horz) return;
2276         if (dir == 2 && !self->max_vert) return;
2277     }
2278
2279     /* we just tell it to configure in the same place and client_configure
2280        worries about filling the screen with the window */
2281     x = self->area.x;
2282     y = self->area.y;
2283     w = self->area.width;
2284     h = self->area.height;
2285
2286     if (max) {
2287         if (savearea) {
2288             if ((dir == 0 || dir == 1) && !self->max_horz) { /* horz */
2289                 RECT_SET(self->pre_max_area,
2290                          self->area.x, self->pre_max_area.y,
2291                          self->area.width, self->pre_max_area.height);
2292             }
2293             if ((dir == 0 || dir == 2) && !self->max_vert) { /* vert */
2294                 RECT_SET(self->pre_max_area,
2295                          self->pre_max_area.x, self->area.y,
2296                          self->pre_max_area.width, self->area.height);
2297             }
2298         }
2299     } else {
2300         Rect *a;
2301
2302         a = screen_area_monitor(self->desktop, 0);
2303         if ((dir == 0 || dir == 1) && self->max_horz) { /* horz */
2304             if (self->pre_max_area.width > 0) {
2305                 x = self->pre_max_area.x;
2306                 w = self->pre_max_area.width;
2307
2308                 RECT_SET(self->pre_max_area, 0, self->pre_max_area.y,
2309                          0, self->pre_max_area.height);
2310             } else {
2311                 /* pick some fallbacks... */
2312                 x = a->x + a->width / 4;
2313                 w = a->width / 2;
2314             }
2315         }
2316         if ((dir == 0 || dir == 2) && self->max_vert) { /* vert */
2317             if (self->pre_max_area.height > 0) {
2318                 y = self->pre_max_area.y;
2319                 h = self->pre_max_area.height;
2320
2321                 RECT_SET(self->pre_max_area, self->pre_max_area.x, 0,
2322                          self->pre_max_area.width, 0);
2323             } else {
2324                 /* pick some fallbacks... */
2325                 y = a->y + a->height / 4;
2326                 h = a->height / 2;
2327             }
2328         }
2329     }
2330
2331     if (dir == 0 || dir == 1) /* horz */
2332         self->max_horz = max;
2333     if (dir == 0 || dir == 2) /* vert */
2334         self->max_vert = max;
2335
2336     client_change_state(self); /* change the state hints on the client */
2337
2338     client_setup_decor_and_functions(self);
2339
2340     client_move_resize(self, x, y, w, h);
2341 }
2342
2343 void client_shade(ObClient *self, gboolean shade)
2344 {
2345     if ((!(self->functions & OB_CLIENT_FUNC_SHADE) &&
2346          shade) ||                         /* can't shade */
2347         self->shaded == shade) return;     /* already done */
2348
2349     /* when we're iconic, don't change the wmstate */
2350     if (!self->iconic) {
2351         glong old;
2352
2353         old = self->wmstate;
2354         self->wmstate = shade ? IconicState : NormalState;
2355         if (old != self->wmstate)
2356             PROP_MSG(self->window, kde_wm_change_state,
2357                      self->wmstate, 1, 0, 0);
2358     }
2359
2360     self->shaded = shade;
2361     client_change_state(self);
2362     /* resize the frame to just the titlebar */
2363     frame_adjust_area(self->frame, FALSE, FALSE, FALSE);
2364 }
2365
2366 void client_close(ObClient *self)
2367 {
2368     XEvent ce;
2369
2370     if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return;
2371
2372     /* in the case that the client provides no means to requesting that it
2373        close, we just kill it */
2374     if (!self->delete_window)
2375         client_kill(self);
2376     
2377     /*
2378       XXX: itd be cool to do timeouts and shit here for killing the client's
2379       process off
2380       like... if the window is around after 5 seconds, then the close button
2381       turns a nice red, and if this function is called again, the client is
2382       explicitly killed.
2383     */
2384
2385     ce.xclient.type = ClientMessage;
2386     ce.xclient.message_type =  prop_atoms.wm_protocols;
2387     ce.xclient.display = ob_display;
2388     ce.xclient.window = self->window;
2389     ce.xclient.format = 32;
2390     ce.xclient.data.l[0] = prop_atoms.wm_delete_window;
2391     ce.xclient.data.l[1] = event_lasttime;
2392     ce.xclient.data.l[2] = 0l;
2393     ce.xclient.data.l[3] = 0l;
2394     ce.xclient.data.l[4] = 0l;
2395     XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
2396 }
2397
2398 void client_kill(ObClient *self)
2399 {
2400     XKillClient(ob_display, self->window);
2401 }
2402
2403 void client_set_desktop_recursive(ObClient *self,
2404                                   guint target, gboolean donthide)
2405 {
2406     guint old;
2407     GSList *it;
2408
2409     if (target != self->desktop) {
2410
2411         ob_debug("Setting desktop %u\n", target+1);
2412
2413         g_assert(target < screen_num_desktops || target == DESKTOP_ALL);
2414
2415         /* remove from the old desktop(s) */
2416         focus_order_remove(self);
2417
2418         old = self->desktop;
2419         self->desktop = target;
2420         PROP_SET32(self->window, net_wm_desktop, cardinal, target);
2421         /* the frame can display the current desktop state */
2422         frame_adjust_state(self->frame);
2423         /* 'move' the window to the new desktop */
2424         if (!donthide)
2425             client_showhide(self);
2426         /* raise if it was not already on the desktop */
2427         if (old != DESKTOP_ALL)
2428             client_raise(self);
2429         screen_update_areas();
2430
2431         /* add to the new desktop(s) */
2432         if (config_focus_new)
2433             focus_order_to_top(self);
2434         else
2435             focus_order_to_bottom(self);
2436     }
2437
2438     /* move all transients */
2439     for (it = self->transients; it != NULL; it = it->next)
2440         if (it->data != self) client_set_desktop_recursive(it->data,
2441                                                            target, donthide);
2442 }
2443
2444 void client_set_desktop(ObClient *self, guint target, gboolean donthide)
2445 {
2446     client_set_desktop_recursive(client_search_top_transient(self),
2447                                  target, donthide);
2448 }
2449
2450 ObClient *client_search_modal_child(ObClient *self)
2451 {
2452     GSList *it;
2453     ObClient *ret;
2454   
2455     for (it = self->transients; it != NULL; it = it->next) {
2456         ObClient *c = it->data;
2457         if ((ret = client_search_modal_child(c))) return ret;
2458         if (c->modal) return c;
2459     }
2460     return NULL;
2461 }
2462
2463 gboolean client_validate(ObClient *self)
2464 {
2465     XEvent e; 
2466
2467     XSync(ob_display, FALSE); /* get all events on the server */
2468
2469     if (XCheckTypedWindowEvent(ob_display, self->window, DestroyNotify, &e) ||
2470         XCheckTypedWindowEvent(ob_display, self->window, UnmapNotify, &e)) {
2471         XPutBackEvent(ob_display, &e);
2472         return FALSE;
2473     }
2474
2475     return TRUE;
2476 }
2477
2478 void client_set_wm_state(ObClient *self, glong state)
2479 {
2480     if (state == self->wmstate) return; /* no change */
2481   
2482     switch (state) {
2483     case IconicState:
2484         client_iconify(self, TRUE, TRUE);
2485         break;
2486     case NormalState:
2487         client_iconify(self, FALSE, TRUE);
2488         break;
2489     }
2490 }
2491
2492 void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
2493 {
2494     gboolean shaded = self->shaded;
2495     gboolean fullscreen = self->fullscreen;
2496     gboolean undecorated = self->undecorated;
2497     gboolean max_horz = self->max_horz;
2498     gboolean max_vert = self->max_vert;
2499     gboolean modal = self->modal;
2500     gint i;
2501
2502     if (!(action == prop_atoms.net_wm_state_add ||
2503           action == prop_atoms.net_wm_state_remove ||
2504           action == prop_atoms.net_wm_state_toggle))
2505         /* an invalid action was passed to the client message, ignore it */
2506         return; 
2507
2508     for (i = 0; i < 2; ++i) {
2509         Atom state = i == 0 ? data1 : data2;
2510     
2511         if (!state) continue;
2512
2513         /* if toggling, then pick whether we're adding or removing */
2514         if (action == prop_atoms.net_wm_state_toggle) {
2515             if (state == prop_atoms.net_wm_state_modal)
2516                 action = modal ? prop_atoms.net_wm_state_remove :
2517                     prop_atoms.net_wm_state_add;
2518             else if (state == prop_atoms.net_wm_state_maximized_vert)
2519                 action = self->max_vert ? prop_atoms.net_wm_state_remove :
2520                     prop_atoms.net_wm_state_add;
2521             else if (state == prop_atoms.net_wm_state_maximized_horz)
2522                 action = self->max_horz ? prop_atoms.net_wm_state_remove :
2523                     prop_atoms.net_wm_state_add;
2524             else if (state == prop_atoms.net_wm_state_shaded)
2525                 action = shaded ? prop_atoms.net_wm_state_remove :
2526                     prop_atoms.net_wm_state_add;
2527             else if (state == prop_atoms.net_wm_state_skip_taskbar)
2528                 action = self->skip_taskbar ?
2529                     prop_atoms.net_wm_state_remove :
2530                     prop_atoms.net_wm_state_add;
2531             else if (state == prop_atoms.net_wm_state_skip_pager)
2532                 action = self->skip_pager ?
2533                     prop_atoms.net_wm_state_remove :
2534                     prop_atoms.net_wm_state_add;
2535             else if (state == prop_atoms.net_wm_state_fullscreen)
2536                 action = fullscreen ?
2537                     prop_atoms.net_wm_state_remove :
2538                     prop_atoms.net_wm_state_add;
2539             else if (state == prop_atoms.net_wm_state_above)
2540                 action = self->above ? prop_atoms.net_wm_state_remove :
2541                     prop_atoms.net_wm_state_add;
2542             else if (state == prop_atoms.net_wm_state_below)
2543                 action = self->below ? prop_atoms.net_wm_state_remove :
2544                     prop_atoms.net_wm_state_add;
2545             else if (state == prop_atoms.ob_wm_state_undecorated)
2546                 action = undecorated ? prop_atoms.net_wm_state_remove :
2547                     prop_atoms.net_wm_state_add;
2548         }
2549     
2550         if (action == prop_atoms.net_wm_state_add) {
2551             if (state == prop_atoms.net_wm_state_modal) {
2552                 modal = TRUE;
2553             } else if (state == prop_atoms.net_wm_state_maximized_vert) {
2554                 max_vert = TRUE;
2555             } else if (state == prop_atoms.net_wm_state_maximized_horz) {
2556                 max_horz = TRUE;
2557             } else if (state == prop_atoms.net_wm_state_shaded) {
2558                 shaded = TRUE;
2559             } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
2560                 self->skip_taskbar = TRUE;
2561             } else if (state == prop_atoms.net_wm_state_skip_pager) {
2562                 self->skip_pager = TRUE;
2563             } else if (state == prop_atoms.net_wm_state_fullscreen) {
2564                 fullscreen = TRUE;
2565             } else if (state == prop_atoms.net_wm_state_above) {
2566                 self->above = TRUE;
2567             } else if (state == prop_atoms.net_wm_state_below) {
2568                 self->below = TRUE;
2569             } else if (state == prop_atoms.ob_wm_state_undecorated) {
2570                 undecorated = TRUE;
2571             }
2572
2573         } else { /* action == prop_atoms.net_wm_state_remove */
2574             if (state == prop_atoms.net_wm_state_modal) {
2575                 modal = FALSE;
2576             } else if (state == prop_atoms.net_wm_state_maximized_vert) {
2577                 max_vert = FALSE;
2578             } else if (state == prop_atoms.net_wm_state_maximized_horz) {
2579                 max_horz = FALSE;
2580             } else if (state == prop_atoms.net_wm_state_shaded) {
2581                 shaded = FALSE;
2582             } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
2583                 self->skip_taskbar = FALSE;
2584             } else if (state == prop_atoms.net_wm_state_skip_pager) {
2585                 self->skip_pager = FALSE;
2586             } else if (state == prop_atoms.net_wm_state_fullscreen) {
2587                 fullscreen = FALSE;
2588             } else if (state == prop_atoms.net_wm_state_above) {
2589                 self->above = FALSE;
2590             } else if (state == prop_atoms.net_wm_state_below) {
2591                 self->below = FALSE;
2592             } else if (state == prop_atoms.ob_wm_state_undecorated) {
2593                 undecorated = FALSE;
2594             }
2595         }
2596     }
2597     if (max_horz != self->max_horz || max_vert != self->max_vert) {
2598         if (max_horz != self->max_horz && max_vert != self->max_vert) {
2599             /* toggling both */
2600             if (max_horz == max_vert) { /* both going the same way */
2601                 client_maximize(self, max_horz, 0, TRUE);
2602             } else {
2603                 client_maximize(self, max_horz, 1, TRUE);
2604                 client_maximize(self, max_vert, 2, TRUE);
2605             }
2606         } else {
2607             /* toggling one */
2608             if (max_horz != self->max_horz)
2609                 client_maximize(self, max_horz, 1, TRUE);
2610             else
2611                 client_maximize(self, max_vert, 2, TRUE);
2612         }
2613     }
2614     /* change fullscreen state before shading, as it will affect if the window
2615        can shade or not */
2616     if (fullscreen != self->fullscreen)
2617         client_fullscreen(self, fullscreen, TRUE);
2618     if (shaded != self->shaded)
2619         client_shade(self, shaded);
2620     if (undecorated != self->undecorated)
2621         client_set_undecorated(self, undecorated);
2622     if (modal != self->modal) {
2623         self->modal = modal;
2624         /* when a window changes modality, then its stacking order with its
2625            transients needs to change */
2626         client_raise(self);
2627     }
2628     client_calc_layer(self);
2629     client_change_state(self); /* change the hint to reflect these changes */
2630 }
2631
2632 ObClient *client_focus_target(ObClient *self)
2633 {
2634     ObClient *child;
2635      
2636     /* if we have a modal child, then focus it, not us */
2637     child = client_search_modal_child(client_search_top_transient(self));
2638     if (child) return child;
2639     return self;
2640 }
2641
2642 gboolean client_can_focus(ObClient *self)
2643 {
2644     XEvent ev;
2645
2646     /* choose the correct target */
2647     self = client_focus_target(self);
2648
2649     if (!self->frame->visible)
2650         return FALSE;
2651
2652     if (!(self->can_focus || self->focus_notify))
2653         return FALSE;
2654
2655     /* do a check to see if the window has already been unmapped or destroyed
2656        do this intelligently while watching out for unmaps we've generated
2657        (ignore_unmaps > 0) */
2658     if (XCheckTypedWindowEvent(ob_display, self->window,
2659                                DestroyNotify, &ev)) {
2660         XPutBackEvent(ob_display, &ev);
2661         return FALSE;
2662     }
2663     while (XCheckTypedWindowEvent(ob_display, self->window,
2664                                   UnmapNotify, &ev)) {
2665         if (self->ignore_unmaps) {
2666             self->ignore_unmaps--;
2667         } else {
2668             XPutBackEvent(ob_display, &ev);
2669             return FALSE;
2670         }
2671     }
2672
2673     return TRUE;
2674 }
2675
2676 gboolean client_focus(ObClient *self)
2677 {
2678     /* choose the correct target */
2679     self = client_focus_target(self);
2680
2681     if (!client_can_focus(self)) {
2682         if (!self->frame->visible) {
2683             /* update the focus lists */
2684             focus_order_to_top(self);
2685         }
2686         return FALSE;
2687     }
2688
2689     if (self->can_focus) {
2690         /* RevertToPointerRoot causes much more headache than RevertToNone, so
2691            I choose to use it always, hopefully to find errors quicker, if any
2692            are left. (I hate X. I hate focus events.)
2693            
2694            Update: Changing this to RevertToNone fixed a bug with mozilla (bug
2695            #799. So now it is RevertToNone again.
2696         */
2697         XSetInputFocus(ob_display, self->window, RevertToNone,
2698                        event_lasttime);
2699     }
2700
2701     if (self->focus_notify) {
2702         XEvent ce;
2703         ce.xclient.type = ClientMessage;
2704         ce.xclient.message_type = prop_atoms.wm_protocols;
2705         ce.xclient.display = ob_display;
2706         ce.xclient.window = self->window;
2707         ce.xclient.format = 32;
2708         ce.xclient.data.l[0] = prop_atoms.wm_take_focus;
2709         ce.xclient.data.l[1] = event_lasttime;
2710         ce.xclient.data.l[2] = 0l;
2711         ce.xclient.data.l[3] = 0l;
2712         ce.xclient.data.l[4] = 0l;
2713         XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
2714     }
2715
2716 #ifdef DEBUG_FOCUS
2717     ob_debug("%sively focusing %lx at %d\n",
2718              (self->can_focus ? "act" : "pass"),
2719              self->window, (gint) event_lasttime);
2720 #endif
2721
2722     /* Cause the FocusIn to come back to us. Important for desktop switches,
2723        since otherwise we'll have no FocusIn on the queue and send it off to
2724        the focus_backup. */
2725     XSync(ob_display, FALSE);
2726     return TRUE;
2727 }
2728
2729 void client_unfocus(ObClient *self)
2730 {
2731     if (focus_client == self) {
2732 #ifdef DEBUG_FOCUS
2733         ob_debug("client_unfocus for %lx\n", self->window);
2734 #endif
2735         focus_fallback(OB_FOCUS_FALLBACK_UNFOCUSING);
2736     }
2737 }
2738
2739 void client_activate(ObClient *self, gboolean here)
2740 {
2741     if (client_normal(self) && screen_showing_desktop)
2742         screen_show_desktop(FALSE);
2743     if (self->iconic)
2744         client_iconify(self, FALSE, here);
2745     if (self->desktop != DESKTOP_ALL &&
2746         self->desktop != screen_desktop) {
2747         if (here)
2748             client_set_desktop(self, screen_desktop, FALSE);
2749         else
2750             screen_set_desktop(self->desktop);
2751     } else if (!self->frame->visible)
2752         /* if its not visible for other reasons, then don't mess
2753            with it */
2754         return;
2755     if (self->shaded)
2756         client_shade(self, FALSE);
2757
2758     client_focus(self);
2759
2760     /* we do this an action here. this is rather important. this is because
2761        we want the results from the focus change to take place BEFORE we go
2762        about raising the window. when a fullscreen window loses focus, we need
2763        this or else the raise wont be able to raise above the to-lose-focus
2764        fullscreen window. */
2765     client_raise(self);
2766 }
2767
2768 void client_raise(ObClient *self)
2769 {
2770     action_run_string("Raise", self);
2771 }
2772
2773 void client_lower(ObClient *self)
2774 {
2775     action_run_string("Raise", self);
2776 }
2777
2778 gboolean client_focused(ObClient *self)
2779 {
2780     return self == focus_client;
2781 }
2782
2783 static ObClientIcon* client_icon_recursive(ObClient *self, gint w, gint h)
2784 {
2785     guint i;
2786     /* si is the smallest image >= req */
2787     /* li is the largest image < req */
2788     gulong size, smallest = 0xffffffff, largest = 0, si = 0, li = 0;
2789
2790     if (!self->nicons) {
2791         ObClientIcon *parent = NULL;
2792
2793         if (self->transient_for) {
2794             if (self->transient_for != OB_TRAN_GROUP)
2795                 parent = client_icon_recursive(self->transient_for, w, h);
2796             else {
2797                 GSList *it;
2798                 for (it = self->group->members; it; it = g_slist_next(it)) {
2799                     ObClient *c = it->data;
2800                     if (c != self && !c->transient_for) {
2801                         if ((parent = client_icon_recursive(c, w, h)))
2802                             break;
2803                     }
2804                 }
2805             }
2806         }
2807         
2808         return parent;
2809     }
2810
2811     for (i = 0; i < self->nicons; ++i) {
2812         size = self->icons[i].width * self->icons[i].height;
2813         if (size < smallest && size >= (unsigned)(w * h)) {
2814             smallest = size;
2815             si = i;
2816         }
2817         if (size > largest && size <= (unsigned)(w * h)) {
2818             largest = size;
2819             li = i;
2820         }
2821     }
2822     if (largest == 0) /* didnt find one smaller than the requested size */
2823         return &self->icons[si];
2824     return &self->icons[li];
2825 }
2826
2827 const ObClientIcon* client_icon(ObClient *self, gint w, gint h)
2828 {
2829     ObClientIcon *ret;
2830     static ObClientIcon deficon;
2831
2832     if (!(ret = client_icon_recursive(self, w, h))) {
2833         deficon.width = deficon.height = 48;
2834         deficon.data = ob_rr_theme->def_win_icon;
2835         ret = &deficon;
2836     }
2837     return ret;
2838 }
2839
2840 /* this be mostly ripped from fvwm */
2841 ObClient *client_find_directional(ObClient *c, ObDirection dir) 
2842 {
2843     gint my_cx, my_cy, his_cx, his_cy;
2844     gint offset = 0;
2845     gint distance = 0;
2846     gint score, best_score;
2847     ObClient *best_client, *cur;
2848     GList *it;
2849
2850     if(!client_list)
2851         return NULL;
2852
2853     /* first, find the centre coords of the currently focused window */
2854     my_cx = c->frame->area.x + c->frame->area.width / 2;
2855     my_cy = c->frame->area.y + c->frame->area.height / 2;
2856
2857     best_score = -1;
2858     best_client = NULL;
2859
2860     for(it = g_list_first(client_list); it; it = it->next) {
2861         cur = it->data;
2862
2863         /* the currently selected window isn't interesting */
2864         if(cur == c)
2865             continue;
2866         if (!client_normal(cur))
2867             continue;
2868         if(c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
2869             continue;
2870         if(cur->iconic)
2871             continue;
2872         if(client_focus_target(cur) == cur &&
2873            !(cur->can_focus || cur->focus_notify))
2874             continue;
2875
2876         /* find the centre coords of this window, from the
2877          * currently focused window's point of view */
2878         his_cx = (cur->frame->area.x - my_cx)
2879             + cur->frame->area.width / 2;
2880         his_cy = (cur->frame->area.y - my_cy)
2881             + cur->frame->area.height / 2;
2882
2883         if(dir == OB_DIRECTION_NORTHEAST || dir == OB_DIRECTION_SOUTHEAST ||
2884            dir == OB_DIRECTION_SOUTHWEST || dir == OB_DIRECTION_NORTHWEST) {
2885             gint tx;
2886             /* Rotate the diagonals 45 degrees counterclockwise.
2887              * To do this, multiply the matrix /+h +h\ with the
2888              * vector (x y).                   \-h +h/
2889              * h = sqrt(0.5). We can set h := 1 since absolute
2890              * distance doesn't matter here. */
2891             tx = his_cx + his_cy;
2892             his_cy = -his_cx + his_cy;
2893             his_cx = tx;
2894         }
2895
2896         switch(dir) {
2897         case OB_DIRECTION_NORTH:
2898         case OB_DIRECTION_SOUTH:
2899         case OB_DIRECTION_NORTHEAST:
2900         case OB_DIRECTION_SOUTHWEST:
2901             offset = (his_cx < 0) ? -his_cx : his_cx;
2902             distance = ((dir == OB_DIRECTION_NORTH ||
2903                          dir == OB_DIRECTION_NORTHEAST) ?
2904                         -his_cy : his_cy);
2905             break;
2906         case OB_DIRECTION_EAST:
2907         case OB_DIRECTION_WEST:
2908         case OB_DIRECTION_SOUTHEAST:
2909         case OB_DIRECTION_NORTHWEST:
2910             offset = (his_cy < 0) ? -his_cy : his_cy;
2911             distance = ((dir == OB_DIRECTION_WEST ||
2912                          dir == OB_DIRECTION_NORTHWEST) ?
2913                         -his_cx : his_cx);
2914             break;
2915         }
2916
2917         /* the target must be in the requested direction */
2918         if(distance <= 0)
2919             continue;
2920
2921         /* Calculate score for this window.  The smaller the better. */
2922         score = distance + offset;
2923
2924         /* windows more than 45 degrees off the direction are
2925          * heavily penalized and will only be chosen if nothing
2926          * else within a million pixels */
2927         if(offset > distance)
2928             score += 1000000;
2929
2930         if(best_score == -1 || score < best_score)
2931             best_client = cur,
2932                 best_score = score;
2933     }
2934
2935     return best_client;
2936 }
2937
2938 void client_set_layer(ObClient *self, gint layer)
2939 {
2940     if (layer < 0) {
2941         self->below = TRUE;
2942         self->above = FALSE;
2943     } else if (layer == 0) {
2944         self->below = self->above = FALSE;
2945     } else {
2946         self->below = FALSE;
2947         self->above = TRUE;
2948     }
2949     client_calc_layer(self);
2950     client_change_state(self); /* reflect this in the state hints */
2951 }
2952
2953 void client_set_undecorated(ObClient *self, gboolean undecorated)
2954 {
2955     if (self->undecorated != undecorated) {
2956         self->undecorated = undecorated;
2957         client_setup_decor_and_functions(self);
2958         client_change_state(self); /* reflect this in the state hints */
2959     }
2960 }
2961
2962 guint client_monitor(ObClient *self)
2963 {
2964     guint i;
2965     guint most = 0;
2966     guint mostv = 0;
2967
2968     for (i = 0; i < screen_num_monitors; ++i) {
2969         Rect *area = screen_physical_area_monitor(i);
2970         if (RECT_INTERSECTS_RECT(*area, self->frame->area)) {
2971             Rect r;
2972             guint v;
2973
2974             RECT_SET_INTERSECTION(r, *area, self->frame->area);
2975             v = r.width * r.height;
2976
2977             if (v > mostv) {
2978                 mostv = v;
2979                 most = i;
2980             }
2981         }
2982     }
2983     return most;
2984 }
2985
2986 ObClient *client_search_top_transient(ObClient *self)
2987 {
2988     /* move up the transient chain as far as possible */
2989     if (self->transient_for) {
2990         if (self->transient_for != OB_TRAN_GROUP) {
2991             return client_search_top_transient(self->transient_for);
2992         } else {
2993             GSList *it;
2994
2995             g_assert(self->group);
2996
2997             for (it = self->group->members; it; it = it->next) {
2998                 ObClient *c = it->data;
2999
3000                 /* checking transient_for prevents infinate loops! */
3001                 if (c != self && !c->transient_for)
3002                     break;
3003             }
3004             if (it)
3005                 return it->data;
3006         }
3007     }
3008
3009     return self;
3010 }
3011
3012 ObClient *client_search_focus_parent(ObClient *self)
3013 {
3014     if (self->transient_for) {
3015         if (self->transient_for != OB_TRAN_GROUP) {
3016             if (client_focused(self->transient_for))
3017                 return self->transient_for;
3018         } else {
3019             GSList *it;
3020
3021             for (it = self->group->members; it; it = it->next) {
3022                 ObClient *c = it->data;
3023
3024                 /* checking transient_for prevents infinate loops! */
3025                 if (c != self && !c->transient_for)
3026                     if (client_focused(c))
3027                         return c;
3028             }
3029         }
3030     }
3031
3032     return NULL;
3033 }
3034
3035 ObClient *client_search_parent(ObClient *self, ObClient *search)
3036 {
3037     if (self->transient_for) {
3038         if (self->transient_for != OB_TRAN_GROUP) {
3039             if (self->transient_for == search)
3040                 return search;
3041         } else {
3042             GSList *it;
3043
3044             for (it = self->group->members; it; it = it->next) {
3045                 ObClient *c = it->data;
3046
3047                 /* checking transient_for prevents infinate loops! */
3048                 if (c != self && !c->transient_for)
3049                     if (c == search)
3050                         return search;
3051             }
3052         }
3053     }
3054
3055     return NULL;
3056 }
3057
3058 ObClient *client_search_transient(ObClient *self, ObClient *search)
3059 {
3060     GSList *sit;
3061
3062     for (sit = self->transients; sit; sit = g_slist_next(sit)) {
3063         if (sit->data == search)
3064             return search;
3065         if (client_search_transient(sit->data, search))
3066             return search;
3067     }
3068     return NULL;
3069 }
3070
3071 void client_update_sm_client_id(ObClient *self)
3072 {
3073     g_free(self->sm_client_id);
3074     self->sm_client_id = NULL;
3075
3076     if (!PROP_GETS(self->window, sm_client_id, locale, &self->sm_client_id) &&
3077         self->group)
3078         PROP_GETS(self->group->leader, sm_client_id, locale,
3079                   &self->sm_client_id);
3080 }
3081
3082 /* finds the nearest edge in the given direction from the current client
3083  * note to self: the edge is the -frame- edge (the actual one), not the
3084  * client edge.
3085  */
3086 gint client_directional_edge_search(ObClient *c, ObDirection dir)
3087 {
3088     gint dest;
3089     gint my_edge_start, my_edge_end, my_offset;
3090     GList *it;
3091     Rect *a;
3092     
3093     if(!client_list)
3094         return -1;
3095
3096     a = screen_area(c->desktop);
3097
3098     switch(dir) {
3099     case OB_DIRECTION_NORTH:
3100         my_edge_start = c->frame->area.x;
3101         my_edge_end = c->frame->area.x + c->frame->area.width;
3102         my_offset = c->frame->area.y;
3103         
3104         /* default: top of screen */
3105         dest = a->y;
3106
3107         for(it = g_list_first(client_list); it; it = it->next) {
3108             gint his_edge_start, his_edge_end, his_offset;
3109             ObClient *cur = it->data;
3110
3111             if(cur == c)
3112                 continue;
3113             if(!client_normal(cur))
3114                 continue;
3115             if(c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
3116                 continue;
3117             if(cur->iconic)
3118                 continue;
3119
3120             his_edge_start = cur->frame->area.x;
3121             his_edge_end = cur->frame->area.x + cur->frame->area.width;
3122             his_offset = cur->frame->area.y + cur->frame->area.height;
3123
3124             if(his_offset + 1 > my_offset)
3125                 continue;
3126
3127             if(his_offset < dest)
3128                 continue;
3129             
3130             if(his_edge_start >= my_edge_start &&
3131                his_edge_start <= my_edge_end)
3132                 dest = his_offset;
3133
3134             if(my_edge_start >= his_edge_start &&
3135                my_edge_start <= his_edge_end)
3136                 dest = his_offset;
3137
3138         }
3139         break;
3140     case OB_DIRECTION_SOUTH:
3141         my_edge_start = c->frame->area.x;
3142         my_edge_end = c->frame->area.x + c->frame->area.width;
3143         my_offset = c->frame->area.y + c->frame->area.height;
3144
3145         /* default: bottom of screen */
3146         dest = a->y + a->height;
3147
3148         for(it = g_list_first(client_list); it; it = it->next) {
3149             gint his_edge_start, his_edge_end, his_offset;
3150             ObClient *cur = it->data;
3151
3152             if(cur == c)
3153                 continue;
3154             if(!client_normal(cur))
3155                 continue;
3156             if(c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
3157                 continue;
3158             if(cur->iconic)
3159                 continue;
3160
3161             his_edge_start = cur->frame->area.x;
3162             his_edge_end = cur->frame->area.x + cur->frame->area.width;
3163             his_offset = cur->frame->area.y;
3164
3165
3166             if(his_offset - 1 < my_offset)
3167                 continue;
3168             
3169             if(his_offset > dest)
3170                 continue;
3171             
3172             if(his_edge_start >= my_edge_start &&
3173                his_edge_start <= my_edge_end)
3174                 dest = his_offset;
3175
3176             if(my_edge_start >= his_edge_start &&
3177                my_edge_start <= his_edge_end)
3178                 dest = his_offset;
3179
3180         }
3181         break;
3182     case OB_DIRECTION_WEST:
3183         my_edge_start = c->frame->area.y;
3184         my_edge_end = c->frame->area.y + c->frame->area.height;
3185         my_offset = c->frame->area.x;
3186
3187         /* default: leftmost egde of screen */
3188         dest = a->x;
3189
3190         for(it = g_list_first(client_list); it; it = it->next) {
3191             gint his_edge_start, his_edge_end, his_offset;
3192             ObClient *cur = it->data;
3193
3194             if(cur == c)
3195                 continue;
3196             if(!client_normal(cur))
3197                 continue;
3198             if(c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
3199                 continue;
3200             if(cur->iconic)
3201                 continue;
3202
3203             his_edge_start = cur->frame->area.y;
3204             his_edge_end = cur->frame->area.y + cur->frame->area.height;
3205             his_offset = cur->frame->area.x + cur->frame->area.width;
3206
3207             if(his_offset + 1 > my_offset)
3208                 continue;
3209             
3210             if(his_offset < dest)
3211                 continue;
3212             
3213             if(his_edge_start >= my_edge_start &&
3214                his_edge_start <= my_edge_end)
3215                 dest = his_offset;
3216
3217             if(my_edge_start >= his_edge_start &&
3218                my_edge_start <= his_edge_end)
3219                 dest = his_offset;
3220                 
3221
3222         }
3223         break;
3224     case OB_DIRECTION_EAST:
3225         my_edge_start = c->frame->area.y;
3226         my_edge_end = c->frame->area.y + c->frame->area.height;
3227         my_offset = c->frame->area.x + c->frame->area.width;
3228         
3229         /* default: rightmost edge of screen */
3230         dest = a->x + a->width;
3231
3232         for(it = g_list_first(client_list); it; it = it->next) {
3233             gint his_edge_start, his_edge_end, his_offset;
3234             ObClient *cur = it->data;
3235
3236             if(cur == c)
3237                 continue;
3238             if(!client_normal(cur))
3239                 continue;
3240             if(c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
3241                 continue;
3242             if(cur->iconic)
3243                 continue;
3244
3245             his_edge_start = cur->frame->area.y;
3246             his_edge_end = cur->frame->area.y + cur->frame->area.height;
3247             his_offset = cur->frame->area.x;
3248
3249             if(his_offset - 1 < my_offset)
3250                 continue;
3251             
3252             if(his_offset > dest)
3253                 continue;
3254             
3255             if(his_edge_start >= my_edge_start &&
3256                his_edge_start <= my_edge_end)
3257                 dest = his_offset;
3258
3259             if(my_edge_start >= his_edge_start &&
3260                my_edge_start <= his_edge_end)
3261                 dest = his_offset;
3262
3263         }
3264         break;
3265     case OB_DIRECTION_NORTHEAST:
3266     case OB_DIRECTION_SOUTHEAST:
3267     case OB_DIRECTION_NORTHWEST:
3268     case OB_DIRECTION_SOUTHWEST:
3269         /* not implemented */
3270     default:
3271         g_assert_not_reached();
3272     }
3273     return dest;
3274 }
3275
3276 ObClient* client_under_pointer()
3277 {
3278     gint x, y;
3279     GList *it;
3280     ObClient *ret = NULL;
3281
3282     if (screen_pointer_pos(&x, &y)) {
3283         for (it = stacking_list; it != NULL; it = it->next) {
3284             if (WINDOW_IS_CLIENT(it->data)) {
3285                 ObClient *c = WINDOW_AS_CLIENT(it->data);
3286                 if (c->frame->visible &&
3287                     RECT_CONTAINS(c->frame->area, x, y)) {
3288                     ret = c;
3289                     break;
3290                 }
3291             }
3292         }
3293     }
3294     return ret;
3295 }
3296
3297 gboolean client_has_group_siblings(ObClient *self)
3298 {
3299     return self->group && self->group->members->next;
3300 }