]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/client.c
unmap and map the client window when the frame is hidden/shown
[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     unsigned int 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         int x = self->area.x, ox = x;
341         int 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     int x = self->area.x;
589     int 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, int *x, int *y, int w, int h,
598                               gboolean rude)
599 {
600     Rect *a;
601     int 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     int oldx = self->area.x, oldy = self->area.y;
652     int 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         int foo;
890         guint ufoo;
891         int 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     long ret;
1086     int 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 = (float)size.min_aspect.x / size.min_aspect.y;
1120             if (size.max_aspect.y)
1121                 self->max_ratio = (float)size.max_aspect.x / size.max_aspect.y;
1122         }
1123
1124         if (size.flags & PMinSize)
1125             SIZE_SET(self->min_size, size.min_width, size.min_height);
1126     
1127         if (size.flags & PMaxSize)
1128             SIZE_SET(self->max_size, size.max_width, size.max_height);
1129     
1130         if (size.flags & PBaseSize)
1131             SIZE_SET(self->base_size, size.base_width, size.base_height);
1132     
1133         if (size.flags & PResizeInc)
1134             SIZE_SET(self->size_inc, size.width_inc, size.height_inc);
1135     }
1136 }
1137
1138 void client_setup_decor_and_functions(ObClient *self)
1139 {
1140     /* start with everything (cept fullscreen) */
1141     self->decorations =
1142         (OB_FRAME_DECOR_TITLEBAR |
1143          (ob_rr_theme->show_handle ? OB_FRAME_DECOR_HANDLE : 0) |
1144          OB_FRAME_DECOR_GRIPS |
1145          OB_FRAME_DECOR_BORDER |
1146          OB_FRAME_DECOR_ICON |
1147          OB_FRAME_DECOR_ALLDESKTOPS |
1148          OB_FRAME_DECOR_ICONIFY |
1149          OB_FRAME_DECOR_MAXIMIZE |
1150          OB_FRAME_DECOR_SHADE |
1151          OB_FRAME_DECOR_CLOSE);
1152     self->functions =
1153         (OB_CLIENT_FUNC_RESIZE |
1154          OB_CLIENT_FUNC_MOVE |
1155          OB_CLIENT_FUNC_ICONIFY |
1156          OB_CLIENT_FUNC_MAXIMIZE |
1157          OB_CLIENT_FUNC_SHADE |
1158          OB_CLIENT_FUNC_CLOSE);
1159
1160     if (!(self->min_size.width < self->max_size.width ||
1161           self->min_size.height < self->max_size.height))
1162         self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1163
1164     switch (self->type) {
1165     case OB_CLIENT_TYPE_NORMAL:
1166         /* normal windows retain all of the possible decorations and
1167            functionality, and are the only windows that you can fullscreen */
1168         self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
1169         break;
1170
1171     case OB_CLIENT_TYPE_DIALOG:
1172     case OB_CLIENT_TYPE_UTILITY:
1173         /* these windows cannot be maximized */
1174         self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1175         break;
1176
1177     case OB_CLIENT_TYPE_MENU:
1178     case OB_CLIENT_TYPE_TOOLBAR:
1179         /* these windows get less functionality */
1180         self->functions &= ~(OB_CLIENT_FUNC_ICONIFY | OB_CLIENT_FUNC_RESIZE);
1181         break;
1182
1183     case OB_CLIENT_TYPE_DESKTOP:
1184     case OB_CLIENT_TYPE_DOCK:
1185     case OB_CLIENT_TYPE_SPLASH:
1186         /* none of these windows are manipulated by the window manager */
1187         self->decorations = 0;
1188         self->functions = 0;
1189         break;
1190     }
1191
1192     /* Mwm Hints are applied subtractively to what has already been chosen for
1193        decor and functionality */
1194     if (self->mwmhints.flags & OB_MWM_FLAG_DECORATIONS) {
1195         if (! (self->mwmhints.decorations & OB_MWM_DECOR_ALL)) {
1196             if (! ((self->mwmhints.decorations & OB_MWM_DECOR_HANDLE) ||
1197                    (self->mwmhints.decorations & OB_MWM_DECOR_TITLE)))
1198                 /* if the mwm hints request no handle or title, then all
1199                    decorations are disabled */
1200                 self->decorations = 0;
1201         }
1202     }
1203
1204     if (self->mwmhints.flags & OB_MWM_FLAG_FUNCTIONS) {
1205         if (! (self->mwmhints.functions & OB_MWM_FUNC_ALL)) {
1206             if (! (self->mwmhints.functions & OB_MWM_FUNC_RESIZE))
1207                 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1208             if (! (self->mwmhints.functions & OB_MWM_FUNC_MOVE))
1209                 self->functions &= ~OB_CLIENT_FUNC_MOVE;
1210             /* dont let mwm hints kill any buttons
1211                if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1212                self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1213                if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1214                self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1215             */
1216             /* dont let mwm hints kill the close button
1217                if (! (self->mwmhints.functions & MwmFunc_Close))
1218                self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1219             }
1220     }
1221
1222     if (!(self->functions & OB_CLIENT_FUNC_SHADE))
1223         self->decorations &= ~OB_FRAME_DECOR_SHADE;
1224     if (!(self->functions & OB_CLIENT_FUNC_ICONIFY))
1225         self->decorations &= ~OB_FRAME_DECOR_ICONIFY;
1226     if (!(self->functions & OB_CLIENT_FUNC_RESIZE))
1227         self->decorations &= ~OB_FRAME_DECOR_GRIPS;
1228
1229     /* can't maximize without moving/resizing */
1230     if (!((self->functions & OB_CLIENT_FUNC_MAXIMIZE) &&
1231           (self->functions & OB_CLIENT_FUNC_MOVE) &&
1232           (self->functions & OB_CLIENT_FUNC_RESIZE))) {
1233         self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1234         self->decorations &= ~OB_FRAME_DECOR_MAXIMIZE;
1235     }
1236
1237     /* kill the handle on fully maxed windows */
1238     if (self->max_vert && self->max_horz)
1239         self->decorations &= ~OB_FRAME_DECOR_HANDLE;
1240
1241     /* finally, the user can have requested no decorations, which overrides
1242        everything (but doesnt give it a border if it doesnt have one) */
1243     if (self->undecorated)
1244         self->decorations &= OB_FRAME_DECOR_BORDER;
1245
1246     /* if we don't have a titlebar, then we cannot shade! */
1247     if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
1248         self->functions &= ~OB_CLIENT_FUNC_SHADE;
1249
1250     /* now we need to check against rules for the client's current state */
1251     if (self->fullscreen) {
1252         self->functions &= (OB_CLIENT_FUNC_CLOSE |
1253                             OB_CLIENT_FUNC_FULLSCREEN |
1254                             OB_CLIENT_FUNC_ICONIFY);
1255         self->decorations = 0;
1256     }
1257
1258     client_change_allowed_actions(self);
1259
1260     if (self->frame) {
1261         /* adjust the client's decorations, etc. */
1262         client_reconfigure(self);
1263     }
1264 }
1265
1266 static void client_change_allowed_actions(ObClient *self)
1267 {
1268     guint32 actions[9];
1269     int num = 0;
1270
1271     /* desktop windows are kept on all desktops */
1272     if (self->type != OB_CLIENT_TYPE_DESKTOP)
1273         actions[num++] = prop_atoms.net_wm_action_change_desktop;
1274
1275     if (self->functions & OB_CLIENT_FUNC_SHADE)
1276         actions[num++] = prop_atoms.net_wm_action_shade;
1277     if (self->functions & OB_CLIENT_FUNC_CLOSE)
1278         actions[num++] = prop_atoms.net_wm_action_close;
1279     if (self->functions & OB_CLIENT_FUNC_MOVE)
1280         actions[num++] = prop_atoms.net_wm_action_move;
1281     if (self->functions & OB_CLIENT_FUNC_ICONIFY)
1282         actions[num++] = prop_atoms.net_wm_action_minimize;
1283     if (self->functions & OB_CLIENT_FUNC_RESIZE)
1284         actions[num++] = prop_atoms.net_wm_action_resize;
1285     if (self->functions & OB_CLIENT_FUNC_FULLSCREEN)
1286         actions[num++] = prop_atoms.net_wm_action_fullscreen;
1287     if (self->functions & OB_CLIENT_FUNC_MAXIMIZE) {
1288         actions[num++] = prop_atoms.net_wm_action_maximize_horz;
1289         actions[num++] = prop_atoms.net_wm_action_maximize_vert;
1290     }
1291
1292     PROP_SETA32(self->window, net_wm_allowed_actions, atom, actions, num);
1293
1294     /* make sure the window isn't breaking any rules now */
1295
1296     if (!(self->functions & OB_CLIENT_FUNC_SHADE) && self->shaded) {
1297         if (self->frame) client_shade(self, FALSE);
1298         else self->shaded = FALSE;
1299     }
1300     if (!(self->functions & OB_CLIENT_FUNC_ICONIFY) && self->iconic) {
1301         if (self->frame) client_iconify(self, FALSE, TRUE);
1302         else self->iconic = FALSE;
1303     }
1304     if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) && self->fullscreen) {
1305         if (self->frame) client_fullscreen(self, FALSE, TRUE);
1306         else self->fullscreen = FALSE;
1307     }
1308     if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && (self->max_horz ||
1309                                                          self->max_vert)) {
1310         if (self->frame) client_maximize(self, FALSE, 0, TRUE);
1311         else self->max_vert = self->max_horz = FALSE;
1312     }
1313 }
1314
1315 void client_reconfigure(ObClient *self)
1316 {
1317     /* by making this pass FALSE for user, we avoid the emacs event storm where
1318        every configurenotify causes an update in its normal hints, i think this
1319        is generally what we want anyways... */
1320     client_configure(self, OB_CORNER_TOPLEFT, self->area.x, self->area.y,
1321                      self->area.width, self->area.height, FALSE, TRUE);
1322 }
1323
1324 void client_update_wmhints(ObClient *self)
1325 {
1326     XWMHints *hints;
1327     gboolean ur = FALSE;
1328     GSList *it;
1329
1330     /* assume a window takes input if it doesnt specify */
1331     self->can_focus = TRUE;
1332   
1333     if ((hints = XGetWMHints(ob_display, self->window)) != NULL) {
1334         if (hints->flags & InputHint)
1335             self->can_focus = hints->input;
1336
1337         /* only do this when first managing the window *AND* when we aren't
1338            starting up! */
1339         if (ob_state() != OB_STATE_STARTING && self->frame == NULL)
1340             if (hints->flags & StateHint)
1341                 self->iconic = hints->initial_state == IconicState;
1342
1343         if (hints->flags & XUrgencyHint)
1344             ur = TRUE;
1345
1346         if (!(hints->flags & WindowGroupHint))
1347             hints->window_group = None;
1348
1349         /* did the group state change? */
1350         if (hints->window_group !=
1351             (self->group ? self->group->leader : None)) {
1352             /* remove from the old group if there was one */
1353             if (self->group != NULL) {
1354                 /* remove transients of the group */
1355                 for (it = self->group->members; it; it = it->next)
1356                     self->transients = g_slist_remove(self->transients,
1357                                                       it->data);
1358
1359                 /* remove myself from parents in the group */
1360                 if (self->transient_for == OB_TRAN_GROUP) {
1361                     for (it = self->group->members; it; it = it->next) {
1362                         ObClient *c = it->data;
1363
1364                         if (c != self && !c->transient_for)
1365                             c->transients = g_slist_remove(c->transients,
1366                                                            self);
1367                     }
1368                 }
1369
1370                 group_remove(self->group, self);
1371                 self->group = NULL;
1372             }
1373             if (hints->window_group != None) {
1374                 self->group = group_add(hints->window_group, self);
1375
1376                 /* i can only have transients from the group if i am not
1377                    transient myself */
1378                 if (!self->transient_for) {
1379                     /* add other transients of the group that are already
1380                        set up */
1381                     for (it = self->group->members; it; it = it->next) {
1382                         ObClient *c = it->data;
1383                         if (c != self && c->transient_for == OB_TRAN_GROUP)
1384                             self->transients =
1385                                 g_slist_append(self->transients, c);
1386                     }
1387                 }
1388             }
1389
1390             /* because the self->transient flag wont change from this call,
1391                we don't need to update the window's type and such, only its
1392                transient_for, and the transients lists of other windows in
1393                the group may be affected */
1394             client_update_transient_for(self);
1395         }
1396
1397         /* the WM_HINTS can contain an icon */
1398         client_update_icons(self);
1399
1400         XFree(hints);
1401     }
1402
1403     if (ur != self->urgent) {
1404         self->urgent = ur;
1405         /* fire the urgent callback if we're mapped, otherwise, wait until
1406            after we're mapped */
1407         if (self->frame)
1408             client_urgent_notify(self);
1409     }
1410 }
1411
1412 void client_update_title(ObClient *self)
1413 {
1414     GList *it;
1415     guint32 nums;
1416     guint i;
1417     gchar *data = NULL;
1418     gboolean read_title;
1419     gchar *old_title;
1420
1421     old_title = self->title;
1422      
1423     /* try netwm */
1424     if (!PROP_GETS(self->window, net_wm_name, utf8, &data))
1425         /* try old x stuff */
1426         if (!PROP_GETS(self->window, wm_name, locale, &data))
1427             data = g_strdup("Unnamed Window");
1428
1429     /* did the title change? then reset the title_count */
1430     if (old_title && 0 != strncmp(old_title, data, strlen(data)))
1431         self->title_count = 1;
1432
1433     /* look for duplicates and append a number */
1434     nums = 0;
1435     for (it = client_list; it; it = it->next)
1436         if (it->data != self) {
1437             ObClient *c = it->data;
1438             if (0 == strncmp(c->title, data, strlen(data)))
1439                 nums |= 1 << c->title_count;
1440         }
1441     /* find first free number */
1442     for (i = 1; i <= 32; ++i)
1443         if (!(nums & (1 << i))) {
1444             if (self->title_count == 1 || i == 1)
1445                 self->title_count = i;
1446             break;
1447         }
1448     /* dont display the number for the first window */
1449     if (self->title_count > 1) {
1450         char *ndata;
1451         ndata = g_strdup_printf("%s - [%u]", data, self->title_count);
1452         g_free(data);
1453         data = ndata;
1454     }
1455
1456     PROP_SETS(self->window, net_wm_visible_name, data);
1457
1458     self->title = data;
1459
1460     if (self->frame)
1461         frame_adjust_title(self->frame);
1462
1463     g_free(old_title);
1464
1465     /* update the icon title */
1466     data = NULL;
1467     g_free(self->icon_title);
1468
1469     read_title = TRUE;
1470     /* try netwm */
1471     if (!PROP_GETS(self->window, net_wm_icon_name, utf8, &data))
1472         /* try old x stuff */
1473         if (!PROP_GETS(self->window, wm_icon_name, locale, &data)) {
1474             data = g_strdup(self->title);
1475             read_title = FALSE;
1476         }
1477
1478     /* append the title count, dont display the number for the first window */
1479     if (read_title && self->title_count > 1) {
1480         char *vdata, *ndata;
1481         ndata = g_strdup_printf(" - [%u]", self->title_count);
1482         vdata = g_strconcat(data, ndata, NULL);
1483         g_free(ndata);
1484         g_free(data);
1485         data = vdata;
1486     }
1487
1488     PROP_SETS(self->window, net_wm_visible_icon_name, data);
1489
1490     self->icon_title = data;
1491 }
1492
1493 void client_update_class(ObClient *self)
1494 {
1495     char **data;
1496     char *s;
1497
1498     if (self->name) g_free(self->name);
1499     if (self->class) g_free(self->class);
1500     if (self->role) g_free(self->role);
1501
1502     self->name = self->class = self->role = NULL;
1503
1504     if (PROP_GETSS(self->window, wm_class, locale, &data)) {
1505         if (data[0]) {
1506             self->name = g_strdup(data[0]);
1507             if (data[1])
1508                 self->class = g_strdup(data[1]);
1509         }
1510         g_strfreev(data);     
1511     }
1512
1513     if (PROP_GETS(self->window, wm_window_role, locale, &s))
1514         self->role = s;
1515
1516     if (self->name == NULL) self->name = g_strdup("");
1517     if (self->class == NULL) self->class = g_strdup("");
1518     if (self->role == NULL) self->role = g_strdup("");
1519 }
1520
1521 void client_update_strut(ObClient *self)
1522 {
1523     guint num;
1524     guint32 *data;
1525     gboolean got = FALSE;
1526     StrutPartial strut;
1527
1528     if (PROP_GETA32(self->window, net_wm_strut_partial, cardinal,
1529                     &data, &num)) {
1530         if (num == 12) {
1531             got = TRUE;
1532             STRUT_PARTIAL_SET(strut,
1533                               data[0], data[2], data[1], data[3],
1534                               data[4], data[5], data[8], data[9],
1535                               data[6], data[7], data[10], data[11]);
1536         }
1537         g_free(data);
1538     }
1539
1540     if (!got &&
1541         PROP_GETA32(self->window, net_wm_strut, cardinal, &data, &num)) {
1542         if (num == 4) {
1543             const Rect *a;
1544
1545             got = TRUE;
1546
1547             /* use the screen's width/height */
1548             a = screen_physical_area();
1549
1550             STRUT_PARTIAL_SET(strut,
1551                               data[0], data[2], data[1], data[3],
1552                               a->y, a->y + a->height - 1,
1553                               a->x, a->x + a->width - 1,
1554                               a->y, a->y + a->height - 1,
1555                               a->x, a->x + a->width - 1);
1556         }
1557         g_free(data);
1558     }
1559
1560     if (!got)
1561         STRUT_PARTIAL_SET(strut, 0, 0, 0, 0,
1562                           0, 0, 0, 0, 0, 0, 0, 0);
1563
1564     if (!STRUT_EQUAL(strut, self->strut)) {
1565         self->strut = strut;
1566
1567         /* updating here is pointless while we're being mapped cuz we're not in
1568            the client list yet */
1569         if (self->frame)
1570             screen_update_areas();
1571     }
1572 }
1573
1574 void client_update_icons(ObClient *self)
1575 {
1576     guint num;
1577     guint32 *data;
1578     guint w, h, i, j;
1579
1580     for (i = 0; i < self->nicons; ++i)
1581         g_free(self->icons[i].data);
1582     if (self->nicons > 0)
1583         g_free(self->icons);
1584     self->nicons = 0;
1585
1586     if (PROP_GETA32(self->window, net_wm_icon, cardinal, &data, &num)) {
1587         /* figure out how many valid icons are in here */
1588         i = 0;
1589         while (num - i > 2) {
1590             w = data[i++];
1591             h = data[i++];
1592             i += w * h;
1593             if (i > num || w*h == 0) break;
1594             ++self->nicons;
1595         }
1596
1597         self->icons = g_new(ObClientIcon, self->nicons);
1598     
1599         /* store the icons */
1600         i = 0;
1601         for (j = 0; j < self->nicons; ++j) {
1602             guint x, y, t;
1603
1604             w = self->icons[j].width = data[i++];
1605             h = self->icons[j].height = data[i++];
1606
1607             if (w*h == 0) continue;
1608
1609             self->icons[j].data = g_new(RrPixel32, w * h);
1610             for (x = 0, y = 0, t = 0; t < w * h; ++t, ++x, ++i) {
1611                 if (x >= w) {
1612                     x = 0;
1613                     ++y;
1614                 }
1615                 self->icons[j].data[t] =
1616                     (((data[i] >> 24) & 0xff) << RrDefaultAlphaOffset) +
1617                     (((data[i] >> 16) & 0xff) << RrDefaultRedOffset) +
1618                     (((data[i] >> 8) & 0xff) << RrDefaultGreenOffset) +
1619                     (((data[i] >> 0) & 0xff) << RrDefaultBlueOffset);
1620             }
1621             g_assert(i <= num);
1622         }
1623
1624         g_free(data);
1625     } else if (PROP_GETA32(self->window, kwm_win_icon,
1626                            kwm_win_icon, &data, &num)) {
1627         if (num == 2) {
1628             self->nicons++;
1629             self->icons = g_new(ObClientIcon, self->nicons);
1630             xerror_set_ignore(TRUE);
1631             if (!RrPixmapToRGBA(ob_rr_inst,
1632                                 data[0], data[1],
1633                                 &self->icons[self->nicons-1].width,
1634                                 &self->icons[self->nicons-1].height,
1635                                 &self->icons[self->nicons-1].data)) {
1636                 g_free(&self->icons[self->nicons-1]);
1637                 self->nicons--;
1638             }
1639             xerror_set_ignore(FALSE);
1640         }
1641         g_free(data);
1642     } else {
1643         XWMHints *hints;
1644
1645         if ((hints = XGetWMHints(ob_display, self->window))) {
1646             if (hints->flags & IconPixmapHint) {
1647                 self->nicons++;
1648                 self->icons = g_new(ObClientIcon, self->nicons);
1649                 xerror_set_ignore(TRUE);
1650                 if (!RrPixmapToRGBA(ob_rr_inst,
1651                                     hints->icon_pixmap,
1652                                     (hints->flags & IconMaskHint ?
1653                                      hints->icon_mask : None),
1654                                     &self->icons[self->nicons-1].width,
1655                                     &self->icons[self->nicons-1].height,
1656                                     &self->icons[self->nicons-1].data)){
1657                     g_free(&self->icons[self->nicons-1]);
1658                     self->nicons--;
1659                 }
1660                 xerror_set_ignore(FALSE);
1661             }
1662             XFree(hints);
1663         }
1664     }
1665
1666     if (self->frame)
1667         frame_adjust_icon(self->frame);
1668 }
1669
1670 static void client_change_state(ObClient *self)
1671 {
1672     guint32 state[2];
1673     guint32 netstate[11];
1674     guint num;
1675
1676     state[0] = self->wmstate;
1677     state[1] = None;
1678     PROP_SETA32(self->window, wm_state, wm_state, state, 2);
1679
1680     num = 0;
1681     if (self->modal)
1682         netstate[num++] = prop_atoms.net_wm_state_modal;
1683     if (self->shaded)
1684         netstate[num++] = prop_atoms.net_wm_state_shaded;
1685     if (self->iconic)
1686         netstate[num++] = prop_atoms.net_wm_state_hidden;
1687     if (self->skip_taskbar)
1688         netstate[num++] = prop_atoms.net_wm_state_skip_taskbar;
1689     if (self->skip_pager)
1690         netstate[num++] = prop_atoms.net_wm_state_skip_pager;
1691     if (self->fullscreen)
1692         netstate[num++] = prop_atoms.net_wm_state_fullscreen;
1693     if (self->max_vert)
1694         netstate[num++] = prop_atoms.net_wm_state_maximized_vert;
1695     if (self->max_horz)
1696         netstate[num++] = prop_atoms.net_wm_state_maximized_horz;
1697     if (self->above)
1698         netstate[num++] = prop_atoms.net_wm_state_above;
1699     if (self->below)
1700         netstate[num++] = prop_atoms.net_wm_state_below;
1701     if (self->undecorated)
1702         netstate[num++] = prop_atoms.ob_wm_state_undecorated;
1703     PROP_SETA32(self->window, net_wm_state, atom, netstate, num);
1704
1705     client_calc_layer(self);
1706
1707     if (self->frame)
1708         frame_adjust_state(self->frame);
1709 }
1710
1711 ObClient *client_search_focus_tree(ObClient *self)
1712 {
1713     GSList *it;
1714     ObClient *ret;
1715
1716     for (it = self->transients; it != NULL; it = it->next) {
1717         if (client_focused(it->data)) return it->data;
1718         if ((ret = client_search_focus_tree(it->data))) return ret;
1719     }
1720     return NULL;
1721 }
1722
1723 ObClient *client_search_focus_tree_full(ObClient *self)
1724 {
1725     if (self->transient_for) {
1726         if (self->transient_for != OB_TRAN_GROUP) {
1727             return client_search_focus_tree_full(self->transient_for);
1728         } else {
1729             GSList *it;
1730             gboolean recursed = FALSE;
1731         
1732             for (it = self->group->members; it; it = it->next)
1733                 if (!((ObClient*)it->data)->transient_for) {
1734                     ObClient *c;
1735                     if ((c = client_search_focus_tree_full(it->data)))
1736                         return c;
1737                     recursed = TRUE;
1738                 }
1739             if (recursed)
1740                 return NULL;
1741         }
1742     }
1743
1744     /* this function checks the whole tree, the client_search_focus_tree~
1745        does not, so we need to check this window */
1746     if (client_focused(self))
1747         return self;
1748     return client_search_focus_tree(self);
1749 }
1750
1751 static ObStackingLayer calc_layer(ObClient *self)
1752 {
1753     ObStackingLayer l;
1754
1755     if (self->fullscreen &&
1756         (client_focused(self) || client_search_focus_tree(self)))
1757         l = OB_STACKING_LAYER_FULLSCREEN;
1758     else if (self->type == OB_CLIENT_TYPE_DESKTOP)
1759         l = OB_STACKING_LAYER_DESKTOP;
1760     else if (self->type == OB_CLIENT_TYPE_DOCK) {
1761         if (self->above) l = OB_STACKING_LAYER_DOCK_ABOVE;
1762         else if (self->below) l = OB_STACKING_LAYER_DOCK_BELOW;
1763         else l = OB_STACKING_LAYER_NORMAL;
1764     }
1765     else if (self->above) l = OB_STACKING_LAYER_ABOVE;
1766     else if (self->below) l = OB_STACKING_LAYER_BELOW;
1767     else l = OB_STACKING_LAYER_NORMAL;
1768
1769     return l;
1770 }
1771
1772 static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
1773                                         ObStackingLayer l, gboolean raised)
1774 {
1775     ObStackingLayer old, own;
1776     GSList *it;
1777
1778     old = self->layer;
1779     own = calc_layer(self);
1780     self->layer = l > own ? l : own;
1781
1782     for (it = self->transients; it; it = it->next)
1783         client_calc_layer_recursive(it->data, orig,
1784                                     l, raised ? raised : l != old);
1785
1786     if (!raised && l != old)
1787         if (orig->frame) { /* only restack if the original window is managed */
1788             stacking_remove(CLIENT_AS_WINDOW(self));
1789             stacking_add(CLIENT_AS_WINDOW(self));
1790         }
1791 }
1792
1793 void client_calc_layer(ObClient *self)
1794 {
1795     ObStackingLayer l;
1796     ObClient *orig;
1797
1798     orig = self;
1799
1800     /* transients take on the layer of their parents */
1801     self = client_search_top_transient(self);
1802
1803     l = calc_layer(self);
1804
1805     client_calc_layer_recursive(self, orig, l, FALSE);
1806 }
1807
1808 gboolean client_should_show(ObClient *self)
1809 {
1810     if (self->iconic)
1811         return FALSE;
1812     if (client_normal(self) && screen_showing_desktop)
1813         return FALSE;
1814     /*
1815     if (self->transient_for) {
1816         if (self->transient_for != OB_TRAN_GROUP)
1817             return client_should_show(self->transient_for);
1818         else {
1819             GSList *it;
1820
1821             for (it = self->group->members; it; it = g_slist_next(it)) {
1822                 ObClient *c = it->data;
1823                 if (c != self && !c->transient_for) {
1824                     if (client_should_show(c))
1825                         return TRUE;
1826                 }
1827             }
1828         }
1829     }
1830     */
1831     if (self->desktop == screen_desktop || self->desktop == DESKTOP_ALL)
1832         return TRUE;
1833     
1834     return FALSE;
1835 }
1836
1837 static void client_showhide(ObClient *self)
1838 {
1839
1840     if (client_should_show(self))
1841         frame_show(self->frame);
1842     else
1843         frame_hide(self->frame);
1844 }
1845
1846 gboolean client_normal(ObClient *self) {
1847     return ! (self->type == OB_CLIENT_TYPE_DESKTOP ||
1848               self->type == OB_CLIENT_TYPE_DOCK ||
1849               self->type == OB_CLIENT_TYPE_SPLASH);
1850 }
1851
1852 static void client_apply_startup_state(ObClient *self)
1853 {
1854     /* these are in a carefully crafted order.. */
1855
1856     if (self->iconic) {
1857         self->iconic = FALSE;
1858         client_iconify(self, TRUE, FALSE);
1859     }
1860     if (self->fullscreen) {
1861         self->fullscreen = FALSE;
1862         client_fullscreen(self, TRUE, FALSE);
1863     }
1864     if (self->undecorated) {
1865         self->undecorated = FALSE;
1866         client_set_undecorated(self, TRUE);
1867     }
1868     if (self->shaded) {
1869         self->shaded = FALSE;
1870         client_shade(self, TRUE);
1871     }
1872     if (self->urgent)
1873         client_urgent_notify(self);
1874   
1875     if (self->max_vert && self->max_horz) {
1876         self->max_vert = self->max_horz = FALSE;
1877         client_maximize(self, TRUE, 0, FALSE);
1878     } else if (self->max_vert) {
1879         self->max_vert = FALSE;
1880         client_maximize(self, TRUE, 2, FALSE);
1881     } else if (self->max_horz) {
1882         self->max_horz = FALSE;
1883         client_maximize(self, TRUE, 1, FALSE);
1884     }
1885
1886     /* nothing to do for the other states:
1887        skip_taskbar
1888        skip_pager
1889        modal
1890        above
1891        below
1892     */
1893 }
1894
1895 void client_configure_full(ObClient *self, ObCorner anchor,
1896                            int x, int y, int w, int h,
1897                            gboolean user, gboolean final,
1898                            gboolean force_reply)
1899 {
1900     gint oldw, oldh;
1901     gboolean send_resize_client;
1902     gboolean moved = FALSE, resized = FALSE;
1903     guint fdecor = self->frame->decorations;
1904     gboolean fhorz = self->frame->max_horz;
1905
1906     /* make the frame recalculate its dimentions n shit without changing
1907        anything visible for real, this way the constraints below can work with
1908        the updated frame dimensions. */
1909     frame_adjust_area(self->frame, TRUE, TRUE, TRUE);
1910
1911     /* gets the frame's position */
1912     frame_client_gravity(self->frame, &x, &y);
1913
1914     /* these positions are frame positions, not client positions */
1915
1916     /* set the size and position if fullscreen */
1917     if (self->fullscreen) {
1918 #ifdef VIDMODE
1919         int dot;
1920         XF86VidModeModeLine mode;
1921 #endif
1922         Rect *a;
1923         guint i;
1924
1925         i = client_monitor(self);
1926         a = screen_physical_area_monitor(i);
1927
1928 #ifdef VIDMODE
1929         if (i == 0 && /* primary head */
1930             extensions_vidmode &&
1931             XF86VidModeGetViewPort(ob_display, ob_screen, &x, &y) &&
1932             /* get the mode last so the mode.privsize isnt freed incorrectly */
1933             XF86VidModeGetModeLine(ob_display, ob_screen, &dot, &mode)) {
1934             x += a->x;
1935             y += a->y;
1936             w = mode.hdisplay;
1937             h = mode.vdisplay;
1938             if (mode.privsize) XFree(mode.private);
1939         } else
1940 #endif
1941         {
1942             x = a->x;
1943             y = a->y;
1944             w = a->width;
1945             h = a->height;
1946         }
1947
1948         user = FALSE; /* ignore that increment etc shit when in fullscreen */
1949     } else {
1950         Rect *a;
1951
1952         a = screen_area_monitor(self->desktop, client_monitor(self));
1953
1954         /* set the size and position if maximized */
1955         if (self->max_horz) {
1956             x = a->x;
1957             w = a->width - self->frame->size.left - self->frame->size.right;
1958         }
1959         if (self->max_vert) {
1960             y = a->y;
1961             h = a->height - self->frame->size.top - self->frame->size.bottom;
1962         }
1963     }
1964
1965     /* gets the client's position */
1966     frame_frame_gravity(self->frame, &x, &y);
1967
1968     /* these override the above states! if you cant move you can't move! */
1969     if (user) {
1970         if (!(self->functions & OB_CLIENT_FUNC_MOVE)) {
1971             x = self->area.x;
1972             y = self->area.y;
1973         }
1974         if (!(self->functions & OB_CLIENT_FUNC_RESIZE)) {
1975             w = self->area.width;
1976             h = self->area.height;
1977         }
1978     }
1979
1980     if (!(w == self->area.width && h == self->area.height)) {
1981         int basew, baseh, minw, minh;
1982
1983         /* base size is substituted with min size if not specified */
1984         if (self->base_size.width || self->base_size.height) {
1985             basew = self->base_size.width;
1986             baseh = self->base_size.height;
1987         } else {
1988             basew = self->min_size.width;
1989             baseh = self->min_size.height;
1990         }
1991         /* min size is substituted with base size if not specified */
1992         if (self->min_size.width || self->min_size.height) {
1993             minw = self->min_size.width;
1994             minh = self->min_size.height;
1995         } else {
1996             minw = self->base_size.width;
1997             minh = self->base_size.height;
1998         }
1999
2000         /* if this is a user-requested resize, then check against min/max
2001            sizes */
2002
2003         /* smaller than min size or bigger than max size? */
2004         if (w > self->max_size.width) w = self->max_size.width;
2005         if (w < minw) w = minw;
2006         if (h > self->max_size.height) h = self->max_size.height;
2007         if (h < minh) h = minh;
2008
2009         w -= basew;
2010         h -= baseh;
2011
2012         /* keep to the increments */
2013         w /= self->size_inc.width;
2014         h /= self->size_inc.height;
2015
2016         /* you cannot resize to nothing */
2017         if (basew + w < 1) w = 1 - basew;
2018         if (baseh + h < 1) h = 1 - baseh;
2019   
2020         /* store the logical size */
2021         SIZE_SET(self->logical_size,
2022                  self->size_inc.width > 1 ? w : w + basew,
2023                  self->size_inc.height > 1 ? h : h + baseh);
2024
2025         w *= self->size_inc.width;
2026         h *= self->size_inc.height;
2027
2028         w += basew;
2029         h += baseh;
2030
2031         /* adjust the height to match the width for the aspect ratios.
2032            for this, min size is not substituted for base size ever. */
2033         w -= self->base_size.width;
2034         h -= self->base_size.height;
2035
2036         if (self->min_ratio)
2037             if (h * self->min_ratio > w) {
2038                 h = (int)(w / self->min_ratio);
2039
2040                 /* you cannot resize to nothing */
2041                 if (h < 1) {
2042                     h = 1;
2043                     w = (int)(h * self->min_ratio);
2044                 }
2045             }
2046         if (self->max_ratio)
2047             if (h * self->max_ratio < w) {
2048                 h = (int)(w / self->max_ratio);
2049
2050                 /* you cannot resize to nothing */
2051                 if (h < 1) {
2052                     h = 1;
2053                     w = (int)(h * self->min_ratio);
2054                 }
2055             }
2056
2057         w += self->base_size.width;
2058         h += self->base_size.height;
2059     }
2060
2061     g_assert(w > 0);
2062     g_assert(h > 0);
2063
2064     switch (anchor) {
2065     case OB_CORNER_TOPLEFT:
2066         break;
2067     case OB_CORNER_TOPRIGHT:
2068         x -= w - self->area.width;
2069         break;
2070     case OB_CORNER_BOTTOMLEFT:
2071         y -= h - self->area.height;
2072         break;
2073     case OB_CORNER_BOTTOMRIGHT:
2074         x -= w - self->area.width;
2075         y -= h - self->area.height;
2076         break;
2077     }
2078
2079     moved = x != self->area.x || y != self->area.y;
2080     resized = w != self->area.width || h != self->area.height;
2081
2082     oldw = self->area.width;
2083     oldh = self->area.height;
2084     RECT_SET(self->area, x, y, w, h);
2085
2086     /* for app-requested resizes, always resize if 'resized' is true.
2087        for user-requested ones, only resize if final is true, or when
2088        resizing in redraw mode */
2089     send_resize_client = ((!user && resized) ||
2090                           (user && (final ||
2091                                     (resized && config_redraw_resize))));
2092
2093     /* if the client is enlarging, the resize the client before the frame */
2094     if (send_resize_client && user && (w > oldw || h > oldh))
2095         XResizeWindow(ob_display, self->window, MAX(w, oldw), MAX(h, oldh));
2096
2097     /* move/resize the frame to match the request */
2098     if (self->frame) {
2099         if (self->decorations != fdecor || self->max_horz != fhorz)
2100             moved = resized = TRUE;
2101
2102         if (moved || resized)
2103             frame_adjust_area(self->frame, moved, resized, FALSE);
2104
2105         if (!resized && (force_reply || ((!user && moved) || (user && final))))
2106         {
2107             XEvent event;
2108             event.type = ConfigureNotify;
2109             event.xconfigure.display = ob_display;
2110             event.xconfigure.event = self->window;
2111             event.xconfigure.window = self->window;
2112
2113             /* root window real coords */
2114             event.xconfigure.x = self->frame->area.x + self->frame->size.left -
2115                 self->border_width;
2116             event.xconfigure.y = self->frame->area.y + self->frame->size.top -
2117                 self->border_width;
2118             event.xconfigure.width = w;
2119             event.xconfigure.height = h;
2120             event.xconfigure.border_width = 0;
2121             event.xconfigure.above = self->frame->plate;
2122             event.xconfigure.override_redirect = FALSE;
2123             XSendEvent(event.xconfigure.display, event.xconfigure.window,
2124                        FALSE, StructureNotifyMask, &event);
2125         }
2126     }
2127
2128     /* if the client is shrinking, then resize the frame before the client */
2129     if (send_resize_client && (!user || (w <= oldw || h <= oldh)))
2130         XResizeWindow(ob_display, self->window, w, h);
2131
2132     XFlush(ob_display);
2133 }
2134
2135 void client_fullscreen(ObClient *self, gboolean fs, gboolean savearea)
2136 {
2137     int x, y, w, h;
2138
2139     if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) || /* can't */
2140         self->fullscreen == fs) return;                   /* already done */
2141
2142     self->fullscreen = fs;
2143     client_change_state(self); /* change the state hints on the client,
2144                                   and adjust out layer/stacking */
2145
2146     if (fs) {
2147         if (savearea)
2148             self->pre_fullscreen_area = self->area;
2149
2150         /* these are not actually used cuz client_configure will set them
2151            as appropriate when the window is fullscreened */
2152         x = y = w = h = 0;
2153     } else {
2154         Rect *a;
2155
2156         if (self->pre_fullscreen_area.width > 0 &&
2157             self->pre_fullscreen_area.height > 0)
2158         {
2159             x = self->pre_fullscreen_area.x;
2160             y = self->pre_fullscreen_area.y;
2161             w = self->pre_fullscreen_area.width;
2162             h = self->pre_fullscreen_area.height;
2163             RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0);
2164         } else {
2165             /* pick some fallbacks... */
2166             a = screen_area_monitor(self->desktop, 0);
2167             x = a->x + a->width / 4;
2168             y = a->y + a->height / 4;
2169             w = a->width / 2;
2170             h = a->height / 2;
2171         }
2172     }
2173
2174     client_setup_decor_and_functions(self);
2175
2176     client_move_resize(self, x, y, w, h);
2177
2178     /* try focus us when we go into fullscreen mode */
2179     client_focus(self);
2180 }
2181
2182 static void client_iconify_recursive(ObClient *self,
2183                                      gboolean iconic, gboolean curdesk)
2184 {
2185     GSList *it;
2186     gboolean changed = FALSE;
2187
2188
2189     if (self->iconic != iconic) {
2190         ob_debug("%sconifying window: 0x%lx\n", (iconic ? "I" : "Uni"),
2191                  self->window);
2192
2193         self->iconic = iconic;
2194
2195         if (iconic) {
2196             if (self->functions & OB_CLIENT_FUNC_ICONIFY) {
2197                 long old;
2198
2199                 old = self->wmstate;
2200                 self->wmstate = IconicState;
2201                 if (old != self->wmstate)
2202                     PROP_MSG(self->window, kde_wm_change_state,
2203                              self->wmstate, 1, 0, 0);
2204
2205                 /* update the focus lists.. iconic windows go to the bottom of
2206                    the list, put the new iconic window at the 'top of the
2207                    bottom'. */
2208                 focus_order_to_top(self);
2209
2210                 changed = TRUE;
2211             }
2212         } else {
2213             long old;
2214
2215             if (curdesk)
2216                 client_set_desktop(self, screen_desktop, FALSE);
2217
2218             old = self->wmstate;
2219             self->wmstate = self->shaded ? IconicState : NormalState;
2220             if (old != self->wmstate)
2221                 PROP_MSG(self->window, kde_wm_change_state,
2222                          self->wmstate, 1, 0, 0);
2223
2224             /* this puts it after the current focused window */
2225             focus_order_remove(self);
2226             focus_order_add_new(self);
2227
2228             /* this is here cuz with the VIDMODE extension, the viewport can
2229                change while a fullscreen window is iconic, and when it
2230                uniconifies, it would be nice if it did so to the new position
2231                of the viewport */
2232             client_reconfigure(self);
2233
2234             changed = TRUE;
2235         }
2236     }
2237
2238     if (changed) {
2239         client_change_state(self);
2240         client_showhide(self);
2241         screen_update_areas();
2242     }
2243
2244     /* iconify all transients */
2245     for (it = self->transients; it != NULL; it = it->next)
2246         if (it->data != self) client_iconify_recursive(it->data,
2247                                                        iconic, curdesk);
2248 }
2249
2250 void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk)
2251 {
2252     /* move up the transient chain as far as possible first */
2253     self = client_search_top_transient(self);
2254
2255     client_iconify_recursive(client_search_top_transient(self),
2256                              iconic, curdesk);
2257 }
2258
2259 void client_maximize(ObClient *self, gboolean max, int dir, gboolean savearea)
2260 {
2261     int x, y, w, h;
2262      
2263     g_assert(dir == 0 || dir == 1 || dir == 2);
2264     if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE)) return; /* can't */
2265
2266     /* check if already done */
2267     if (max) {
2268         if (dir == 0 && self->max_horz && self->max_vert) return;
2269         if (dir == 1 && self->max_horz) return;
2270         if (dir == 2 && self->max_vert) return;
2271     } else {
2272         if (dir == 0 && !self->max_horz && !self->max_vert) return;
2273         if (dir == 1 && !self->max_horz) return;
2274         if (dir == 2 && !self->max_vert) return;
2275     }
2276
2277     /* we just tell it to configure in the same place and client_configure
2278        worries about filling the screen with the window */
2279     x = self->area.x;
2280     y = self->area.y;
2281     w = self->area.width;
2282     h = self->area.height;
2283
2284     if (max) {
2285         if (savearea) {
2286             if ((dir == 0 || dir == 1) && !self->max_horz) { /* horz */
2287                 RECT_SET(self->pre_max_area,
2288                          self->area.x, self->pre_max_area.y,
2289                          self->area.width, self->pre_max_area.height);
2290             }
2291             if ((dir == 0 || dir == 2) && !self->max_vert) { /* vert */
2292                 RECT_SET(self->pre_max_area,
2293                          self->pre_max_area.x, self->area.y,
2294                          self->pre_max_area.width, self->area.height);
2295             }
2296         }
2297     } else {
2298         Rect *a;
2299
2300         a = screen_area_monitor(self->desktop, 0);
2301         if ((dir == 0 || dir == 1) && self->max_horz) { /* horz */
2302             if (self->pre_max_area.width > 0) {
2303                 x = self->pre_max_area.x;
2304                 w = self->pre_max_area.width;
2305
2306                 RECT_SET(self->pre_max_area, 0, self->pre_max_area.y,
2307                          0, self->pre_max_area.height);
2308             } else {
2309                 /* pick some fallbacks... */
2310                 x = a->x + a->width / 4;
2311                 w = a->width / 2;
2312             }
2313         }
2314         if ((dir == 0 || dir == 2) && self->max_vert) { /* vert */
2315             if (self->pre_max_area.height > 0) {
2316                 y = self->pre_max_area.y;
2317                 h = self->pre_max_area.height;
2318
2319                 RECT_SET(self->pre_max_area, self->pre_max_area.x, 0,
2320                          self->pre_max_area.width, 0);
2321             } else {
2322                 /* pick some fallbacks... */
2323                 y = a->y + a->height / 4;
2324                 h = a->height / 2;
2325             }
2326         }
2327     }
2328
2329     if (dir == 0 || dir == 1) /* horz */
2330         self->max_horz = max;
2331     if (dir == 0 || dir == 2) /* vert */
2332         self->max_vert = max;
2333
2334     client_change_state(self); /* change the state hints on the client */
2335
2336     client_setup_decor_and_functions(self);
2337
2338     client_move_resize(self, x, y, w, h);
2339 }
2340
2341 void client_shade(ObClient *self, gboolean shade)
2342 {
2343     if ((!(self->functions & OB_CLIENT_FUNC_SHADE) &&
2344          shade) ||                         /* can't shade */
2345         self->shaded == shade) return;     /* already done */
2346
2347     /* when we're iconic, don't change the wmstate */
2348     if (!self->iconic) {
2349         long old;
2350
2351         old = self->wmstate;
2352         self->wmstate = shade ? IconicState : NormalState;
2353         if (old != self->wmstate)
2354             PROP_MSG(self->window, kde_wm_change_state,
2355                      self->wmstate, 1, 0, 0);
2356     }
2357
2358     self->shaded = shade;
2359     client_change_state(self);
2360     /* resize the frame to just the titlebar */
2361     frame_adjust_area(self->frame, FALSE, FALSE, FALSE);
2362 }
2363
2364 void client_close(ObClient *self)
2365 {
2366     XEvent ce;
2367
2368     if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return;
2369
2370     /* in the case that the client provides no means to requesting that it
2371        close, we just kill it */
2372     if (!self->delete_window)
2373         client_kill(self);
2374     
2375     /*
2376       XXX: itd be cool to do timeouts and shit here for killing the client's
2377       process off
2378       like... if the window is around after 5 seconds, then the close button
2379       turns a nice red, and if this function is called again, the client is
2380       explicitly killed.
2381     */
2382
2383     ce.xclient.type = ClientMessage;
2384     ce.xclient.message_type =  prop_atoms.wm_protocols;
2385     ce.xclient.display = ob_display;
2386     ce.xclient.window = self->window;
2387     ce.xclient.format = 32;
2388     ce.xclient.data.l[0] = prop_atoms.wm_delete_window;
2389     ce.xclient.data.l[1] = event_lasttime;
2390     ce.xclient.data.l[2] = 0l;
2391     ce.xclient.data.l[3] = 0l;
2392     ce.xclient.data.l[4] = 0l;
2393     XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
2394 }
2395
2396 void client_kill(ObClient *self)
2397 {
2398     XKillClient(ob_display, self->window);
2399 }
2400
2401 void client_set_desktop_recursive(ObClient *self,
2402                                   guint target, gboolean donthide)
2403 {
2404     guint old;
2405     GSList *it;
2406
2407     if (target != self->desktop) {
2408
2409         ob_debug("Setting desktop %u\n", target+1);
2410
2411         g_assert(target < screen_num_desktops || target == DESKTOP_ALL);
2412
2413         /* remove from the old desktop(s) */
2414         focus_order_remove(self);
2415
2416         old = self->desktop;
2417         self->desktop = target;
2418         PROP_SET32(self->window, net_wm_desktop, cardinal, target);
2419         /* the frame can display the current desktop state */
2420         frame_adjust_state(self->frame);
2421         /* 'move' the window to the new desktop */
2422         if (!donthide)
2423             client_showhide(self);
2424         /* raise if it was not already on the desktop */
2425         if (old != DESKTOP_ALL)
2426             client_raise(self);
2427         screen_update_areas();
2428
2429         /* add to the new desktop(s) */
2430         if (config_focus_new)
2431             focus_order_to_top(self);
2432         else
2433             focus_order_to_bottom(self);
2434     }
2435
2436     /* move all transients */
2437     for (it = self->transients; it != NULL; it = it->next)
2438         if (it->data != self) client_set_desktop_recursive(it->data,
2439                                                            target, donthide);
2440 }
2441
2442 void client_set_desktop(ObClient *self, guint target, gboolean donthide)
2443 {
2444     client_set_desktop_recursive(client_search_top_transient(self),
2445                                  target, donthide);
2446 }
2447
2448 ObClient *client_search_modal_child(ObClient *self)
2449 {
2450     GSList *it;
2451     ObClient *ret;
2452   
2453     for (it = self->transients; it != NULL; it = it->next) {
2454         ObClient *c = it->data;
2455         if ((ret = client_search_modal_child(c))) return ret;
2456         if (c->modal) return c;
2457     }
2458     return NULL;
2459 }
2460
2461 gboolean client_validate(ObClient *self)
2462 {
2463     XEvent e; 
2464
2465     XSync(ob_display, FALSE); /* get all events on the server */
2466
2467     if (XCheckTypedWindowEvent(ob_display, self->window, DestroyNotify, &e) ||
2468         XCheckTypedWindowEvent(ob_display, self->window, UnmapNotify, &e)) {
2469         XPutBackEvent(ob_display, &e);
2470         return FALSE;
2471     }
2472
2473     return TRUE;
2474 }
2475
2476 void client_set_wm_state(ObClient *self, long state)
2477 {
2478     if (state == self->wmstate) return; /* no change */
2479   
2480     switch (state) {
2481     case IconicState:
2482         client_iconify(self, TRUE, TRUE);
2483         break;
2484     case NormalState:
2485         client_iconify(self, FALSE, TRUE);
2486         break;
2487     }
2488 }
2489
2490 void client_set_state(ObClient *self, Atom action, long data1, long data2)
2491 {
2492     gboolean shaded = self->shaded;
2493     gboolean fullscreen = self->fullscreen;
2494     gboolean undecorated = self->undecorated;
2495     gboolean max_horz = self->max_horz;
2496     gboolean max_vert = self->max_vert;
2497     gboolean modal = self->modal;
2498     int i;
2499
2500     if (!(action == prop_atoms.net_wm_state_add ||
2501           action == prop_atoms.net_wm_state_remove ||
2502           action == prop_atoms.net_wm_state_toggle))
2503         /* an invalid action was passed to the client message, ignore it */
2504         return; 
2505
2506     for (i = 0; i < 2; ++i) {
2507         Atom state = i == 0 ? data1 : data2;
2508     
2509         if (!state) continue;
2510
2511         /* if toggling, then pick whether we're adding or removing */
2512         if (action == prop_atoms.net_wm_state_toggle) {
2513             if (state == prop_atoms.net_wm_state_modal)
2514                 action = modal ? prop_atoms.net_wm_state_remove :
2515                     prop_atoms.net_wm_state_add;
2516             else if (state == prop_atoms.net_wm_state_maximized_vert)
2517                 action = self->max_vert ? prop_atoms.net_wm_state_remove :
2518                     prop_atoms.net_wm_state_add;
2519             else if (state == prop_atoms.net_wm_state_maximized_horz)
2520                 action = self->max_horz ? prop_atoms.net_wm_state_remove :
2521                     prop_atoms.net_wm_state_add;
2522             else if (state == prop_atoms.net_wm_state_shaded)
2523                 action = shaded ? prop_atoms.net_wm_state_remove :
2524                     prop_atoms.net_wm_state_add;
2525             else if (state == prop_atoms.net_wm_state_skip_taskbar)
2526                 action = self->skip_taskbar ?
2527                     prop_atoms.net_wm_state_remove :
2528                     prop_atoms.net_wm_state_add;
2529             else if (state == prop_atoms.net_wm_state_skip_pager)
2530                 action = self->skip_pager ?
2531                     prop_atoms.net_wm_state_remove :
2532                     prop_atoms.net_wm_state_add;
2533             else if (state == prop_atoms.net_wm_state_fullscreen)
2534                 action = fullscreen ?
2535                     prop_atoms.net_wm_state_remove :
2536                     prop_atoms.net_wm_state_add;
2537             else if (state == prop_atoms.net_wm_state_above)
2538                 action = self->above ? prop_atoms.net_wm_state_remove :
2539                     prop_atoms.net_wm_state_add;
2540             else if (state == prop_atoms.net_wm_state_below)
2541                 action = self->below ? prop_atoms.net_wm_state_remove :
2542                     prop_atoms.net_wm_state_add;
2543             else if (state == prop_atoms.ob_wm_state_undecorated)
2544                 action = undecorated ? prop_atoms.net_wm_state_remove :
2545                     prop_atoms.net_wm_state_add;
2546         }
2547     
2548         if (action == prop_atoms.net_wm_state_add) {
2549             if (state == prop_atoms.net_wm_state_modal) {
2550                 modal = TRUE;
2551             } else if (state == prop_atoms.net_wm_state_maximized_vert) {
2552                 max_vert = TRUE;
2553             } else if (state == prop_atoms.net_wm_state_maximized_horz) {
2554                 max_horz = TRUE;
2555             } else if (state == prop_atoms.net_wm_state_shaded) {
2556                 shaded = TRUE;
2557             } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
2558                 self->skip_taskbar = TRUE;
2559             } else if (state == prop_atoms.net_wm_state_skip_pager) {
2560                 self->skip_pager = TRUE;
2561             } else if (state == prop_atoms.net_wm_state_fullscreen) {
2562                 fullscreen = TRUE;
2563             } else if (state == prop_atoms.net_wm_state_above) {
2564                 self->above = TRUE;
2565             } else if (state == prop_atoms.net_wm_state_below) {
2566                 self->below = TRUE;
2567             } else if (state == prop_atoms.ob_wm_state_undecorated) {
2568                 undecorated = TRUE;
2569             }
2570
2571         } else { /* action == prop_atoms.net_wm_state_remove */
2572             if (state == prop_atoms.net_wm_state_modal) {
2573                 modal = FALSE;
2574             } else if (state == prop_atoms.net_wm_state_maximized_vert) {
2575                 max_vert = FALSE;
2576             } else if (state == prop_atoms.net_wm_state_maximized_horz) {
2577                 max_horz = FALSE;
2578             } else if (state == prop_atoms.net_wm_state_shaded) {
2579                 shaded = FALSE;
2580             } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
2581                 self->skip_taskbar = FALSE;
2582             } else if (state == prop_atoms.net_wm_state_skip_pager) {
2583                 self->skip_pager = FALSE;
2584             } else if (state == prop_atoms.net_wm_state_fullscreen) {
2585                 fullscreen = FALSE;
2586             } else if (state == prop_atoms.net_wm_state_above) {
2587                 self->above = FALSE;
2588             } else if (state == prop_atoms.net_wm_state_below) {
2589                 self->below = FALSE;
2590             } else if (state == prop_atoms.ob_wm_state_undecorated) {
2591                 undecorated = FALSE;
2592             }
2593         }
2594     }
2595     if (max_horz != self->max_horz || max_vert != self->max_vert) {
2596         if (max_horz != self->max_horz && max_vert != self->max_vert) {
2597             /* toggling both */
2598             if (max_horz == max_vert) { /* both going the same way */
2599                 client_maximize(self, max_horz, 0, TRUE);
2600             } else {
2601                 client_maximize(self, max_horz, 1, TRUE);
2602                 client_maximize(self, max_vert, 2, TRUE);
2603             }
2604         } else {
2605             /* toggling one */
2606             if (max_horz != self->max_horz)
2607                 client_maximize(self, max_horz, 1, TRUE);
2608             else
2609                 client_maximize(self, max_vert, 2, TRUE);
2610         }
2611     }
2612     /* change fullscreen state before shading, as it will affect if the window
2613        can shade or not */
2614     if (fullscreen != self->fullscreen)
2615         client_fullscreen(self, fullscreen, TRUE);
2616     if (shaded != self->shaded)
2617         client_shade(self, shaded);
2618     if (undecorated != self->undecorated)
2619         client_set_undecorated(self, undecorated);
2620     if (modal != self->modal) {
2621         self->modal = modal;
2622         /* when a window changes modality, then its stacking order with its
2623            transients needs to change */
2624         client_raise(self);
2625     }
2626     client_calc_layer(self);
2627     client_change_state(self); /* change the hint to reflect these changes */
2628 }
2629
2630 ObClient *client_focus_target(ObClient *self)
2631 {
2632     ObClient *child;
2633      
2634     /* if we have a modal child, then focus it, not us */
2635     child = client_search_modal_child(client_search_top_transient(self));
2636     if (child) return child;
2637     return self;
2638 }
2639
2640 gboolean client_can_focus(ObClient *self)
2641 {
2642     XEvent ev;
2643
2644     /* choose the correct target */
2645     self = client_focus_target(self);
2646
2647     if (!self->frame->visible)
2648         return FALSE;
2649
2650     if (!(self->can_focus || self->focus_notify))
2651         return FALSE;
2652
2653     /* do a check to see if the window has already been unmapped or destroyed
2654        do this intelligently while watching out for unmaps we've generated
2655        (ignore_unmaps > 0) */
2656     if (XCheckTypedWindowEvent(ob_display, self->window,
2657                                DestroyNotify, &ev)) {
2658         XPutBackEvent(ob_display, &ev);
2659         return FALSE;
2660     }
2661     while (XCheckTypedWindowEvent(ob_display, self->window,
2662                                   UnmapNotify, &ev)) {
2663         if (self->ignore_unmaps) {
2664             self->ignore_unmaps--;
2665         } else {
2666             XPutBackEvent(ob_display, &ev);
2667             return FALSE;
2668         }
2669     }
2670
2671     return TRUE;
2672 }
2673
2674 gboolean client_focus(ObClient *self)
2675 {
2676     /* choose the correct target */
2677     self = client_focus_target(self);
2678
2679     if (!client_can_focus(self)) {
2680         if (!self->frame->visible) {
2681             /* update the focus lists */
2682             focus_order_to_top(self);
2683         }
2684         return FALSE;
2685     }
2686
2687     if (self->can_focus) {
2688         /* RevertToPointerRoot causes much more headache than RevertToNone, so
2689            I choose to use it always, hopefully to find errors quicker, if any
2690            are left. (I hate X. I hate focus events.)
2691            
2692            Update: Changing this to RevertToNone fixed a bug with mozilla (bug
2693            #799. So now it is RevertToNone again.
2694         */
2695         XSetInputFocus(ob_display, self->window, RevertToNone,
2696                        event_lasttime);
2697     }
2698
2699     if (self->focus_notify) {
2700         XEvent ce;
2701         ce.xclient.type = ClientMessage;
2702         ce.xclient.message_type = prop_atoms.wm_protocols;
2703         ce.xclient.display = ob_display;
2704         ce.xclient.window = self->window;
2705         ce.xclient.format = 32;
2706         ce.xclient.data.l[0] = prop_atoms.wm_take_focus;
2707         ce.xclient.data.l[1] = event_lasttime;
2708         ce.xclient.data.l[2] = 0l;
2709         ce.xclient.data.l[3] = 0l;
2710         ce.xclient.data.l[4] = 0l;
2711         XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
2712     }
2713
2714 #ifdef DEBUG_FOCUS
2715     ob_debug("%sively focusing %lx at %d\n",
2716              (self->can_focus ? "act" : "pass"),
2717              self->window, (int) event_lasttime);
2718 #endif
2719
2720     /* Cause the FocusIn to come back to us. Important for desktop switches,
2721        since otherwise we'll have no FocusIn on the queue and send it off to
2722        the focus_backup. */
2723     XSync(ob_display, FALSE);
2724     return TRUE;
2725 }
2726
2727 void client_unfocus(ObClient *self)
2728 {
2729     if (focus_client == self) {
2730 #ifdef DEBUG_FOCUS
2731         ob_debug("client_unfocus for %lx\n", self->window);
2732 #endif
2733         focus_fallback(OB_FOCUS_FALLBACK_UNFOCUSING);
2734     }
2735 }
2736
2737 void client_activate(ObClient *self, gboolean here)
2738 {
2739     if (client_normal(self) && screen_showing_desktop)
2740         screen_show_desktop(FALSE);
2741     if (self->iconic)
2742         client_iconify(self, FALSE, here);
2743     if (self->desktop != DESKTOP_ALL &&
2744         self->desktop != screen_desktop) {
2745         if (here)
2746             client_set_desktop(self, screen_desktop, FALSE);
2747         else
2748             screen_set_desktop(self->desktop);
2749     } else if (!self->frame->visible)
2750         /* if its not visible for other reasons, then don't mess
2751            with it */
2752         return;
2753     if (self->shaded)
2754         client_shade(self, FALSE);
2755
2756     client_focus(self);
2757
2758     /* we do this an action here. this is rather important. this is because
2759        we want the results from the focus change to take place BEFORE we go
2760        about raising the window. when a fullscreen window loses focus, we need
2761        this or else the raise wont be able to raise above the to-lose-focus
2762        fullscreen window. */
2763     client_raise(self);
2764 }
2765
2766 void client_raise(ObClient *self)
2767 {
2768     action_run_string("Raise", self);
2769 }
2770
2771 void client_lower(ObClient *self)
2772 {
2773     action_run_string("Raise", self);
2774 }
2775
2776 gboolean client_focused(ObClient *self)
2777 {
2778     return self == focus_client;
2779 }
2780
2781 static ObClientIcon* client_icon_recursive(ObClient *self, int w, int h)
2782 {
2783     guint i;
2784     /* si is the smallest image >= req */
2785     /* li is the largest image < req */
2786     unsigned long size, smallest = 0xffffffff, largest = 0, si = 0, li = 0;
2787
2788     if (!self->nicons) {
2789         ObClientIcon *parent = NULL;
2790
2791         if (self->transient_for) {
2792             if (self->transient_for != OB_TRAN_GROUP)
2793                 parent = client_icon_recursive(self->transient_for, w, h);
2794             else {
2795                 GSList *it;
2796                 for (it = self->group->members; it; it = g_slist_next(it)) {
2797                     ObClient *c = it->data;
2798                     if (c != self && !c->transient_for) {
2799                         if ((parent = client_icon_recursive(c, w, h)))
2800                             break;
2801                     }
2802                 }
2803             }
2804         }
2805         
2806         return parent;
2807     }
2808
2809     for (i = 0; i < self->nicons; ++i) {
2810         size = self->icons[i].width * self->icons[i].height;
2811         if (size < smallest && size >= (unsigned)(w * h)) {
2812             smallest = size;
2813             si = i;
2814         }
2815         if (size > largest && size <= (unsigned)(w * h)) {
2816             largest = size;
2817             li = i;
2818         }
2819     }
2820     if (largest == 0) /* didnt find one smaller than the requested size */
2821         return &self->icons[si];
2822     return &self->icons[li];
2823 }
2824
2825 const ObClientIcon* client_icon(ObClient *self, int w, int h)
2826 {
2827     ObClientIcon *ret;
2828     static ObClientIcon deficon;
2829
2830     if (!(ret = client_icon_recursive(self, w, h))) {
2831         deficon.width = deficon.height = 48;
2832         deficon.data = ob_rr_theme->def_win_icon;
2833         ret = &deficon;
2834     }
2835     return ret;
2836 }
2837
2838 /* this be mostly ripped from fvwm */
2839 ObClient *client_find_directional(ObClient *c, ObDirection dir) 
2840 {
2841     int my_cx, my_cy, his_cx, his_cy;
2842     int offset = 0;
2843     int distance = 0;
2844     int score, best_score;
2845     ObClient *best_client, *cur;
2846     GList *it;
2847
2848     if(!client_list)
2849         return NULL;
2850
2851     /* first, find the centre coords of the currently focused window */
2852     my_cx = c->frame->area.x + c->frame->area.width / 2;
2853     my_cy = c->frame->area.y + c->frame->area.height / 2;
2854
2855     best_score = -1;
2856     best_client = NULL;
2857
2858     for(it = g_list_first(client_list); it; it = it->next) {
2859         cur = it->data;
2860
2861         /* the currently selected window isn't interesting */
2862         if(cur == c)
2863             continue;
2864         if (!client_normal(cur))
2865             continue;
2866         if(c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
2867             continue;
2868         if(cur->iconic)
2869             continue;
2870         if(client_focus_target(cur) == cur &&
2871            !(cur->can_focus || cur->focus_notify))
2872             continue;
2873
2874         /* find the centre coords of this window, from the
2875          * currently focused window's point of view */
2876         his_cx = (cur->frame->area.x - my_cx)
2877             + cur->frame->area.width / 2;
2878         his_cy = (cur->frame->area.y - my_cy)
2879             + cur->frame->area.height / 2;
2880
2881         if(dir == OB_DIRECTION_NORTHEAST || dir == OB_DIRECTION_SOUTHEAST ||
2882            dir == OB_DIRECTION_SOUTHWEST || dir == OB_DIRECTION_NORTHWEST) {
2883             int tx;
2884             /* Rotate the diagonals 45 degrees counterclockwise.
2885              * To do this, multiply the matrix /+h +h\ with the
2886              * vector (x y).                   \-h +h/
2887              * h = sqrt(0.5). We can set h := 1 since absolute
2888              * distance doesn't matter here. */
2889             tx = his_cx + his_cy;
2890             his_cy = -his_cx + his_cy;
2891             his_cx = tx;
2892         }
2893
2894         switch(dir) {
2895         case OB_DIRECTION_NORTH:
2896         case OB_DIRECTION_SOUTH:
2897         case OB_DIRECTION_NORTHEAST:
2898         case OB_DIRECTION_SOUTHWEST:
2899             offset = (his_cx < 0) ? -his_cx : his_cx;
2900             distance = ((dir == OB_DIRECTION_NORTH ||
2901                          dir == OB_DIRECTION_NORTHEAST) ?
2902                         -his_cy : his_cy);
2903             break;
2904         case OB_DIRECTION_EAST:
2905         case OB_DIRECTION_WEST:
2906         case OB_DIRECTION_SOUTHEAST:
2907         case OB_DIRECTION_NORTHWEST:
2908             offset = (his_cy < 0) ? -his_cy : his_cy;
2909             distance = ((dir == OB_DIRECTION_WEST ||
2910                          dir == OB_DIRECTION_NORTHWEST) ?
2911                         -his_cx : his_cx);
2912             break;
2913         }
2914
2915         /* the target must be in the requested direction */
2916         if(distance <= 0)
2917             continue;
2918
2919         /* Calculate score for this window.  The smaller the better. */
2920         score = distance + offset;
2921
2922         /* windows more than 45 degrees off the direction are
2923          * heavily penalized and will only be chosen if nothing
2924          * else within a million pixels */
2925         if(offset > distance)
2926             score += 1000000;
2927
2928         if(best_score == -1 || score < best_score)
2929             best_client = cur,
2930                 best_score = score;
2931     }
2932
2933     return best_client;
2934 }
2935
2936 void client_set_layer(ObClient *self, int layer)
2937 {
2938     if (layer < 0) {
2939         self->below = TRUE;
2940         self->above = FALSE;
2941     } else if (layer == 0) {
2942         self->below = self->above = FALSE;
2943     } else {
2944         self->below = FALSE;
2945         self->above = TRUE;
2946     }
2947     client_calc_layer(self);
2948     client_change_state(self); /* reflect this in the state hints */
2949 }
2950
2951 void client_set_undecorated(ObClient *self, gboolean undecorated)
2952 {
2953     if (self->undecorated != undecorated) {
2954         self->undecorated = undecorated;
2955         client_setup_decor_and_functions(self);
2956         client_change_state(self); /* reflect this in the state hints */
2957     }
2958 }
2959
2960 guint client_monitor(ObClient *self)
2961 {
2962     guint i;
2963     guint most = 0;
2964     guint mostv = 0;
2965
2966     for (i = 0; i < screen_num_monitors; ++i) {
2967         Rect *area = screen_physical_area_monitor(i);
2968         if (RECT_INTERSECTS_RECT(*area, self->frame->area)) {
2969             Rect r;
2970             guint v;
2971
2972             RECT_SET_INTERSECTION(r, *area, self->frame->area);
2973             v = r.width * r.height;
2974
2975             if (v > mostv) {
2976                 mostv = v;
2977                 most = i;
2978             }
2979         }
2980     }
2981     return most;
2982 }
2983
2984 ObClient *client_search_top_transient(ObClient *self)
2985 {
2986     /* move up the transient chain as far as possible */
2987     if (self->transient_for) {
2988         if (self->transient_for != OB_TRAN_GROUP) {
2989             return client_search_top_transient(self->transient_for);
2990         } else {
2991             GSList *it;
2992
2993             g_assert(self->group);
2994
2995             for (it = self->group->members; it; it = it->next) {
2996                 ObClient *c = it->data;
2997
2998                 /* checking transient_for prevents infinate loops! */
2999                 if (c != self && !c->transient_for)
3000                     break;
3001             }
3002             if (it)
3003                 return it->data;
3004         }
3005     }
3006
3007     return self;
3008 }
3009
3010 ObClient *client_search_focus_parent(ObClient *self)
3011 {
3012     if (self->transient_for) {
3013         if (self->transient_for != OB_TRAN_GROUP) {
3014             if (client_focused(self->transient_for))
3015                 return self->transient_for;
3016         } else {
3017             GSList *it;
3018
3019             for (it = self->group->members; it; it = it->next) {
3020                 ObClient *c = it->data;
3021
3022                 /* checking transient_for prevents infinate loops! */
3023                 if (c != self && !c->transient_for)
3024                     if (client_focused(c))
3025                         return c;
3026             }
3027         }
3028     }
3029
3030     return NULL;
3031 }
3032
3033 ObClient *client_search_parent(ObClient *self, ObClient *search)
3034 {
3035     if (self->transient_for) {
3036         if (self->transient_for != OB_TRAN_GROUP) {
3037             if (self->transient_for == search)
3038                 return search;
3039         } else {
3040             GSList *it;
3041
3042             for (it = self->group->members; it; it = it->next) {
3043                 ObClient *c = it->data;
3044
3045                 /* checking transient_for prevents infinate loops! */
3046                 if (c != self && !c->transient_for)
3047                     if (c == search)
3048                         return search;
3049             }
3050         }
3051     }
3052
3053     return NULL;
3054 }
3055
3056 ObClient *client_search_transient(ObClient *self, ObClient *search)
3057 {
3058     GSList *sit;
3059
3060     for (sit = self->transients; sit; sit = g_slist_next(sit)) {
3061         if (sit->data == search)
3062             return search;
3063         if (client_search_transient(sit->data, search))
3064             return search;
3065     }
3066     return NULL;
3067 }
3068
3069 void client_update_sm_client_id(ObClient *self)
3070 {
3071     g_free(self->sm_client_id);
3072     self->sm_client_id = NULL;
3073
3074     if (!PROP_GETS(self->window, sm_client_id, locale, &self->sm_client_id) &&
3075         self->group)
3076         PROP_GETS(self->group->leader, sm_client_id, locale,
3077                   &self->sm_client_id);
3078 }
3079
3080 /* finds the nearest edge in the given direction from the current client
3081  * note to self: the edge is the -frame- edge (the actual one), not the
3082  * client edge.
3083  */
3084 int client_directional_edge_search(ObClient *c, ObDirection dir)
3085 {
3086     int dest;
3087     int my_edge_start, my_edge_end, my_offset;
3088     GList *it;
3089     Rect *a;
3090     
3091     if(!client_list)
3092         return -1;
3093
3094     a = screen_area(c->desktop);
3095
3096     switch(dir) {
3097     case OB_DIRECTION_NORTH:
3098         my_edge_start = c->frame->area.x;
3099         my_edge_end = c->frame->area.x + c->frame->area.width;
3100         my_offset = c->frame->area.y;
3101         
3102         /* default: top of screen */
3103         dest = a->y;
3104
3105         for(it = g_list_first(client_list); it; it = it->next) {
3106             int his_edge_start, his_edge_end, his_offset;
3107             ObClient *cur = it->data;
3108
3109             if(cur == c)
3110                 continue;
3111             if(!client_normal(cur))
3112                 continue;
3113             if(c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
3114                 continue;
3115             if(cur->iconic)
3116                 continue;
3117
3118             his_edge_start = cur->frame->area.x;
3119             his_edge_end = cur->frame->area.x + cur->frame->area.width;
3120             his_offset = cur->frame->area.y + cur->frame->area.height;
3121
3122             if(his_offset + 1 > my_offset)
3123                 continue;
3124
3125             if(his_offset < dest)
3126                 continue;
3127             
3128             if(his_edge_start >= my_edge_start &&
3129                his_edge_start <= my_edge_end)
3130                 dest = his_offset;
3131
3132             if(my_edge_start >= his_edge_start &&
3133                my_edge_start <= his_edge_end)
3134                 dest = his_offset;
3135
3136         }
3137         break;
3138     case OB_DIRECTION_SOUTH:
3139         my_edge_start = c->frame->area.x;
3140         my_edge_end = c->frame->area.x + c->frame->area.width;
3141         my_offset = c->frame->area.y + c->frame->area.height;
3142
3143         /* default: bottom of screen */
3144         dest = a->y + a->height;
3145
3146         for(it = g_list_first(client_list); it; it = it->next) {
3147             int his_edge_start, his_edge_end, his_offset;
3148             ObClient *cur = it->data;
3149
3150             if(cur == c)
3151                 continue;
3152             if(!client_normal(cur))
3153                 continue;
3154             if(c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
3155                 continue;
3156             if(cur->iconic)
3157                 continue;
3158
3159             his_edge_start = cur->frame->area.x;
3160             his_edge_end = cur->frame->area.x + cur->frame->area.width;
3161             his_offset = cur->frame->area.y;
3162
3163
3164             if(his_offset - 1 < my_offset)
3165                 continue;
3166             
3167             if(his_offset > dest)
3168                 continue;
3169             
3170             if(his_edge_start >= my_edge_start &&
3171                his_edge_start <= my_edge_end)
3172                 dest = his_offset;
3173
3174             if(my_edge_start >= his_edge_start &&
3175                my_edge_start <= his_edge_end)
3176                 dest = his_offset;
3177
3178         }
3179         break;
3180     case OB_DIRECTION_WEST:
3181         my_edge_start = c->frame->area.y;
3182         my_edge_end = c->frame->area.y + c->frame->area.height;
3183         my_offset = c->frame->area.x;
3184
3185         /* default: leftmost egde of screen */
3186         dest = a->x;
3187
3188         for(it = g_list_first(client_list); it; it = it->next) {
3189             int his_edge_start, his_edge_end, his_offset;
3190             ObClient *cur = it->data;
3191
3192             if(cur == c)
3193                 continue;
3194             if(!client_normal(cur))
3195                 continue;
3196             if(c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
3197                 continue;
3198             if(cur->iconic)
3199                 continue;
3200
3201             his_edge_start = cur->frame->area.y;
3202             his_edge_end = cur->frame->area.y + cur->frame->area.height;
3203             his_offset = cur->frame->area.x + cur->frame->area.width;
3204
3205             if(his_offset + 1 > my_offset)
3206                 continue;
3207             
3208             if(his_offset < dest)
3209                 continue;
3210             
3211             if(his_edge_start >= my_edge_start &&
3212                his_edge_start <= my_edge_end)
3213                 dest = his_offset;
3214
3215             if(my_edge_start >= his_edge_start &&
3216                my_edge_start <= his_edge_end)
3217                 dest = his_offset;
3218                 
3219
3220         }
3221         break;
3222     case OB_DIRECTION_EAST:
3223         my_edge_start = c->frame->area.y;
3224         my_edge_end = c->frame->area.y + c->frame->area.height;
3225         my_offset = c->frame->area.x + c->frame->area.width;
3226         
3227         /* default: rightmost edge of screen */
3228         dest = a->x + a->width;
3229
3230         for(it = g_list_first(client_list); it; it = it->next) {
3231             int his_edge_start, his_edge_end, his_offset;
3232             ObClient *cur = it->data;
3233
3234             if(cur == c)
3235                 continue;
3236             if(!client_normal(cur))
3237                 continue;
3238             if(c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
3239                 continue;
3240             if(cur->iconic)
3241                 continue;
3242
3243             his_edge_start = cur->frame->area.y;
3244             his_edge_end = cur->frame->area.y + cur->frame->area.height;
3245             his_offset = cur->frame->area.x;
3246
3247             if(his_offset - 1 < my_offset)
3248                 continue;
3249             
3250             if(his_offset > dest)
3251                 continue;
3252             
3253             if(his_edge_start >= my_edge_start &&
3254                his_edge_start <= my_edge_end)
3255                 dest = his_offset;
3256
3257             if(my_edge_start >= his_edge_start &&
3258                my_edge_start <= his_edge_end)
3259                 dest = his_offset;
3260
3261         }
3262         break;
3263     case OB_DIRECTION_NORTHEAST:
3264     case OB_DIRECTION_SOUTHEAST:
3265     case OB_DIRECTION_NORTHWEST:
3266     case OB_DIRECTION_SOUTHWEST:
3267         /* not implemented */
3268     default:
3269         g_assert_not_reached();
3270     }
3271     return dest;
3272 }
3273
3274 ObClient* client_under_pointer()
3275 {
3276     int x, y;
3277     GList *it;
3278     ObClient *ret = NULL;
3279
3280     if (screen_pointer_pos(&x, &y)) {
3281         for (it = stacking_list; it != NULL; it = it->next) {
3282             if (WINDOW_IS_CLIENT(it->data)) {
3283                 ObClient *c = WINDOW_AS_CLIENT(it->data);
3284                 if (c->frame->visible &&
3285                     RECT_CONTAINS(c->frame->area, x, y)) {
3286                     ret = c;
3287                     break;
3288                 }
3289             }
3290         }
3291     }
3292     return ret;
3293 }
3294
3295 gboolean client_has_group_siblings(ObClient *self)
3296 {
3297     return self->group && self->group->members->next;
3298 }