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