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