]> icculus.org git repositories - dana/openbox.git/blob - openbox/client.c
i lied.. fuck @ those gnome dialogs
[dana/openbox.git] / openbox / client.c
1 #include "client.h"
2 #include "screen.h"
3 #include "moveresize.h"
4 #include "prop.h"
5 #include "extensions.h"
6 #include "frame.h"
7 #include "event.h"
8 #include "grab.h"
9 #include "focus.h"
10 #include "stacking.h"
11 #include "dispatch.h"
12 #include "openbox.h"
13 #include "group.h"
14 #include "config.h"
15
16 #include <glib.h>
17 #include <X11/Xutil.h>
18
19 /*! The event mask to grab on client windows */
20 #define CLIENT_EVENTMASK (PropertyChangeMask | FocusChangeMask | \
21                           StructureNotifyMask)
22
23 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
24                                 ButtonMotionMask)
25
26 GList      *client_list      = NULL;
27 GHashTable *client_map       = NULL;
28
29 static Window *client_startup_stack_order = NULL;
30 static guint  client_startup_stack_size = 0;
31
32 static void client_get_all(Client *self);
33 static void client_toggle_border(Client *self, gboolean show);
34 static void client_get_area(Client *self);
35 static void client_get_desktop(Client *self);
36 static void client_get_state(Client *self);
37 static void client_get_shaped(Client *self);
38 static void client_get_mwm_hints(Client *self);
39 static void client_get_gravity(Client *self);
40 static void client_showhide(Client *self);
41 static void client_change_allowed_actions(Client *self);
42 static void client_change_state(Client *self);
43 static void client_move_onscreen(Client *self);
44 static Client *search_focus_tree(Client *node, Client *skip);
45 static void client_apply_startup_state(Client *self);
46 static Client *search_modal_tree(Client *node, Client *skip);
47
48 static guint map_hash(Window *w) { return *w; }
49 static gboolean map_key_comp(Window *w1, Window *w2) { return *w1 == *w2; }
50
51 void client_startup()
52 {
53     client_map = g_hash_table_new((GHashFunc)map_hash,
54                                   (GEqualFunc)map_key_comp);
55
56     /* save the stacking order on startup! */
57     PROP_GETA32(ob_root, net_client_list_stacking, window,
58                 (guint32**)&client_startup_stack_order,
59                 &client_startup_stack_size);
60
61     client_set_list();
62 }
63
64 void client_shutdown()
65 {
66     g_hash_table_destroy(client_map);
67 }
68
69 void client_set_list()
70 {
71     Window *windows, *win_it;
72     GList *it;
73     guint size = g_list_length(client_list);
74
75     /* create an array of the window ids */
76     if (size > 0) {
77         windows = g_new(Window, size);
78         win_it = windows;
79         for (it = client_list; it != NULL; it = it->next, ++win_it)
80             *win_it = ((Client*)it->data)->window;
81     } else
82         windows = NULL;
83
84     PROP_SETA32(ob_root, net_client_list, window, (guint32*)windows, size);
85
86     if (windows)
87         g_free(windows);
88
89     stacking_set_list();
90 }
91
92 void client_manage_all()
93 {
94     unsigned int i, j, nchild;
95     Window w, *children;
96     XWMHints *wmhints;
97     XWindowAttributes attrib;
98
99     XQueryTree(ob_display, ob_root, &w, &w, &children, &nchild);
100
101     /* remove all icon windows from the list */
102     for (i = 0; i < nchild; i++) {
103         if (children[i] == None) continue;
104         wmhints = XGetWMHints(ob_display, children[i]);
105         if (wmhints) {
106             if ((wmhints->flags & IconWindowHint) &&
107                 (wmhints->icon_window != children[i]))
108                 for (j = 0; j < nchild; j++)
109                     if (children[j] == wmhints->icon_window) {
110                         children[j] = None;
111                         break;
112                     }
113             XFree(wmhints);
114         }
115     }
116
117     for (i = 0; i < nchild; ++i) {
118         if (children[i] == None)
119             continue;
120         if (XGetWindowAttributes(ob_display, children[i], &attrib)) {
121             if (attrib.override_redirect) continue;
122
123             if (attrib.map_state != IsUnmapped)
124                 client_manage(children[i]);
125         }
126     }
127     XFree(children);
128
129     /* stack them as they were on startup!
130        why with stacking_lower? Why, because then windows who aren't in the
131        stacking list are on the top where you can see them instead of buried
132        at the bottom! */
133     for (i = client_startup_stack_size; i > 0; --i) {
134         Client *c;
135
136         w = client_startup_stack_order[i-1];
137         c = g_hash_table_lookup(client_map, &w);
138         if (c) stacking_lower(c);
139     }
140     g_free(client_startup_stack_order);
141     client_startup_stack_order = NULL;
142     client_startup_stack_size = 0;
143
144     if (config_focus_new)
145         focus_fallback(Fallback_NoFocus);
146 }
147
148 void client_manage(Window window)
149 {
150     Client *self;
151     XEvent e;
152     XWindowAttributes attrib;
153     XSetWindowAttributes attrib_set;
154 /*    XWMHints *wmhint; */
155     guint i;
156     Client *parent;
157
158     grab_server(TRUE);
159
160     /* check if it has already been unmapped by the time we started mapping
161        the grab does a sync so we don't have to here */
162     if (XCheckTypedWindowEvent(ob_display, window, DestroyNotify, &e) ||
163         XCheckTypedWindowEvent(ob_display, window, UnmapNotify, &e)) {
164         XPutBackEvent(ob_display, &e);
165
166         grab_server(FALSE);
167         return; /* don't manage it */
168     }
169
170     /* make sure it isn't an override-redirect window */
171     if (!XGetWindowAttributes(ob_display, window, &attrib) ||
172         attrib.override_redirect) {
173         grab_server(FALSE);
174         return; /* don't manage it */
175     }
176   
177 /*    /\* is the window a docking app *\/
178     if ((wmhint = XGetWMHints(ob_display, window))) {
179         if ((wmhint->flags & StateHint) &&
180             wmhint->initial_state == WithdrawnState) {
181             /\* XXX: make dock apps work! *\/
182             grab_server(FALSE);
183             XFree(wmhint);
184             return;
185         }
186         XFree(wmhint);
187     }
188 */
189     g_message("Managing window: %lx", window);
190
191     /* choose the events we want to receive on the CLIENT window */
192     attrib_set.event_mask = CLIENT_EVENTMASK;
193     attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK;
194     XChangeWindowAttributes(ob_display, window,
195                             CWEventMask|CWDontPropagate, &attrib_set);
196
197
198     /* create the Client struct, and populate it from the hints on the
199        window */
200     self = g_new(Client, 1);
201     self->window = window;
202     client_get_all(self);
203
204     /* remove the client's border (and adjust re gravity) */
205     client_toggle_border(self, FALSE);
206      
207     /* specify that if we exit, the window should not be destroyed and should
208        be reparented back to root automatically */
209     XChangeSaveSet(ob_display, window, SetModeInsert);
210
211     /* create the decoration frame for the client window */
212     self->frame = frame_new();
213
214     frame_grab_client(self->frame, self);
215
216     client_apply_startup_state(self);
217
218     grab_server(FALSE);
219      
220     client_list = g_list_append(client_list, self);
221     stacking_list = g_list_append(stacking_list, self);
222     g_assert(!g_hash_table_lookup(client_map, &self->window));
223     g_hash_table_insert(client_map, &self->window, self);
224
225     /* update the focus lists */
226     if (self->desktop == DESKTOP_ALL) {
227         for (i = 0; i < screen_num_desktops; ++i)
228             focus_order[i] = g_list_insert(focus_order[i], self, 1);
229     } else {
230         i = self->desktop;
231         focus_order[i] = g_list_insert(focus_order[i], self, 1);
232     }
233
234     stacking_raise(self);
235
236     screen_update_struts();
237
238     dispatch_client(Event_Client_New, self, 0, 0);
239
240     client_showhide(self);
241
242     /* focus the new window? */
243     if (ob_state != State_Starting) {
244         parent = NULL;
245
246         if (self->transient_for) {
247             if (self->transient_for != TRAN_GROUP) {/* transient of a window */
248                 parent = self->transient_for;
249             } else { /* transient of a group */
250                 GSList *it;
251
252                 for (it = self->group->members; it; it = it->next)
253                     if (it->data != self &&
254                         ((Client*)it->data)->transient_for != TRAN_GROUP)
255                         parent = it->data;
256             }
257         }
258         /* note the check against Type_Normal, not client_normal(self), which
259            would also include dialog types. in this case we want more strict
260            rules for focus */
261         if ((config_focus_new && self->type == Type_Normal) ||
262             (parent && (client_focused(parent) ||
263                         search_focus_tree(parent, parent)))) {
264             client_focus(self);
265         }
266     }
267
268     /* update the list hints */
269     client_set_list();
270
271     /* make sure the window is visible */
272     client_move_onscreen(self);
273
274     dispatch_client(Event_Client_Mapped, self, 0, 0);
275
276     g_message("Managed window 0x%lx", window);
277 }
278
279 void client_unmanage_all()
280 {
281     while (client_list != NULL)
282         client_unmanage(client_list->data);
283 }
284
285 void client_unmanage(Client *self)
286 {
287     guint i;
288     int j;
289     GSList *it;
290
291     g_message("Unmanaging window: %lx", self->window);
292
293     dispatch_client(Event_Client_Destroy, self, 0, 0);
294     g_assert(self != NULL);
295
296     /* remove the window from our save set */
297     XChangeSaveSet(ob_display, self->window, SetModeDelete);
298
299     /* we dont want events no more */
300     XSelectInput(ob_display, self->window, NoEventMask);
301
302     frame_hide(self->frame);
303
304     client_list = g_list_remove(client_list, self);
305     stacking_list = g_list_remove(stacking_list, self);
306     g_hash_table_remove(client_map, &self->window);
307
308     /* update the focus lists */
309     if (self->desktop == DESKTOP_ALL) {
310         for (i = 0; i < screen_num_desktops; ++i)
311             focus_order[i] = g_list_remove(focus_order[i], self);
312     } else {
313         i = self->desktop;
314         focus_order[i] = g_list_remove(focus_order[i], self);
315     }
316
317     /* once the client is out of the list, update the struts to remove it's
318        influence */
319     screen_update_struts();
320
321     /* tell our parent(s) that we're gone */
322     if (self->transient_for == TRAN_GROUP) { /* transient of group */
323         GSList *it;
324
325         for (it = self->group->members; it; it = it->next)
326             if (it->data != self)
327                 ((Client*)it->data)->transients =
328                     g_slist_remove(((Client*)it->data)->transients, self);
329     } else if (self->transient_for) {        /* transient of window */
330         self->transient_for->transients =
331             g_slist_remove(self->transient_for->transients, self);
332     }
333
334     /* tell our transients that we're gone */
335     for (it = self->transients; it != NULL; it = it->next) {
336         if (((Client*)it->data)->transient_for != TRAN_GROUP) {
337             ((Client*)it->data)->transient_for = NULL;
338             client_calc_layer(it->data);
339         }
340     }
341
342     if (moveresize_client == self)
343         moveresize_end(TRUE);
344
345     if (focus_client == self) {
346         XEvent e;
347
348         /* focus the last focused window on the desktop, and ignore enter
349            events from the unmap so it doesnt mess with the focus */
350         while (XCheckTypedEvent(ob_display, EnterNotify, &e));
351         client_unfocus(self);
352     }
353
354     /* remove from its group */
355     if (self->group) {
356         group_remove(self->group, self);
357         self->group = NULL;
358     }
359
360     /* dispatch the unmapped event */
361     dispatch_client(Event_Client_Unmapped, self, 0, 0);
362     g_assert(self != NULL);
363
364     /* give the client its border back */
365     client_toggle_border(self, TRUE);
366
367     /* reparent the window out of the frame, and free the frame */
368     frame_release_client(self->frame, self);
369     self->frame = NULL;
370      
371     if (ob_state != State_Exiting) {
372         /* these values should not be persisted across a window
373            unmapping/mapping */
374         prop_erase(self->window, prop_atoms.net_wm_desktop);
375         prop_erase(self->window, prop_atoms.net_wm_state);
376     } else {
377         /* if we're left in an iconic state, the client wont be mapped. this is
378            bad, since we will no longer be managing the window on restart */
379         if (self->iconic)
380             XMapWindow(ob_display, self->window);
381     }
382
383
384     g_message("Unmanaged window 0x%lx", self->window);
385
386     /* free all data allocated in the client struct */
387     g_slist_free(self->transients);
388     for (j = 0; j < self->nicons; ++j)
389         g_free(self->icons[j].data);
390     if (self->nicons > 0)
391         g_free(self->icons);
392     g_free(self->title);
393     g_free(self->icon_title);
394     g_free(self->name);
395     g_free(self->class);
396     g_free(self->role);
397     g_free(self);
398      
399     /* update the list hints */
400     client_set_list();
401 }
402
403 static void client_move_onscreen(Client *self)
404 {
405     Rect *a;
406     int x = self->frame->area.x, y = self->frame->area.y;
407
408     a = screen_area(self->desktop);
409     if (x >= a->x + a->width - 1)
410         x = a->x + a->width - self->frame->area.width;
411     if (y >= a->y + a->height - 1)
412         y = a->y + a->height - self->frame->area.height;
413     if (x + self->frame->area.width - 1 < a->x)
414         x = a->x;
415     if (y + self->frame->area.height - 1< a->y)
416         y = a->y;
417
418     frame_frame_gravity(self->frame, &x, &y); /* get where the client
419                                               should be */
420     client_configure(self , Corner_TopLeft, x, y,
421                      self->area.width, self->area.height,
422                      TRUE, TRUE);
423 }
424
425 static void client_toggle_border(Client *self, gboolean show)
426 {
427     /* adjust our idea of where the client is, based on its border. When the
428        border is removed, the client should now be considered to be in a
429        different position.
430        when re-adding the border to the client, the same operation needs to be
431        reversed. */
432     int oldx = self->area.x, oldy = self->area.y;
433     int x = oldx, y = oldy;
434     switch(self->gravity) {
435     default:
436     case NorthWestGravity:
437     case WestGravity:
438     case SouthWestGravity:
439         break;
440     case NorthEastGravity:
441     case EastGravity:
442     case SouthEastGravity:
443         if (show) x -= self->border_width * 2;
444         else      x += self->border_width * 2;
445         break;
446     case NorthGravity:
447     case SouthGravity:
448     case CenterGravity:
449     case ForgetGravity:
450     case StaticGravity:
451         if (show) x -= self->border_width;
452         else      x += self->border_width;
453         break;
454     }
455     switch(self->gravity) {
456     default:
457     case NorthWestGravity:
458     case NorthGravity:
459     case NorthEastGravity:
460         break;
461     case SouthWestGravity:
462     case SouthGravity:
463     case SouthEastGravity:
464         if (show) y -= self->border_width * 2;
465         else      y += self->border_width * 2;
466         break;
467     case WestGravity:
468     case EastGravity:
469     case CenterGravity:
470     case ForgetGravity:
471     case StaticGravity:
472         if (show) y -= self->border_width;
473         else      y += self->border_width;
474         break;
475     }
476     self->area.x = x;
477     self->area.y = y;
478
479     if (show) {
480         XSetWindowBorderWidth(ob_display, self->window, self->border_width);
481
482         /* move the client so it is back it the right spot _with_ its
483            border! */
484         if (x != oldx || y != oldy)
485             XMoveWindow(ob_display, self->window, x, y);
486     } else
487         XSetWindowBorderWidth(ob_display, self->window, 0);
488 }
489
490
491 static void client_get_all(Client *self)
492 {
493     /* update EVERYTHING!! */
494
495     self->ignore_unmaps = 0;
496   
497     /* defaults */
498     self->frame = NULL;
499     self->title = self->icon_title = NULL;
500     self->name = self->class = self->role = NULL;
501     self->wmstate = NormalState;
502     self->transient = FALSE;
503     self->transients = NULL;
504     self->transient_for = NULL;
505     self->layer = -1;
506     self->urgent = FALSE;
507     self->positioned = FALSE;
508     self->disabled_decorations = 0;
509     self->group = NULL;
510     self->nicons = 0;
511
512     client_get_area(self);
513     client_update_transient_for(self);
514     client_update_wmhints(self);
515     client_get_desktop(self);
516     client_get_state(self);
517     client_get_shaped(self);
518
519     client_get_mwm_hints(self);
520     client_get_type(self);/* this can change the mwmhints for special cases */
521
522     client_update_protocols(self);
523
524     client_get_gravity(self); /* get the attribute gravity */
525     client_update_normal_hints(self); /* this may override the attribute
526                                          gravity */
527
528     /* got the type, the mwmhints, the protocols, and the normal hints
529        (min/max sizes), so we're ready to set up the decorations/functions */
530     client_setup_decor_and_functions(self);
531   
532     client_update_title(self);
533     client_update_icon_title(self);
534     client_update_class(self);
535     client_update_strut(self);
536     client_update_icons(self);
537     client_update_kwm_icon(self);
538
539     client_change_state(self);
540 }
541
542 static void client_get_area(Client *self)
543 {
544     XWindowAttributes wattrib;
545     Status ret;
546   
547     ret = XGetWindowAttributes(ob_display, self->window, &wattrib);
548     g_assert(ret != BadWindow);
549
550     RECT_SET(self->area, wattrib.x, wattrib.y, wattrib.width, wattrib.height);
551     self->border_width = wattrib.border_width;
552 }
553
554 static void client_get_desktop(Client *self)
555 {
556     guint32 d;
557
558     if (PROP_GET32(self->window, net_wm_desktop, cardinal, &d)) {
559         if (d >= screen_num_desktops && d != DESKTOP_ALL)
560             d = screen_num_desktops - 1;
561         self->desktop = d;
562     } else { 
563         gboolean trdesk = FALSE;
564
565        if (self->transient_for) {
566            if (self->transient_for != TRAN_GROUP) {
567                 self->desktop = self->transient_for->desktop;
568                 trdesk = TRUE;
569             } else {
570                 GSList *it;
571
572                 for (it = self->group->members; it; it = it->next)
573                     if (it->data != self &&
574                         ((Client*)it->data)->transient_for != TRAN_GROUP) {
575                         self->desktop = ((Client*)it->data)->desktop;
576                         trdesk = TRUE;
577                         break;
578                     }
579             }
580        }
581        if (!trdesk)
582            /* defaults to the current desktop */
583            self->desktop = screen_desktop;
584
585         /* set the desktop hint, to make sure that it always exists */
586         PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop);
587     }
588 }
589
590 static void client_get_state(Client *self)
591 {
592     guint32 *state;
593     guint num;
594   
595     self->modal = self->shaded = self->max_horz = self->max_vert =
596         self->fullscreen = self->above = self->below = self->iconic =
597         self->skip_taskbar = self->skip_pager = FALSE;
598
599     if (PROP_GETA32(self->window, net_wm_state, atom, &state, &num)) {
600         gulong i;
601         for (i = 0; i < num; ++i) {
602             if (state[i] == prop_atoms.net_wm_state_modal)
603                 self->modal = TRUE;
604             else if (state[i] == prop_atoms.net_wm_state_shaded)
605                 self->shaded = TRUE;
606             else if (state[i] == prop_atoms.net_wm_state_hidden)
607                 self->iconic = TRUE;
608             else if (state[i] == prop_atoms.net_wm_state_skip_taskbar)
609                 self->skip_taskbar = TRUE;
610             else if (state[i] == prop_atoms.net_wm_state_skip_pager)
611                 self->skip_pager = TRUE;
612             else if (state[i] == prop_atoms.net_wm_state_fullscreen)
613                 self->fullscreen = TRUE;
614             else if (state[i] == prop_atoms.net_wm_state_maximized_vert)
615                 self->max_vert = TRUE;
616             else if (state[i] == prop_atoms.net_wm_state_maximized_horz)
617                 self->max_horz = TRUE;
618             else if (state[i] == prop_atoms.net_wm_state_above)
619                 self->above = TRUE;
620             else if (state[i] == prop_atoms.net_wm_state_below)
621                 self->below = TRUE;
622         }
623
624         g_free(state);
625     }
626 }
627
628 static void client_get_shaped(Client *self)
629 {
630     self->shaped = FALSE;
631 #ifdef   SHAPE
632     if (extensions_shape) {
633         int foo;
634         guint ufoo;
635         int s;
636
637         XShapeSelectInput(ob_display, self->window, ShapeNotifyMask);
638
639         XShapeQueryExtents(ob_display, self->window, &s, &foo,
640                            &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo,
641                            &ufoo);
642         self->shaped = (s != 0);
643     }
644 #endif
645 }
646
647 void client_update_transient_for(Client *self)
648 {
649     Window t = None;
650     Client *c = NULL;
651
652     if (XGetTransientForHint(ob_display, self->window, &t) &&
653         t != self->window) { /* cant be transient to itself! */
654         self->transient = TRUE;
655         c = g_hash_table_lookup(client_map, &t);
656         g_assert(c != self);/* if this happens then we need to check for it*/
657
658         if (!c && self->group) {
659             /* not transient to a client, see if it is transient for a
660                group */
661             if (t == self->group->leader ||
662                 t == None ||
663                 t == ob_root) {
664                 /* window is a transient for its group! */
665                 c = TRAN_GROUP;
666             }
667         }
668     } else
669         self->transient = FALSE;
670
671     /* if anything has changed... */
672     if (c != self->transient_for) {
673         if (self->transient_for == TRAN_GROUP) { /* transient of group */
674             GSList *it;
675
676             /* remove from old parents */
677             for (it = self->group->members; it; it = it->next)
678                 if (it->data != self)
679                     ((Client*)it->data)->transients =
680                         g_slist_remove(((Client*)it->data)->transients, self);
681         } else if (self->transient_for != NULL) { /* transient of window */
682             /* remove from old parent */
683             self->transient_for->transients =
684                 g_slist_remove(self->transient_for->transients, self);
685         }
686         self->transient_for = c;
687         if (self->transient_for == TRAN_GROUP) { /* transient of group */
688             GSList *it;
689
690             /* add to new parents */
691             for (it = self->group->members; it; it = it->next)
692                 if (it->data != self)
693                     ((Client*)it->data)->transients =
694                         g_slist_append(((Client*)it->data)->transients, self);
695         } else if (self->transient_for != NULL) { /* transient of window */
696             /* add to new parent */
697             self->transient_for->transients =
698                 g_slist_append(self->transient_for->transients, self);
699         }
700     }
701 }
702
703 static void client_get_mwm_hints(Client *self)
704 {
705     guint num;
706     guint32 *hints;
707
708     self->mwmhints.flags = 0; /* default to none */
709
710     if (PROP_GETA32(self->window, motif_wm_hints, motif_wm_hints,
711                     &hints, &num)) {
712         if (num >= MWM_ELEMENTS) {
713             self->mwmhints.flags = hints[0];
714             self->mwmhints.functions = hints[1];
715             self->mwmhints.decorations = hints[2];
716         }
717         g_free(hints);
718     }
719 }
720
721 void client_get_type(Client *self)
722 {
723     guint num, i;
724     guint32 *val;
725
726     self->type = -1;
727   
728     if (PROP_GETA32(self->window, net_wm_window_type, atom, &val, &num)) {
729         /* use the first value that we know about in the array */
730         for (i = 0; i < num; ++i) {
731             if (val[i] == prop_atoms.net_wm_window_type_desktop)
732                 self->type = Type_Desktop;
733             else if (val[i] == prop_atoms.net_wm_window_type_dock)
734                 self->type = Type_Dock;
735             else if (val[i] == prop_atoms.net_wm_window_type_toolbar)
736                 self->type = Type_Toolbar;
737             else if (val[i] == prop_atoms.net_wm_window_type_menu)
738                 self->type = Type_Menu;
739             else if (val[i] == prop_atoms.net_wm_window_type_utility)
740                 self->type = Type_Utility;
741             else if (val[i] == prop_atoms.net_wm_window_type_splash)
742                 self->type = Type_Splash;
743             else if (val[i] == prop_atoms.net_wm_window_type_dialog)
744                 self->type = Type_Dialog;
745             else if (val[i] == prop_atoms.net_wm_window_type_normal)
746                 self->type = Type_Normal;
747             else if (val[i] == prop_atoms.kde_net_wm_window_type_override) {
748                 /* prevent this window from getting any decor or
749                    functionality */
750                 self->mwmhints.flags &= (MwmFlag_Functions |
751                                          MwmFlag_Decorations);
752                 self->mwmhints.decorations = 0;
753                 self->mwmhints.functions = 0;
754             }
755             if (self->type != (WindowType) -1)
756                 break; /* grab the first legit type */
757         }
758         g_free(val);
759     }
760     
761     if (self->type == (WindowType) -1) {
762         /*the window type hint was not set, which means we either classify
763           ourself as a normal window or a dialog, depending on if we are a
764           transient. */
765         if (self->transient)
766             self->type = Type_Dialog;
767         else
768             self->type = Type_Normal;
769     }
770
771     /* this makes sure that these windows appear on all desktops */
772     if (self->type == Type_Desktop)
773         self->desktop = DESKTOP_ALL;
774 }
775
776 void client_update_protocols(Client *self)
777 {
778     guint32 *proto;
779     guint num_return, i;
780
781     self->focus_notify = FALSE;
782     self->delete_window = FALSE;
783
784     if (PROP_GETA32(self->window, wm_protocols, atom, &proto, &num_return)) {
785         for (i = 0; i < num_return; ++i) {
786             if (proto[i] == prop_atoms.wm_delete_window) {
787                 /* this means we can request the window to close */
788                 self->delete_window = TRUE;
789             } else if (proto[i] == prop_atoms.wm_take_focus)
790                 /* if this protocol is requested, then the window will be
791                    notified whenever we want it to receive focus */
792                 self->focus_notify = TRUE;
793         }
794         g_free(proto);
795     }
796 }
797
798 static void client_get_gravity(Client *self)
799 {
800     XWindowAttributes wattrib;
801     Status ret;
802
803     ret = XGetWindowAttributes(ob_display, self->window, &wattrib);
804     g_assert(ret != BadWindow);
805     self->gravity = wattrib.win_gravity;
806 }
807
808 void client_update_normal_hints(Client *self)
809 {
810     XSizeHints size;
811     long ret;
812     int oldgravity = self->gravity;
813
814     /* defaults */
815     self->min_ratio = 0.0f;
816     self->max_ratio = 0.0f;
817     SIZE_SET(self->size_inc, 1, 1);
818     SIZE_SET(self->base_size, 0, 0);
819     SIZE_SET(self->min_size, 0, 0);
820     SIZE_SET(self->max_size, G_MAXINT, G_MAXINT);
821
822     /* get the hints from the window */
823     if (XGetWMNormalHints(ob_display, self->window, &size, &ret)) {
824         self->positioned = !!(size.flags & (PPosition|USPosition));
825
826         if (size.flags & PWinGravity) {
827             self->gravity = size.win_gravity;
828       
829             /* if the client has a frame, i.e. has already been mapped and
830                is changing its gravity */
831             if (self->frame && self->gravity != oldgravity) {
832                 /* move our idea of the client's position based on its new
833                    gravity */
834                 self->area.x = self->frame->area.x;
835                 self->area.y = self->frame->area.y;
836                 frame_frame_gravity(self->frame, &self->area.x, &self->area.y);
837             }
838         }
839
840         if (size.flags & PAspect) {
841             if (size.min_aspect.y)
842                 self->min_ratio = (float)size.min_aspect.x / size.min_aspect.y;
843             if (size.max_aspect.y)
844                 self->max_ratio = (float)size.max_aspect.x / size.max_aspect.y;
845         }
846
847         if (size.flags & PMinSize)
848             SIZE_SET(self->min_size, size.min_width, size.min_height);
849     
850         if (size.flags & PMaxSize)
851             SIZE_SET(self->max_size, size.max_width, size.max_height);
852     
853         if (size.flags & PBaseSize)
854             SIZE_SET(self->base_size, size.base_width, size.base_height);
855     
856         if (size.flags & PResizeInc)
857             SIZE_SET(self->size_inc, size.width_inc, size.height_inc);
858     }
859 }
860
861 void client_setup_decor_and_functions(Client *self)
862 {
863     /* start with everything (cept fullscreen) */
864     self->decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
865         Decor_Icon | Decor_AllDesktops | Decor_Iconify | Decor_Maximize |
866         Decor_Shade;
867     self->functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize |
868         Func_Shade;
869     if (self->delete_window) {
870         self->decorations |= Decor_Close;
871         self->functions |= Func_Close;
872     }
873
874     if (!(self->min_size.width < self->max_size.width ||
875           self->min_size.height < self->max_size.height)) {
876         self->decorations &= ~(Decor_Maximize | Decor_Handle);
877         self->functions &= ~(Func_Resize | Func_Maximize);
878     }
879
880     switch (self->type) {
881     case Type_Normal:
882         /* normal windows retain all of the possible decorations and
883            functionality, and are the only windows that you can fullscreen */
884         self->functions |= Func_Fullscreen;
885         break;
886
887     case Type_Dialog:
888     case Type_Utility:
889         /* these windows cannot be maximized */
890         self->decorations &= ~Decor_Maximize;
891         self->functions &= ~Func_Maximize;
892         break;
893
894     case Type_Menu:
895     case Type_Toolbar:
896         /* these windows get less functionality */
897         self->decorations &= ~(Decor_Iconify | Decor_Handle);
898         self->functions &= ~(Func_Iconify | Func_Resize);
899         break;
900
901     case Type_Desktop:
902     case Type_Dock:
903     case Type_Splash:
904         /* none of these windows are manipulated by the window manager */
905         self->decorations = 0;
906         self->functions = 0;
907         break;
908     }
909
910     /* Mwm Hints are applied subtractively to what has already been chosen for
911        decor and functionality */
912     if (self->mwmhints.flags & MwmFlag_Decorations) {
913         if (! (self->mwmhints.decorations & MwmDecor_All)) {
914             if (! (self->mwmhints.decorations & MwmDecor_Border))
915                 self->decorations &= ~Decor_Border;
916             if (! (self->mwmhints.decorations & MwmDecor_Handle))
917                 self->decorations &= ~Decor_Handle;
918             if (! (self->mwmhints.decorations & MwmDecor_Title))
919                 self->decorations &= ~Decor_Titlebar;
920             if (! (self->mwmhints.decorations & MwmDecor_Iconify))
921                 self->decorations &= ~Decor_Iconify;
922             if (! (self->mwmhints.decorations & MwmDecor_Maximize))
923                 self->decorations &= ~Decor_Maximize;
924         }
925     }
926
927     if (self->mwmhints.flags & MwmFlag_Functions) {
928         if (! (self->mwmhints.functions & MwmFunc_All)) {
929             if (! (self->mwmhints.functions & MwmFunc_Resize))
930                 self->functions &= ~Func_Resize;
931             if (! (self->mwmhints.functions & MwmFunc_Move))
932                 self->functions &= ~Func_Move;
933             if (! (self->mwmhints.functions & MwmFunc_Iconify))
934                 self->functions &= ~Func_Iconify;
935             if (! (self->mwmhints.functions & MwmFunc_Maximize))
936                 self->functions &= ~Func_Maximize;
937             /* dont let mwm hints kill the close button
938                if (! (self->mwmhints.functions & MwmFunc_Close))
939                self->functions &= ~Func_Close; */
940         }
941     }
942
943     /* can't maximize without moving/resizing */
944     if (!((self->functions & Func_Move) && (self->functions & Func_Resize)))
945         self->functions &= ~(Func_Maximize | Func_Fullscreen);
946
947     /* finally, user specified disabled decorations are applied to subtract
948        decorations */
949     if (self->disabled_decorations & Decor_Titlebar)
950         self->decorations &= ~Decor_Titlebar;
951     if (self->disabled_decorations & Decor_Handle)
952         self->decorations &= ~Decor_Handle;
953     if (self->disabled_decorations & Decor_Border)
954         self->decorations &= ~Decor_Border;
955     if (self->disabled_decorations & Decor_Iconify)
956         self->decorations &= ~Decor_Iconify;
957     if (self->disabled_decorations & Decor_Maximize)
958         self->decorations &= ~Decor_Maximize;
959     if (self->disabled_decorations & Decor_AllDesktops)
960         self->decorations &= ~Decor_AllDesktops;
961     if (self->disabled_decorations & Decor_Shade)
962         self->decorations &= ~Decor_Shade;
963     if (self->disabled_decorations & Decor_Close)
964         self->decorations &= ~Decor_Close;
965
966     /* if we don't have a titlebar, then we cannot shade! */
967     if (!(self->decorations & Decor_Titlebar))
968         self->functions &= ~Func_Shade;
969
970     /* now we need to check against rules for the client's current state */
971     if (self->fullscreen) {
972         self->functions &= (Func_Close | Func_Fullscreen | Func_Iconify);
973         self->decorations = 0;
974     }
975
976     client_change_allowed_actions(self);
977
978     if (self->frame) {
979         /* change the decors on the frame, and with more/less decorations,
980            we may also need to be repositioned */
981         frame_adjust_area(self->frame, TRUE, TRUE);
982         /* with new decor, the window's maximized size may change */
983         client_remaximize(self);
984     }
985 }
986
987 static void client_change_allowed_actions(Client *self)
988 {
989     guint32 actions[9];
990     int num = 0;
991
992     actions[num++] = prop_atoms.net_wm_action_change_desktop;
993
994     if (self->functions & Func_Shade)
995         actions[num++] = prop_atoms.net_wm_action_shade;
996     if (self->functions & Func_Close)
997         actions[num++] = prop_atoms.net_wm_action_close;
998     if (self->functions & Func_Move)
999         actions[num++] = prop_atoms.net_wm_action_move;
1000     if (self->functions & Func_Iconify)
1001         actions[num++] = prop_atoms.net_wm_action_minimize;
1002     if (self->functions & Func_Resize)
1003         actions[num++] = prop_atoms.net_wm_action_resize;
1004     if (self->functions & Func_Fullscreen)
1005         actions[num++] = prop_atoms.net_wm_action_fullscreen;
1006     if (self->functions & Func_Maximize) {
1007         actions[num++] = prop_atoms.net_wm_action_maximize_horz;
1008         actions[num++] = prop_atoms.net_wm_action_maximize_vert;
1009     }
1010
1011     PROP_SETA32(self->window, net_wm_allowed_actions, atom, actions, num);
1012
1013     /* make sure the window isn't breaking any rules now */
1014
1015     if (!(self->functions & Func_Shade) && self->shaded) {
1016         if (self->frame) client_shade(self, FALSE);
1017         else self->shaded = FALSE;
1018     }
1019     if (!(self->functions & Func_Iconify) && self->iconic) {
1020         g_message("UNSETTING ICONIC");
1021         if (self->frame) client_iconify(self, FALSE, TRUE);
1022         else self->iconic = FALSE;
1023     }
1024     if (!(self->functions & Func_Fullscreen) && self->fullscreen) {
1025         if (self->frame) client_fullscreen(self, FALSE, TRUE);
1026         else self->fullscreen = FALSE;
1027     }
1028     if (!(self->functions & Func_Maximize) && (self->max_horz ||
1029                                                self->max_vert)) {
1030         if (self->frame) client_maximize(self, FALSE, 0, TRUE);
1031         else self->max_vert = self->max_horz = FALSE;
1032     }
1033 }
1034
1035 void client_remaximize(Client *self)
1036 {
1037     int dir;
1038     if (self->max_horz && self->max_vert)
1039         dir = 0;
1040     else if (self->max_horz)
1041         dir = 1;
1042     else if (self->max_vert)
1043         dir = 2;
1044     else
1045         return; /* not maximized */
1046     self->max_horz = self->max_vert = FALSE;
1047     client_maximize(self, TRUE, dir, FALSE);
1048 }
1049
1050 void client_update_wmhints(Client *self)
1051 {
1052     XWMHints *hints;
1053     gboolean ur = FALSE;
1054     GSList *it;
1055
1056     /* assume a window takes input if it doesnt specify */
1057     self->can_focus = TRUE;
1058   
1059     if ((hints = XGetWMHints(ob_display, self->window)) != NULL) {
1060         if (hints->flags & InputHint)
1061             self->can_focus = hints->input;
1062
1063         /* only do this when first managing the window *AND* when we aren't
1064            starting up! */
1065         if (ob_state != State_Starting && self->frame == NULL)
1066             if (hints->flags & StateHint)
1067                 self->iconic = hints->initial_state == IconicState;
1068
1069         if (hints->flags & XUrgencyHint)
1070             ur = TRUE;
1071
1072         if (!(hints->flags & WindowGroupHint))
1073             hints->window_group = None;
1074
1075         /* did the group state change? */
1076         if (hints->window_group !=
1077             (self->group ? self->group->leader : None)) {
1078             /* remove from the old group if there was one */
1079             if (self->group != NULL) {
1080                 /* remove transients of the group */
1081                 for (it = self->group->members; it; it = it->next)
1082                     if (it->data != self &&
1083                         ((Client*)it->data)->transient_for == TRAN_GROUP) {
1084                         self->transients = g_slist_remove(self->transients,
1085                                                           it->data);
1086                     }
1087                 group_remove(self->group, self);
1088                 self->group = NULL;
1089             }
1090             if (hints->window_group != None) {
1091                 self->group = group_add(hints->window_group, self);
1092
1093                 /* add other transients of the group that are already
1094                    set up */
1095                 for (it = self->group->members; it; it = it->next)
1096                     if (it->data != self &&
1097                         ((Client*)it->data)->transient_for == TRAN_GROUP)
1098                         self->transients = g_slist_append(self->transients,
1099                                                           it->data);
1100             }
1101
1102             /* because the self->transient flag wont change from this call,
1103                we don't need to update the window's type and such, only its
1104                transient_for, and the transients lists of other windows in
1105                the group may be affected */
1106             client_update_transient_for(self);
1107         }
1108
1109         client_update_kwm_icon(self);
1110         /* try get the kwm icon first, this is a fallback only */
1111         if (self->pixmap_icon == None) {
1112             if (hints->flags & IconPixmapHint) {
1113                 if (self->pixmap_icon == None) {
1114                     self->pixmap_icon = hints->icon_pixmap;
1115                     if (hints->flags & IconMaskHint)
1116                         self->pixmap_icon_mask = hints->icon_mask;
1117                     else
1118                         self->pixmap_icon_mask = None;
1119
1120                     if (self->frame)
1121                         frame_adjust_icon(self->frame);
1122                 }
1123             }
1124         }
1125
1126         XFree(hints);
1127     }
1128
1129     if (ur != self->urgent) {
1130         self->urgent = ur;
1131         g_message("Urgent Hint for 0x%lx: %s", self->window,
1132                   ur ? "ON" : "OFF");
1133         /* fire the urgent callback if we're mapped, otherwise, wait until
1134            after we're mapped */
1135         if (self->frame)
1136             dispatch_client(Event_Client_Urgent, self, self->urgent, 0);
1137     }
1138 }
1139
1140 void client_update_title(Client *self)
1141 {
1142     char *data = NULL;
1143
1144     g_free(self->title);
1145      
1146     /* try netwm */
1147     if (!PROP_GETS(self->window, net_wm_name, utf8, &data))
1148         /* try old x stuff */
1149         if (!PROP_GETS(self->window, wm_name, locale, &data))
1150             data = g_strdup("Unnamed Window");
1151
1152     /* look for duplicates and append a number */
1153
1154     PROP_SETS(self->window, net_wm_visible_name, data);
1155
1156     self->title = data;
1157
1158     if (self->frame)
1159         frame_adjust_title(self->frame);
1160 }
1161
1162 void client_update_icon_title(Client *self)
1163 {
1164     char *data = NULL;
1165
1166     g_free(self->icon_title);
1167      
1168     /* try netwm */
1169     if (!PROP_GETS(self->window, net_wm_icon_name, utf8, &data))
1170         /* try old x stuff */
1171         if (!PROP_GETS(self->window, wm_icon_name, locale, &data))
1172             data = g_strdup("Unnamed Window");
1173
1174     PROP_SETS(self->window, net_wm_visible_icon_name, data);
1175
1176     self->icon_title = data;
1177 }
1178
1179 void client_update_class(Client *self)
1180 {
1181     char **data;
1182     char *s;
1183
1184     if (self->name) g_free(self->name);
1185     if (self->class) g_free(self->class);
1186     if (self->role) g_free(self->role);
1187
1188     self->name = self->class = self->role = NULL;
1189
1190     if (PROP_GETSS(self->window, wm_class, locale, &data)) {
1191         if (data[0]) {
1192             self->name = g_strdup(data[0]);
1193             if (data[1])
1194                 self->class = g_strdup(data[1]);
1195         }
1196         g_strfreev(data);     
1197     }
1198
1199     if (PROP_GETS(self->window, wm_window_role, locale, &s))
1200         self->role = g_strdup(s);
1201
1202     if (self->name == NULL) self->name = g_strdup("");
1203     if (self->class == NULL) self->class = g_strdup("");
1204     if (self->role == NULL) self->role = g_strdup("");
1205 }
1206
1207 void client_update_strut(Client *self)
1208 {
1209     guint num;
1210     guint32 *data;
1211
1212     if (!PROP_GETA32(self->window, net_wm_strut, cardinal, &data, &num)) {
1213         STRUT_SET(self->strut, 0, 0, 0, 0);
1214     } else {
1215         if (num == 4)
1216             STRUT_SET(self->strut, data[0], data[2], data[1], data[3]);
1217         else
1218             STRUT_SET(self->strut, 0, 0, 0, 0);
1219         g_free(data);
1220     }
1221
1222     /* updating here is pointless while we're being mapped cuz we're not in
1223        the client list yet */
1224     if (self->frame)
1225         screen_update_struts();
1226 }
1227
1228 void client_update_icons(Client *self)
1229 {
1230     guint num;
1231     guint32 *data;
1232     guint w, h, i;
1233     int j;
1234
1235     for (j = 0; j < self->nicons; ++j)
1236         g_free(self->icons[j].data);
1237     if (self->nicons > 0)
1238         g_free(self->icons);
1239     self->nicons = 0;
1240
1241     if (PROP_GETA32(self->window, net_wm_icon, cardinal, &data, &num)) {
1242         /* figure out how many valid icons are in here */
1243         i = 0;
1244         while (num - i > 2) {
1245             w = data[i++];
1246             h = data[i++];
1247             i += w * h;
1248             if (i > num) break;
1249             ++self->nicons;
1250         }
1251
1252         self->icons = g_new(Icon, self->nicons);
1253     
1254         /* store the icons */
1255         i = 0;
1256         for (j = 0; j < self->nicons; ++j) {
1257             w = self->icons[j].width = data[i++];
1258             h = self->icons[j].height = data[i++];
1259             self->icons[j].data =
1260                 g_memdup(&data[i], w * h * sizeof(gulong));
1261             i += w * h;
1262             g_assert(i <= num);
1263         }
1264
1265         g_free(data);
1266     }
1267
1268     if (self->frame)
1269         frame_adjust_icon(self->frame);
1270 }
1271
1272 void client_update_kwm_icon(Client *self)
1273 {
1274     guint num;
1275     guint32 *data;
1276
1277     if (!PROP_GETA32(self->window, kwm_win_icon, kwm_win_icon, &data, &num)) {
1278         self->pixmap_icon = self->pixmap_icon_mask = None;
1279     } else {
1280         if (num == 2) {
1281             self->pixmap_icon = data[0];
1282             self->pixmap_icon_mask = data[1];
1283         } else
1284             self->pixmap_icon = self->pixmap_icon_mask = None;
1285         g_free(data);
1286     }
1287     if (self->frame)
1288         frame_adjust_icon(self->frame);
1289 }
1290
1291 static void client_change_state(Client *self)
1292 {
1293     guint32 state[2];
1294     guint32 netstate[10];
1295     guint num;
1296
1297     state[0] = self->wmstate;
1298     state[1] = None;
1299     PROP_SETA32(self->window, wm_state, wm_state, state, 2);
1300
1301     num = 0;
1302     if (self->modal)
1303         netstate[num++] = prop_atoms.net_wm_state_modal;
1304     if (self->shaded)
1305         netstate[num++] = prop_atoms.net_wm_state_shaded;
1306     if (self->iconic)
1307         netstate[num++] = prop_atoms.net_wm_state_hidden;
1308     if (self->skip_taskbar)
1309         netstate[num++] = prop_atoms.net_wm_state_skip_taskbar;
1310     if (self->skip_pager)
1311         netstate[num++] = prop_atoms.net_wm_state_skip_pager;
1312     if (self->fullscreen)
1313         netstate[num++] = prop_atoms.net_wm_state_fullscreen;
1314     if (self->max_vert)
1315         netstate[num++] = prop_atoms.net_wm_state_maximized_vert;
1316     if (self->max_horz)
1317         netstate[num++] = prop_atoms.net_wm_state_maximized_horz;
1318     if (self->above)
1319         netstate[num++] = prop_atoms.net_wm_state_above;
1320     if (self->below)
1321         netstate[num++] = prop_atoms.net_wm_state_below;
1322     PROP_SETA32(self->window, net_wm_state, atom, netstate, num);
1323
1324     client_calc_layer(self);
1325
1326     if (self->frame)
1327         frame_adjust_state(self->frame);
1328 }
1329
1330 static Client *search_focus_tree(Client *node, Client *skip)
1331 {
1332     GSList *it;
1333     Client *ret;
1334
1335     for (it = node->transients; it != NULL; it = it->next) {
1336         Client *c = it->data;
1337         if (c == skip) continue; /* circular? */
1338         if ((ret = search_focus_tree(c, skip))) return ret;
1339         if (client_focused(c)) return c;
1340     }
1341     return NULL;
1342 }
1343
1344 static void calc_recursive(Client *self, StackLayer l, gboolean raised)
1345 {
1346     StackLayer old;
1347     GSList *it;
1348
1349     old = self->layer;
1350     self->layer = l;
1351
1352     for (it = self->transients; it; it = it->next)
1353         calc_recursive(it->data, l, raised ? raised : l != old);
1354
1355     if (!raised && l != old)
1356         if (self->frame)
1357             stacking_raise(self);
1358 }
1359
1360 void client_calc_layer(Client *self)
1361 {
1362     StackLayer l;
1363     gboolean f;
1364
1365     /* transients take on the layer of their parents */
1366     if (self->transient_for) {
1367         if (self->transient_for != TRAN_GROUP) {
1368             self = self->transient_for;
1369         } else {
1370             GSList *it;
1371
1372             for (it = self->group->members; it; it = it->next)
1373                 if (it->data != self &&
1374                     ((Client*)it->data)->transient_for != TRAN_GROUP) {
1375                     self = it->data;
1376                     break;
1377                 }
1378         }
1379     }
1380
1381     /* is us or one of our transients focused? */
1382     if (client_focused(self))
1383         f = TRUE;
1384     else if (search_focus_tree(self, self))
1385         f = TRUE;
1386     else
1387         f = FALSE;
1388
1389     if (self->iconic) l = Layer_Icon;
1390     /* fullscreen windows are only in the fullscreen layer while focused */
1391     else if (self->fullscreen && f) l = Layer_Fullscreen;
1392     else if (self->type == Type_Desktop) l = Layer_Desktop;
1393     else if (self->type == Type_Dock) {
1394         if (!self->below) l = Layer_Top;
1395         else l = Layer_Normal;
1396     }
1397     else if (self->above) l = Layer_Above;
1398     else if (self->below) l = Layer_Below;
1399     else l = Layer_Normal;
1400
1401     calc_recursive(self, l, FALSE);
1402 }
1403
1404 gboolean client_should_show(Client *self)
1405 {
1406     if (self->iconic) return FALSE;
1407     else if (!(self->desktop == screen_desktop ||
1408                self->desktop == DESKTOP_ALL)) return FALSE;
1409     else if (client_normal(self) && screen_showing_desktop) return FALSE;
1410     
1411     return TRUE;
1412 }
1413
1414 static void client_showhide(Client *self)
1415 {
1416
1417     if (client_should_show(self))
1418         frame_show(self->frame);
1419     else
1420         frame_hide(self->frame);
1421 }
1422
1423 gboolean client_normal(Client *self) {
1424     return ! (self->type == Type_Desktop || self->type == Type_Dock ||
1425               self->type == Type_Splash);
1426 }
1427
1428 static void client_apply_startup_state(Client *self)
1429 {
1430     /* these are in a carefully crafted order.. */
1431
1432     if (self->iconic) {
1433         self->iconic = FALSE;
1434         client_iconify(self, TRUE, FALSE);
1435     }
1436     if (self->fullscreen) {
1437         self->fullscreen = FALSE;
1438         client_fullscreen(self, TRUE, FALSE);
1439     }
1440     if (self->shaded) {
1441         self->shaded = FALSE;
1442         client_shade(self, TRUE);
1443     }
1444     if (self->urgent)
1445         dispatch_client(Event_Client_Urgent, self, self->urgent, 0);
1446   
1447     if (self->max_vert && self->max_horz) {
1448         self->max_vert = self->max_horz = FALSE;
1449         client_maximize(self, TRUE, 0, FALSE);
1450     } else if (self->max_vert) {
1451         self->max_vert = FALSE;
1452         client_maximize(self, TRUE, 2, FALSE);
1453     } else if (self->max_horz) {
1454         self->max_horz = FALSE;
1455         client_maximize(self, TRUE, 1, FALSE);
1456     }
1457
1458     /* nothing to do for the other states:
1459        skip_taskbar
1460        skip_pager
1461        modal
1462        above
1463        below
1464     */
1465 }
1466
1467 void client_configure(Client *self, Corner anchor, int x, int y, int w, int h,
1468                       gboolean user, gboolean final)
1469 {
1470     gboolean moved = FALSE, resized = FALSE;
1471
1472     /* gets the frame's position */
1473     frame_client_gravity(self->frame, &x, &y);
1474
1475     /* these positions are frame positions, not client positions */
1476
1477     /* set the size and position if fullscreen */
1478     if (self->fullscreen) {
1479         x = 0;
1480         y = 0;
1481         w = screen_physical_size.width;
1482         h = screen_physical_size.height;
1483         user = FALSE; /* ignore that increment etc shit when in fullscreen */
1484     } else {
1485         /* set the size and position if maximized */
1486         if (self->max_horz) {
1487             x = screen_area(self->desktop)->x - self->frame->size.left;
1488             w = screen_area(self->desktop)->width;
1489         }
1490         if (self->max_vert) {
1491             y = screen_area(self->desktop)->y;
1492             h = screen_area(self->desktop)->height -
1493                 self->frame->size.top - self->frame->size.bottom;
1494         }
1495     }
1496
1497     /* gets the client's position */
1498     frame_frame_gravity(self->frame, &x, &y);
1499
1500     /* these override the above states! if you cant move you can't move! */
1501     if (user) {
1502         if (!(self->functions & Func_Move)) {
1503             x = self->area.x;
1504             y = self->area.y;
1505         }
1506         if (!(self->functions & Func_Resize)) {
1507             w = self->area.width;
1508             h = self->area.height;
1509         }
1510     }
1511
1512     if (!(w == self->area.width && h == self->area.height)) {
1513         w -= self->base_size.width;
1514         h -= self->base_size.height;
1515
1516         if (user) {
1517             /* for interactive resizing. have to move half an increment in each
1518                direction. */
1519
1520             /* how far we are towards the next size inc */
1521             int mw = w % self->size_inc.width; 
1522             int mh = h % self->size_inc.height;
1523             /* amount to add */
1524             int aw = self->size_inc.width / 2;
1525             int ah = self->size_inc.height / 2;
1526             /* don't let us move into a new size increment */
1527             if (mw + aw >= self->size_inc.width)
1528                 aw = self->size_inc.width - mw - 1;
1529             if (mh + ah >= self->size_inc.height)
1530                 ah = self->size_inc.height - mh - 1;
1531             w += aw;
1532             h += ah;
1533     
1534             /* if this is a user-requested resize, then check against min/max
1535                sizes and aspect ratios */
1536
1537             /* smaller than min size or bigger than max size? */
1538             if (w > self->max_size.width) w = self->max_size.width;
1539             if (w < self->min_size.width) w = self->min_size.width;
1540             if (h > self->max_size.height) h = self->max_size.height;
1541             if (h < self->min_size.height) h = self->min_size.height;
1542
1543             /* adjust the height ot match the width for the aspect ratios */
1544             if (self->min_ratio)
1545                 if (h * self->min_ratio > w) h = (int)(w / self->min_ratio);
1546             if (self->max_ratio)
1547                 if (h * self->max_ratio < w) h = (int)(w / self->max_ratio);
1548         }
1549
1550         /* keep to the increments */
1551         w /= self->size_inc.width;
1552         h /= self->size_inc.height;
1553
1554         /* you cannot resize to nothing */
1555         if (w < 1) w = 1;
1556         if (h < 1) h = 1;
1557   
1558         /* store the logical size */
1559         SIZE_SET(self->logical_size, w, h);
1560
1561         w *= self->size_inc.width;
1562         h *= self->size_inc.height;
1563
1564         w += self->base_size.width;
1565         h += self->base_size.height;
1566     }
1567
1568     switch (anchor) {
1569     case Corner_TopLeft:
1570         break;
1571     case Corner_TopRight:
1572         x -= w - self->area.width;
1573         break;
1574     case Corner_BottomLeft:
1575         y -= h - self->area.height;
1576         break;
1577     case Corner_BottomRight:
1578         x -= w - self->area.width;
1579         y -= h - self->area.height;
1580         break;
1581     }
1582
1583     moved = x != self->area.x || y != self->area.y;
1584     resized = w != self->area.width || h != self->area.height;
1585
1586     RECT_SET(self->area, x, y, w, h);
1587
1588     if (resized)
1589         XResizeWindow(ob_display, self->window, w, h);
1590
1591     /* move/resize the frame to match the request */
1592     if (self->frame) {
1593         if (moved || resized)
1594             frame_adjust_area(self->frame, moved, resized);
1595
1596         if (!user || final) {
1597             XEvent event;
1598             event.type = ConfigureNotify;
1599             event.xconfigure.display = ob_display;
1600             event.xconfigure.event = self->window;
1601             event.xconfigure.window = self->window;
1602     
1603             /* root window coords with border in mind */
1604             event.xconfigure.x = x - self->border_width +
1605                 self->frame->size.left;
1606             event.xconfigure.y = y - self->border_width +
1607                 self->frame->size.top;
1608     
1609             event.xconfigure.width = self->area.width;
1610             event.xconfigure.height = self->area.height;
1611             event.xconfigure.border_width = self->border_width;
1612             event.xconfigure.above = self->frame->plate;
1613             event.xconfigure.override_redirect = FALSE;
1614             XSendEvent(event.xconfigure.display, event.xconfigure.window,
1615                        FALSE, StructureNotifyMask, &event);
1616         }
1617     }
1618 }
1619
1620 void client_fullscreen(Client *self, gboolean fs, gboolean savearea)
1621 {
1622     int x, y, w, h;
1623
1624     if (!(self->functions & Func_Fullscreen) || /* can't */
1625         self->fullscreen == fs) return;         /* already done */
1626
1627     self->fullscreen = fs;
1628     client_change_state(self); /* change the state hints on the client,
1629                                   and adjust out layer/stacking */
1630
1631     if (fs) {
1632         if (savearea) {
1633             guint32 dimensions[4];
1634             dimensions[0] = self->area.x;
1635             dimensions[1] = self->area.y;
1636             dimensions[2] = self->area.width;
1637             dimensions[3] = self->area.height;
1638   
1639             PROP_SETA32(self->window, openbox_premax, cardinal,
1640                         dimensions, 4);
1641         }
1642
1643         /* these are not actually used cuz client_configure will set them
1644            as appropriate when the window is fullscreened */
1645         x = y = w = h = 0;
1646     } else {
1647         guint num;
1648         gint32 *dimensions;
1649
1650         /* pick some fallbacks... */
1651         x = screen_area(self->desktop)->x +
1652             screen_area(self->desktop)->width / 4;
1653         y = screen_area(self->desktop)->y +
1654             screen_area(self->desktop)->height / 4;
1655         w = screen_area(self->desktop)->width / 2;
1656         h = screen_area(self->desktop)->height / 2;
1657
1658         if (PROP_GETA32(self->window, openbox_premax, cardinal,
1659                         (guint32**)&dimensions, &num)) {
1660             if (num == 4) {
1661                 x = dimensions[0];
1662                 y = dimensions[1];
1663                 w = dimensions[2];
1664                 h = dimensions[3];
1665             }
1666             g_free(dimensions);
1667         }
1668     }
1669
1670     client_setup_decor_and_functions(self);
1671
1672     client_configure(self, Corner_TopLeft, x, y, w, h, TRUE, TRUE);
1673
1674     /* try focus us when we go into fullscreen mode */
1675     client_focus(self);
1676 }
1677
1678 void client_iconify(Client *self, gboolean iconic, gboolean curdesk)
1679 {
1680     GSList *it;
1681
1682     /* move up the transient chain as far as possible first if deiconifying */
1683     if (!iconic)
1684         while (self->transient_for) {
1685             if (self->transient_for != TRAN_GROUP) {
1686                 if (self->transient_for->iconic == iconic)
1687                     break;
1688                 self = self->transient_for;
1689             } else {
1690                 GSList *it;
1691
1692                 /* the check for TRAN_GROUP is to prevent an infinate loop with
1693                    2 transients of the same group at the head of the group's
1694                    members list */
1695                 for (it = self->group->members; it; it = it->next) {
1696                     Client *c = it->data;
1697
1698                     if (c != self && c->transient_for->iconic != iconic &&
1699                         c->transient_for != TRAN_GROUP) {
1700                         self = it->data;
1701                         break;
1702                     }
1703                 }
1704                 if (it == NULL) break;
1705             }
1706         }
1707
1708     if (self->iconic == iconic) return; /* nothing to do */
1709
1710     g_message("%sconifying window: 0x%lx", (iconic ? "I" : "Uni"),
1711               self->window);
1712
1713     self->iconic = iconic;
1714
1715     if (iconic) {
1716         self->wmstate = IconicState;
1717         self->ignore_unmaps++;
1718         /* we unmap the client itself so that we can get MapRequest events,
1719            and because the ICCCM tells us to! */
1720         XUnmapWindow(ob_display, self->window);
1721     } else {
1722         if (curdesk)
1723             client_set_desktop(self, screen_desktop, FALSE);
1724         self->wmstate = self->shaded ? IconicState : NormalState;
1725         XMapWindow(ob_display, self->window);
1726     }
1727     client_change_state(self);
1728     client_showhide(self);
1729     screen_update_struts();
1730
1731     dispatch_client(iconic ? Event_Client_Unmapped : Event_Client_Mapped,
1732                     self, 0, 0);
1733
1734     /* iconify all transients */
1735     for (it = self->transients; it != NULL; it = it->next)
1736         if (it->data != self) client_iconify(it->data, iconic, curdesk);
1737 }
1738
1739 void client_maximize(Client *self, gboolean max, int dir, gboolean savearea)
1740 {
1741     int x, y, w, h;
1742      
1743     g_assert(dir == 0 || dir == 1 || dir == 2);
1744     if (!(self->functions & Func_Maximize)) return; /* can't */
1745
1746     /* check if already done */
1747     if (max) {
1748         if (dir == 0 && self->max_horz && self->max_vert) return;
1749         if (dir == 1 && self->max_horz) return;
1750         if (dir == 2 && self->max_vert) return;
1751     } else {
1752         if (dir == 0 && !self->max_horz && !self->max_vert) return;
1753         if (dir == 1 && !self->max_horz) return;
1754         if (dir == 2 && !self->max_vert) return;
1755     }
1756
1757     /* work with the frame's coords */
1758     x = self->frame->area.x;
1759     y = self->frame->area.y;
1760     w = self->area.width;
1761     h = self->area.height;
1762
1763     if (max) {
1764         if (savearea) {
1765             gint32 dimensions[4];
1766             gint32 *readdim;
1767             guint num;
1768
1769             dimensions[0] = x;
1770             dimensions[1] = y;
1771             dimensions[2] = w;
1772             dimensions[3] = h;
1773
1774             /* get the property off the window and use it for the dimensions
1775                we are already maxed on */
1776             if (PROP_GETA32(self->window, openbox_premax, cardinal,
1777                             (guint32**)&readdim, &num)) {
1778                 if (num == 4) {
1779                     if (self->max_horz) {
1780                         dimensions[0] = readdim[0];
1781                         dimensions[2] = readdim[2];
1782                     }
1783                     if (self->max_vert) {
1784                         dimensions[1] = readdim[1];
1785                         dimensions[3] = readdim[3];
1786                     }
1787                 }
1788                 g_free(readdim);
1789             }
1790
1791             PROP_SETA32(self->window, openbox_premax, cardinal,
1792                         (guint32*)dimensions, 4);
1793         }
1794     } else {
1795         guint num;
1796         gint32 *dimensions;
1797
1798         /* pick some fallbacks... */
1799         if (dir == 0 || dir == 1) { /* horz */
1800             x = screen_area(self->desktop)->x +
1801                 screen_area(self->desktop)->width / 4;
1802             w = screen_area(self->desktop)->width / 2;
1803         }
1804         if (dir == 0 || dir == 2) { /* vert */
1805             y = screen_area(self->desktop)->y +
1806                 screen_area(self->desktop)->height / 4;
1807             h = screen_area(self->desktop)->height / 2;
1808         }
1809
1810         if (PROP_GETA32(self->window, openbox_premax, cardinal,
1811                         (guint32**)&dimensions, &num)) {
1812             if (num == 4) {
1813                 if (dir == 0 || dir == 1) { /* horz */
1814                     x = dimensions[0];
1815                     w = dimensions[2];
1816                 }
1817                 if (dir == 0 || dir == 2) { /* vert */
1818                     y = dimensions[1];
1819                     h = dimensions[3];
1820                 }
1821             }
1822             g_free(dimensions);
1823         }
1824     }
1825
1826     if (dir == 0 || dir == 1) /* horz */
1827         self->max_horz = max;
1828     if (dir == 0 || dir == 2) /* vert */
1829         self->max_vert = max;
1830
1831     if (!self->max_horz && !self->max_vert)
1832         PROP_ERASE(self->window, openbox_premax);
1833
1834     client_change_state(self); /* change the state hints on the client */
1835
1836     /* figure out where the client should be going */
1837     frame_frame_gravity(self->frame, &x, &y);
1838     client_configure(self, Corner_TopLeft, x, y, w, h, TRUE, TRUE);
1839 }
1840
1841 void client_shade(Client *self, gboolean shade)
1842 {
1843     if ((!(self->functions & Func_Shade) && shade) || /* can't shade */
1844         self->shaded == shade) return;     /* already done */
1845
1846     /* when we're iconic, don't change the wmstate */
1847     if (!self->iconic)
1848         self->wmstate = shade ? IconicState : NormalState;
1849     self->shaded = shade;
1850     client_change_state(self);
1851     /* resize the frame to just the titlebar */
1852     frame_adjust_area(self->frame, FALSE, FALSE);
1853 }
1854
1855 void client_close(Client *self)
1856 {
1857     XEvent ce;
1858
1859     if (!(self->functions & Func_Close)) return;
1860
1861     /*
1862       XXX: itd be cool to do timeouts and shit here for killing the client's
1863       process off
1864       like... if the window is around after 5 seconds, then the close button
1865       turns a nice red, and if this function is called again, the client is
1866       explicitly killed.
1867     */
1868
1869     ce.xclient.type = ClientMessage;
1870     ce.xclient.message_type =  prop_atoms.wm_protocols;
1871     ce.xclient.display = ob_display;
1872     ce.xclient.window = self->window;
1873     ce.xclient.format = 32;
1874     ce.xclient.data.l[0] = prop_atoms.wm_delete_window;
1875     ce.xclient.data.l[1] = event_lasttime;
1876     ce.xclient.data.l[2] = 0l;
1877     ce.xclient.data.l[3] = 0l;
1878     ce.xclient.data.l[4] = 0l;
1879     XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
1880 }
1881
1882 void client_kill(Client *self)
1883 {
1884     XKillClient(ob_display, self->window);
1885 }
1886
1887 void client_set_desktop(Client *self, guint target, gboolean donthide)
1888 {
1889     guint old, i;
1890
1891     if (target == self->desktop) return;
1892   
1893     g_message("Setting desktop %u", target);
1894
1895     g_assert(target < screen_num_desktops || target == DESKTOP_ALL);
1896
1897     old = self->desktop;
1898     self->desktop = target;
1899     PROP_SET32(self->window, net_wm_desktop, cardinal, target);
1900     /* the frame can display the current desktop state */
1901     frame_adjust_state(self->frame);
1902     /* 'move' the window to the new desktop */
1903     if (!donthide)
1904         client_showhide(self);
1905     /* raise if it was not already on the desktop */
1906     if (old != DESKTOP_ALL)
1907         stacking_raise(self);
1908     screen_update_struts();
1909
1910     /* update the focus lists */
1911     if (old == DESKTOP_ALL) {
1912         for (i = 0; i < screen_num_desktops; ++i)
1913             focus_order[i] = g_list_remove(focus_order[i], self);
1914     } else
1915         focus_order[old] = g_list_remove(focus_order[old], self);
1916     if (target == DESKTOP_ALL) {
1917         for (i = 0; i < screen_num_desktops; ++i) {
1918             if (config_focus_new)
1919                 focus_order[i] = g_list_prepend(focus_order[i], self);
1920             else
1921                 focus_order[i] = g_list_append(focus_order[i], self);
1922         }
1923     } else {
1924         if (config_focus_new)
1925             focus_order[target] = g_list_prepend(focus_order[target], self);
1926         else
1927             focus_order[target] = g_list_append(focus_order[target], self);
1928     }
1929
1930     dispatch_client(Event_Client_Desktop, self, target, old);
1931 }
1932
1933 static Client *search_modal_tree(Client *node, Client *skip)
1934 {
1935     GSList *it;
1936     Client *ret;
1937   
1938     for (it = node->transients; it != NULL; it = it->next) {
1939         Client *c = it->data;
1940         if (c == skip) continue; /* circular? */
1941         if ((ret = search_modal_tree(c, skip))) return ret;
1942         if (c->modal) return c;
1943     }
1944     return NULL;
1945 }
1946
1947 Client *client_find_modal_child(Client *self)
1948 {
1949     return search_modal_tree(self, self);
1950 }
1951
1952 gboolean client_validate(Client *self)
1953 {
1954     XEvent e; 
1955
1956     XSync(ob_display, FALSE); /* get all events on the server */
1957
1958     if (XCheckTypedWindowEvent(ob_display, self->window, DestroyNotify, &e) ||
1959         XCheckTypedWindowEvent(ob_display, self->window, UnmapNotify, &e)) {
1960         XPutBackEvent(ob_display, &e);
1961         return FALSE;
1962     }
1963
1964     return TRUE;
1965 }
1966
1967 void client_set_wm_state(Client *self, long state)
1968 {
1969     if (state == self->wmstate) return; /* no change */
1970   
1971     switch (state) {
1972     case IconicState:
1973         client_iconify(self, TRUE, TRUE);
1974         break;
1975     case NormalState:
1976         client_iconify(self, FALSE, TRUE);
1977         break;
1978     }
1979 }
1980
1981 void client_set_state(Client *self, Atom action, long data1, long data2)
1982 {
1983     gboolean shaded = self->shaded;
1984     gboolean fullscreen = self->fullscreen;
1985     gboolean max_horz = self->max_horz;
1986     gboolean max_vert = self->max_vert;
1987     int i;
1988
1989     if (!(action == prop_atoms.net_wm_state_add ||
1990           action == prop_atoms.net_wm_state_remove ||
1991           action == prop_atoms.net_wm_state_toggle))
1992         /* an invalid action was passed to the client message, ignore it */
1993         return; 
1994
1995     for (i = 0; i < 2; ++i) {
1996         Atom state = i == 0 ? data1 : data2;
1997     
1998         if (!state) continue;
1999
2000         /* if toggling, then pick whether we're adding or removing */
2001         if (action == prop_atoms.net_wm_state_toggle) {
2002             if (state == prop_atoms.net_wm_state_modal)
2003                 action = self->modal ? prop_atoms.net_wm_state_remove :
2004                     prop_atoms.net_wm_state_add;
2005             else if (state == prop_atoms.net_wm_state_maximized_vert)
2006                 action = self->max_vert ? prop_atoms.net_wm_state_remove :
2007                     prop_atoms.net_wm_state_add;
2008             else if (state == prop_atoms.net_wm_state_maximized_horz)
2009                 action = self->max_horz ? prop_atoms.net_wm_state_remove :
2010                     prop_atoms.net_wm_state_add;
2011             else if (state == prop_atoms.net_wm_state_shaded)
2012                 action = self->shaded ? prop_atoms.net_wm_state_remove :
2013                     prop_atoms.net_wm_state_add;
2014             else if (state == prop_atoms.net_wm_state_skip_taskbar)
2015                 action = self->skip_taskbar ?
2016                     prop_atoms.net_wm_state_remove :
2017                     prop_atoms.net_wm_state_add;
2018             else if (state == prop_atoms.net_wm_state_skip_pager)
2019                 action = self->skip_pager ?
2020                     prop_atoms.net_wm_state_remove :
2021                     prop_atoms.net_wm_state_add;
2022             else if (state == prop_atoms.net_wm_state_fullscreen)
2023                 action = self->fullscreen ?
2024                     prop_atoms.net_wm_state_remove :
2025                     prop_atoms.net_wm_state_add;
2026             else if (state == prop_atoms.net_wm_state_above)
2027                 action = self->above ? prop_atoms.net_wm_state_remove :
2028                     prop_atoms.net_wm_state_add;
2029             else if (state == prop_atoms.net_wm_state_below)
2030                 action = self->below ? prop_atoms.net_wm_state_remove :
2031                     prop_atoms.net_wm_state_add;
2032         }
2033     
2034         if (action == prop_atoms.net_wm_state_add) {
2035             if (state == prop_atoms.net_wm_state_modal) {
2036                 /* XXX raise here or something? */
2037                 self->modal = TRUE;
2038             } else if (state == prop_atoms.net_wm_state_maximized_vert) {
2039                 max_vert = TRUE;
2040             } else if (state == prop_atoms.net_wm_state_maximized_horz) {
2041                 max_horz = TRUE;
2042             } else if (state == prop_atoms.net_wm_state_shaded) {
2043                 shaded = TRUE;
2044             } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
2045                 self->skip_taskbar = TRUE;
2046             } else if (state == prop_atoms.net_wm_state_skip_pager) {
2047                 self->skip_pager = TRUE;
2048             } else if (state == prop_atoms.net_wm_state_fullscreen) {
2049                 fullscreen = TRUE;
2050             } else if (state == prop_atoms.net_wm_state_above) {
2051                 self->above = TRUE;
2052             } else if (state == prop_atoms.net_wm_state_below) {
2053                 self->below = TRUE;
2054             }
2055
2056         } else { /* action == prop_atoms.net_wm_state_remove */
2057             if (state == prop_atoms.net_wm_state_modal) {
2058                 self->modal = FALSE;
2059             } else if (state == prop_atoms.net_wm_state_maximized_vert) {
2060                 max_vert = FALSE;
2061             } else if (state == prop_atoms.net_wm_state_maximized_horz) {
2062                 max_horz = FALSE;
2063             } else if (state == prop_atoms.net_wm_state_shaded) {
2064                 shaded = FALSE;
2065             } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
2066                 self->skip_taskbar = FALSE;
2067             } else if (state == prop_atoms.net_wm_state_skip_pager) {
2068                 self->skip_pager = FALSE;
2069             } else if (state == prop_atoms.net_wm_state_fullscreen) {
2070                 fullscreen = FALSE;
2071             } else if (state == prop_atoms.net_wm_state_above) {
2072                 self->above = FALSE;
2073             } else if (state == prop_atoms.net_wm_state_below) {
2074                 self->below = FALSE;
2075             }
2076         }
2077     }
2078     if (max_horz != self->max_horz || max_vert != self->max_vert) {
2079         if (max_horz != self->max_horz && max_vert != self->max_vert) {
2080             /* toggling both */
2081             if (max_horz == max_vert) { /* both going the same way */
2082                 client_maximize(self, max_horz, 0, TRUE);
2083             } else {
2084                 client_maximize(self, max_horz, 1, TRUE);
2085                 client_maximize(self, max_vert, 2, TRUE);
2086             }
2087         } else {
2088             /* toggling one */
2089             if (max_horz != self->max_horz)
2090                 client_maximize(self, max_horz, 1, TRUE);
2091             else
2092                 client_maximize(self, max_vert, 2, TRUE);
2093         }
2094     }
2095     /* change fullscreen state before shading, as it will affect if the window
2096        can shade or not */
2097     if (fullscreen != self->fullscreen)
2098         client_fullscreen(self, fullscreen, TRUE);
2099     if (shaded != self->shaded)
2100         client_shade(self, shaded);
2101     client_calc_layer(self);
2102     client_change_state(self); /* change the hint to relect these changes */
2103 }
2104
2105 Client *client_focus_target(Client *self)
2106 {
2107     Client *child;
2108      
2109     /* if we have a modal child, then focus it, not us */
2110     child = client_find_modal_child(self);
2111     if (child) return child;
2112     return self;
2113 }
2114
2115 gboolean client_focusable(Client *self)
2116 {
2117     /* won't try focus if the client doesn't want it, or if the window isn't
2118        visible on the screen */
2119     return self->frame->visible &&
2120         (self->can_focus || self->focus_notify);
2121 }
2122
2123 gboolean client_focus(Client *self)
2124 {
2125     XEvent ev;
2126     guint i;
2127
2128     /* choose the correct target */
2129     self = client_focus_target(self);
2130
2131     if (self->desktop != DESKTOP_ALL && self->desktop != screen_desktop) {
2132         /* update the focus lists */
2133         if (self->desktop == DESKTOP_ALL) {
2134             for (i = 0; i < screen_num_desktops; ++i) {
2135                 focus_order[i] = g_list_remove(focus_order[i], self);
2136                 focus_order[i] = g_list_prepend(focus_order[i], self);
2137             }
2138         } else {
2139             i = self->desktop;
2140             focus_order[i] = g_list_remove(focus_order[i], self);
2141             focus_order[i] = g_list_prepend(focus_order[i], self);
2142         }
2143         return FALSE;
2144     }
2145
2146     if (!client_focusable(self))
2147         return FALSE;
2148
2149     /* do a check to see if the window has already been unmapped or destroyed
2150        do this intelligently while watching out for unmaps we've generated
2151        (ignore_unmaps > 0) */
2152     if (XCheckTypedWindowEvent(ob_display, self->window,
2153                                DestroyNotify, &ev)) {
2154         XPutBackEvent(ob_display, &ev);
2155         return FALSE;
2156     }
2157     while (XCheckTypedWindowEvent(ob_display, self->window,
2158                                   UnmapNotify, &ev)) {
2159         if (self->ignore_unmaps) {
2160             self->ignore_unmaps--;
2161         } else {
2162             XPutBackEvent(ob_display, &ev);
2163             return FALSE;
2164         }
2165     }
2166
2167     if (self->can_focus)
2168         /* RevertToPointerRoot causes much more headache than RevertToNone, so
2169            I choose to use it always, hopefully to find errors quicker, if any
2170            are left. (I hate X. I hate focus events.) */
2171         XSetInputFocus(ob_display, self->window, RevertToPointerRoot,
2172                        event_lasttime);
2173
2174     if (self->focus_notify) {
2175         XEvent ce;
2176         ce.xclient.type = ClientMessage;
2177         ce.xclient.message_type = prop_atoms.wm_protocols;
2178         ce.xclient.display = ob_display;
2179         ce.xclient.window = self->window;
2180         ce.xclient.format = 32;
2181         ce.xclient.data.l[0] = prop_atoms.wm_take_focus;
2182         ce.xclient.data.l[1] = event_lasttime;
2183         ce.xclient.data.l[2] = 0l;
2184         ce.xclient.data.l[3] = 0l;
2185         ce.xclient.data.l[4] = 0l;
2186         XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
2187     }
2188
2189 #ifdef DEBUG_FOCUS
2190     g_message("%sively focusing %lx at %d", (self->can_focus ? "act" : "pass"),
2191               self->window, (int)
2192               event_lasttime);
2193 #endif
2194
2195     /* Cause the FocusIn to come back to us. Important for desktop switches,
2196        since otherwise we'll have no FocusIn on the queue and send it off to
2197        the focus_backup. */
2198     XSync(ob_display, FALSE);
2199     return TRUE;
2200 }
2201
2202 void client_unfocus(Client *self)
2203 {
2204     g_assert(focus_client == self);
2205 #ifdef DEBUG_FOCUS
2206     g_message("client_unfocus for %lx", self->window);
2207 #endif
2208     focus_fallback(Fallback_Unfocusing);
2209 }
2210
2211 gboolean client_focused(Client *self)
2212 {
2213     return self == focus_client;
2214 }
2215
2216 Icon *client_icon(Client *self, int w, int h)
2217 {
2218     int i;
2219     /* si is the smallest image >= req */
2220     /* li is the largest image < req */
2221     unsigned long size, smallest = 0xffffffff, largest = 0, si = 0, li = 0;
2222
2223     if (!self->nicons) return NULL;
2224
2225     for (i = 0; i < self->nicons; ++i) {
2226         size = self->icons[i].width * self->icons[i].height;
2227         if (size < smallest && size >= (unsigned)(w * h)) {
2228             smallest = size;
2229             si = i;
2230         }
2231         if (size > largest && size <= (unsigned)(w * h)) {
2232             largest = size;
2233             li = i;
2234         }
2235     }
2236     if (largest == 0) /* didnt find one smaller than the requested size */
2237         return &self->icons[si];
2238     return &self->icons[li];
2239 }