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