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