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