]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/client.c
send a configurenotify on mapping after the window is shown
[mikachu/openbox.git] / openbox / client.c
1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
2    
3    client.c for the Openbox window manager
4    Copyright (c) 2006        Mikael Magnusson
5    Copyright (c) 2003-2007   Dana 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 "propwin.h"
36 #include "stacking.h"
37 #include "openbox.h"
38 #include "group.h"
39 #include "config.h"
40 #include "menuframe.h"
41 #include "keyboard.h"
42 #include "mouse.h"
43 #include "render/render.h"
44
45 #ifdef HAVE_UNISTD_H
46 #  include <unistd.h>
47 #endif
48
49 #include <glib.h>
50 #include <X11/Xutil.h>
51
52 /*! The event mask to grab on client windows */
53 #define CLIENT_EVENTMASK (PropertyChangeMask | StructureNotifyMask | \
54                           ColormapChangeMask)
55
56 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
57                                 ButtonMotionMask)
58
59 typedef struct
60 {
61     ObClientCallback func;
62     gpointer data;
63 } ClientCallback;
64
65 GList            *client_list          = NULL;
66
67 static GSList *client_destroy_notifies = NULL;
68 static GSList *client_hide_notifies    = NULL;
69
70 static void client_get_all(ObClient *self, gboolean real);
71 static void client_toggle_border(ObClient *self, gboolean show);
72 static void client_get_startup_id(ObClient *self);
73 static void client_get_session_ids(ObClient *self);
74 static void client_get_area(ObClient *self);
75 static void client_get_desktop(ObClient *self);
76 static void client_get_state(ObClient *self);
77 static void client_get_shaped(ObClient *self);
78 static void client_get_mwm_hints(ObClient *self);
79 static void client_get_colormap(ObClient *self);
80 static void client_change_allowed_actions(ObClient *self);
81 static void client_change_state(ObClient *self);
82 static void client_change_wm_state(ObClient *self);
83 static void client_apply_startup_state(ObClient *self);
84 static void client_restore_session_state(ObClient *self);
85 static gboolean client_restore_session_stacking(ObClient *self);
86 static ObAppSettings *client_get_settings_state(ObClient *self);
87 static void client_update_transient_tree(ObClient *self,
88                                          ObGroup *oldgroup, ObGroup *newgroup,
89                                          ObClient* oldparent,
90                                          ObClient *newparent);
91 static void client_present(ObClient *self, gboolean here, gboolean raise);
92 static GSList *client_search_all_top_parents_internal(ObClient *self,
93                                                       gboolean bylayer,
94                                                       ObStackingLayer layer);
95 static void client_call_notifies(ObClient *self, GSList *list);
96
97 void client_startup(gboolean reconfig)
98 {
99     if (reconfig) return;
100
101     client_set_list();
102 }
103
104 void client_shutdown(gboolean reconfig)
105 {
106     if (reconfig) return;
107 }
108
109 static void client_call_notifies(ObClient *self, GSList *list)
110 {
111     GSList *it;
112
113     for (it = list; it; it = g_slist_next(it)) {
114         ClientCallback *d = it->data;
115         d->func(self, d->data);
116     }
117 }
118
119 void client_add_destroy_notify(ObClientCallback func, gpointer data)
120 {
121     ClientCallback *d = g_new(ClientCallback, 1);
122     d->func = func;
123     d->data = data;
124     client_destroy_notifies = g_slist_prepend(client_destroy_notifies, d);
125 }
126
127 void client_remove_destroy_notify(ObClientCallback func)
128 {
129     GSList *it;
130
131     for (it = client_destroy_notifies; it; it = g_slist_next(it)) {
132         ClientCallback *d = it->data;
133         if (d->func == func) {
134             g_free(d);
135             client_destroy_notifies =
136                 g_slist_delete_link(client_destroy_notifies, it);
137             break;
138         }
139     }
140 }
141
142 void client_add_hide_notify(ObClientCallback func, gpointer data)
143 {
144     ClientCallback *d = g_new(ClientCallback, 1);
145     d->func = func;
146     d->data = data;
147     client_hide_notifies = g_slist_prepend(client_hide_notifies, d);
148 }
149
150 void client_remove_hide_notify(ObClientCallback func)
151 {
152     GSList *it;
153
154     for (it = client_hide_notifies; it; it = g_slist_next(it)) {
155         ClientCallback *d = it->data;
156         if (d->func == func) {
157             g_free(d);
158             client_hide_notifies =
159                 g_slist_delete_link(client_hide_notifies, it);
160             break;
161         }
162     }
163 }
164
165 void client_set_list()
166 {
167     Window *windows, *win_it;
168     GList *it;
169     guint size = g_list_length(client_list);
170
171     /* create an array of the window ids */
172     if (size > 0) {
173         windows = g_new(Window, size);
174         win_it = windows;
175         for (it = client_list; it; it = g_list_next(it), ++win_it)
176             *win_it = ((ObClient*)it->data)->window;
177     } else
178         windows = NULL;
179
180     PROP_SETA32(RootWindow(ob_display, ob_screen),
181                 net_client_list, window, (gulong*)windows, size);
182
183     if (windows)
184         g_free(windows);
185
186     stacking_set_list();
187 }
188
189 /*
190   void client_foreach_transient(ObClient *self, ObClientForeachFunc func, gpointer data)
191   {
192   GSList *it;
193
194   for (it = self->transients; it; it = g_slist_next(it)) {
195   if (!func(it->data, data)) return;
196   client_foreach_transient(it->data, func, data);
197   }
198   }
199
200   void client_foreach_ancestor(ObClient *self, ObClientForeachFunc func, gpointer data)
201   {
202   if (self->transient_for) {
203   if (self->transient_for != OB_TRAN_GROUP) {
204   if (!func(self->transient_for, data)) return;
205   client_foreach_ancestor(self->transient_for, func, data);
206   } else {
207   GSList *it;
208
209   for (it = self->group->members; it; it = g_slist_next(it))
210   if (it->data != self &&
211   !((ObClient*)it->data)->transient_for) {
212   if (!func(it->data, data)) return;
213   client_foreach_ancestor(it->data, func, data);
214   }
215   }
216   }
217   }
218 */
219
220 void client_manage_all()
221 {
222     guint i, j, nchild;
223     Window w, *children;
224     XWMHints *wmhints;
225     XWindowAttributes attrib;
226
227     XQueryTree(ob_display, RootWindow(ob_display, ob_screen),
228                &w, &w, &children, &nchild);
229
230     /* remove all icon windows from the list */
231     for (i = 0; i < nchild; i++) {
232         if (children[i] == None) continue;
233         wmhints = XGetWMHints(ob_display, children[i]);
234         if (wmhints) {
235             if ((wmhints->flags & IconWindowHint) &&
236                 (wmhints->icon_window != children[i]))
237                 for (j = 0; j < nchild; j++)
238                     if (children[j] == wmhints->icon_window) {
239                         children[j] = None;
240                         break;
241                     }
242             XFree(wmhints);
243         }
244     }
245
246     for (i = 0; i < nchild; ++i) {
247         if (children[i] == None)
248             continue;
249         if (XGetWindowAttributes(ob_display, children[i], &attrib)) {
250             if (attrib.override_redirect) continue;
251
252             if (attrib.map_state != IsUnmapped)
253                 client_manage(children[i]);
254         }
255     }
256     XFree(children);
257 }
258
259 void client_manage(Window window)
260 {
261     ObClient *self;
262     XEvent e;
263     XWindowAttributes attrib;
264     XSetWindowAttributes attrib_set;
265     XWMHints *wmhint;
266     gboolean activate = FALSE;
267     ObAppSettings *settings;
268
269     grab_server(TRUE);
270
271     /* check if it has already been unmapped by the time we started
272        mapping. the grab does a sync so we don't have to here */
273     if (XCheckTypedWindowEvent(ob_display, window, DestroyNotify, &e) ||
274         XCheckTypedWindowEvent(ob_display, window, UnmapNotify, &e))
275     {
276         XPutBackEvent(ob_display, &e);
277
278         ob_debug("Trying to manage unmapped window. Aborting that.\n");
279         grab_server(FALSE);
280         return; /* don't manage it */
281     }
282
283     /* make sure it isn't an override-redirect window */
284     if (!XGetWindowAttributes(ob_display, window, &attrib) ||
285         attrib.override_redirect)
286     {
287         grab_server(FALSE);
288         return; /* don't manage it */
289     }
290   
291     /* is the window a docking app */
292     if ((wmhint = XGetWMHints(ob_display, window))) {
293         if ((wmhint->flags & StateHint) &&
294             wmhint->initial_state == WithdrawnState)
295         {
296             dock_add(window, wmhint);
297             grab_server(FALSE);
298             XFree(wmhint);
299             return;
300         }
301         XFree(wmhint);
302     }
303
304     ob_debug("Managing window: %lx\n", window);
305
306     /* choose the events we want to receive on the CLIENT window */
307     attrib_set.event_mask = CLIENT_EVENTMASK;
308     attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK;
309     XChangeWindowAttributes(ob_display, window,
310                             CWEventMask|CWDontPropagate, &attrib_set);
311
312     /* create the ObClient struct, and populate it from the hints on the
313        window */
314     self = g_new0(ObClient, 1);
315     self->obwin.type = Window_Client;
316     self->window = window;
317
318     /* non-zero defaults */
319     self->wmstate = WithdrawnState; /* make sure it gets updated first time */
320     self->gravity = NorthWestGravity;
321     self->desktop = screen_num_desktops; /* always an invalid value */
322     self->user_time = focus_client ? focus_client->user_time : CurrentTime;
323
324     /* get all the stuff off the window */
325     client_get_all(self, TRUE);
326
327     /* specify that if we exit, the window should not be destroyed and
328        should be reparented back to root automatically */
329     XChangeSaveSet(ob_display, window, SetModeInsert);
330
331     /* create the decoration frame for the client window */
332     self->frame = frame_new(self);
333
334     frame_grab_client(self->frame);
335
336     /* we've grabbed everything and set everything that we need to at mapping
337        time now */
338     grab_server(FALSE);
339
340     /* per-app settings override stuff from client_get_all, and return the
341        settings for other uses too */
342     settings = client_get_settings_state(self);
343     /* the session should get the last say thought */
344     client_restore_session_state(self);
345
346     /* now we have all of the window's information so we can set this up */
347     client_setup_decor_and_functions(self);
348
349     /* remove the client's border (and adjust re gravity) */
350     client_toggle_border(self, FALSE);
351      
352     {
353         Time t = sn_app_started(self->startup_id, self->class);
354         if (t) self->user_time = t;
355     }
356
357     /* do this after we have a frame.. it uses the frame to help determine the
358        WM_STATE to apply. */
359     client_change_state(self);
360
361     /* add ourselves to the focus order */
362     focus_order_add_new(self);
363
364     /* do this to add ourselves to the stacking list in a non-intrusive way */
365     client_calc_layer(self);
366
367     /* focus the new window? */
368     if (ob_state() != OB_STATE_STARTING &&
369         (!self->session || self->session->focused) &&
370         !self->iconic &&
371         /* this means focus=true for window is same as config_focus_new=true */
372         ((config_focus_new || (settings && settings->focus == 1)) ||
373          client_search_focus_parent(self)) &&
374         /* this checks for focus=false for the window */
375         (!settings || settings->focus != 0) &&
376         /* note the check against Type_Normal/Dialog, not client_normal(self),
377            which would also include other types. in this case we want more
378            strict rules for focus */
379         (self->type == OB_CLIENT_TYPE_NORMAL ||
380          self->type == OB_CLIENT_TYPE_DIALOG))
381     {
382         activate = TRUE;
383     }
384
385     /* figure out placement for the window */
386     if (ob_state() == OB_STATE_RUNNING) {
387         gboolean transient;
388
389         ob_debug("Positioned: %s @ %d %d\n",
390                  (!self->positioned ? "no" :
391                   (self->positioned == PPosition ? "program specified" :
392                    (self->positioned == USPosition ? "user specified" :
393                     "BADNESS !?"))), self->area.x, self->area.y);
394
395         transient = place_client(self, &self->area.x, &self->area.y, settings);
396
397         /* make sure the window is visible. */
398         client_find_onscreen(self, &self->area.x, &self->area.y,
399                              self->area.width, self->area.height,
400                              /* non-normal clients has less rules, and
401                                 windows that are being restored from a
402                                 session do also. we can assume you want
403                                 it back where you saved it. Clients saying
404                                 they placed themselves are subjected to
405                                 harder rules, ones that are placed by
406                                 place.c or by the user are allowed partially
407                                 off-screen and on xinerama divides (ie,
408                                 it is up to the placement routines to avoid
409                                 the xinerama divides) */
410                              transient ||
411                              (((self->positioned & PPosition) &&
412                                !(self->positioned & USPosition)) &&
413                               client_normal(self) &&
414                               !self->session));
415     }
416
417     /* do this after the window is placed, so the premax/prefullscreen numbers
418        won't be all wacko!!
419        also, this moves the window to the position where it has been placed
420     */
421     ob_debug("placing window 0x%x at %d, %d with size %d x %d\n",
422              self->window, self->area.x, self->area.y,
423              self->area.width, self->area.height);
424     if (self->session)
425         ob_debug("  but session requested %d %d instead, overriding\n",
426                  self->session->x, self->session->y);
427
428     client_apply_startup_state(self);
429
430     mouse_grab_for_client(self, TRUE);
431
432     if (activate) {
433         guint32 last_time = focus_client ?
434             focus_client->user_time : CurrentTime;
435
436         /* This is focus stealing prevention */
437         ob_debug_type(OB_DEBUG_FOCUS,
438                       "Want to focus new window 0x%x with time %u "
439                       "(last time %u)\n",
440                       self->window, self->user_time, last_time);
441
442         /* if it's on another desktop */
443         if (!(self->desktop == screen_desktop || self->desktop == DESKTOP_ALL)
444             && /* the timestamp is from before you changed desktops */
445             self->user_time && screen_desktop_user_time &&
446             !event_time_after(self->user_time, screen_desktop_user_time))
447         {
448             activate = FALSE;
449             ob_debug_type(OB_DEBUG_FOCUS,
450                           "Not focusing the window because its on another "
451                           "desktop\n");
452         }
453         /* If something is focused, and it's not our parent... */
454         else if (focus_client && client_search_focus_parent(self) == NULL)
455         {
456             /* If time stamp is old, don't steal focus */
457             if (self->user_time && last_time &&
458                 !event_time_after(self->user_time, last_time))
459             {
460                 activate = FALSE;
461                 ob_debug_type(OB_DEBUG_FOCUS,
462                               "Not focusing the window because the time is "
463                               "too old\n");
464             }
465             /* Don't steal focus from globally active clients.
466                I stole this idea from KWin. It seems nice.
467              */
468             if (!(focus_client->can_focus || focus_client->focus_notify)) {
469                 activate = FALSE;
470                 ob_debug_type(OB_DEBUG_FOCUS,
471                               "Not focusing the window because a globally "
472                               "active client has focus\n");
473             }
474         }
475
476         if (!activate) {
477             ob_debug_type(OB_DEBUG_FOCUS,
478                           "Focus stealing prevention activated for %s with "
479                           "time %u (last time %u)\n",
480                           self->title, self->user_time, last_time);
481             /* if the client isn't focused, then hilite it so the user
482                knows it is there */
483             client_hilite(self, TRUE);
484         }
485     }
486     else {
487         /* This may look rather odd. Well it's because new windows are added
488            to the stacking order non-intrusively. If we're not going to focus
489            the new window or hilite it, then we raise it to the top. This will
490            take affect for things that don't get focused like splash screens.
491            Also if you don't have focus_new enabled, then it's going to get
492            raised to the top. Legacy begets legacy I guess?
493         */
494         if (!client_restore_session_stacking(self))
495             stacking_raise(CLIENT_AS_WINDOW(self));
496     }
497
498     /* adjust the frame to the client's size before showing the window */
499     frame_adjust_area(self->frame, FALSE, TRUE, FALSE);
500     frame_adjust_client_area(self->frame);
501
502     /* this has to happen before we try focus the window, but we want it to
503        happen after the client's stacking has been determined or it looks bad
504     */
505     client_show(self);
506
507     /* generate a ConfigureNotify telling the client where it is.
508
509        do this after showing the window. otherwise applications tend to
510        ignore the configurenotify. */
511     client_configure_full(self, self->area.x, self->area.y,
512                           self->area.width, self->area.height,
513                           FALSE, TRUE);
514
515
516     if (activate) {
517         gboolean stacked = client_restore_session_stacking(self);
518         client_present(self, FALSE, !stacked);
519     }
520
521     /* add to client list/map */
522     client_list = g_list_append(client_list, self);
523     g_hash_table_insert(window_map, &self->window, self);
524
525     /* this has to happen after we're in the client_list */
526     if (STRUT_EXISTS(self->strut))
527         screen_update_areas();
528
529     /* update the list hints */
530     client_set_list();
531
532     ob_debug("Managed window 0x%lx plate 0x%x (%s)\n",
533              window, self->frame->plate, self->class);
534
535     return;
536 }
537
538
539 ObClient *client_fake_manage(Window window)
540 {
541     ObClient *self;
542     ObAppSettings *settings;
543
544     ob_debug("Pretend-managing window: %lx\n", window);
545
546     /* do this minimal stuff to figure out the client's decorations */
547
548     self = g_new0(ObClient, 1);
549     self->window = window;
550
551     client_get_all(self, FALSE);
552     /* per-app settings override stuff, and return the settings for other
553        uses too */
554     settings = client_get_settings_state(self);
555
556     client_setup_decor_and_functions(self);
557
558     /* create the decoration frame for the client window and adjust its size */
559     self->frame = frame_new(self);
560     frame_adjust_area(self->frame, FALSE, TRUE, TRUE);
561     return self;
562 }
563
564 void client_unmanage_all()
565 {
566     while (client_list != NULL)
567         client_unmanage(client_list->data);
568 }
569
570 void client_unmanage(ObClient *self)
571 {
572     guint j;
573     GSList *it;
574
575     ob_debug("Unmanaging window: 0x%x plate 0x%x (%s) (%s)\n",
576              self->window, self->frame->plate,
577              self->class, self->title ? self->title : "");
578
579     g_assert(self != NULL);
580
581     /* we dont want events no more. do this before hiding the frame so we
582        don't generate more events */
583     XSelectInput(ob_display, self->window, NoEventMask);
584
585     frame_hide(self->frame);
586     /* flush to send the hide to the server quickly */
587     XFlush(ob_display);
588
589     /* ignore enter events from the unmap so it doesnt mess with the
590        focus */
591     event_ignore_queued_enters();
592
593     mouse_grab_for_client(self, FALSE);
594
595     /* remove the window from our save set */
596     XChangeSaveSet(ob_display, self->window, SetModeDelete);
597
598     /* kill the property windows */
599     propwin_remove(self->user_time_window, OB_PROPWIN_USER_TIME, self);
600
601     /* update the focus lists */
602     focus_order_remove(self);
603     if (client_focused(self)) {
604         /* don't leave an invalid focus_client */
605         focus_client = NULL;
606     }
607
608     client_list = g_list_remove(client_list, self);
609     stacking_remove(self);
610     g_hash_table_remove(window_map, &self->window);
611
612     /* once the client is out of the list, update the struts to remove its
613        influence */
614     if (STRUT_EXISTS(self->strut))
615         screen_update_areas();
616
617     client_call_notifies(self, client_destroy_notifies);
618
619     /* tell our parent(s) that we're gone */
620     if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */
621         for (it = self->group->members; it; it = g_slist_next(it))
622             if (it->data != self)
623                 ((ObClient*)it->data)->transients =
624                     g_slist_remove(((ObClient*)it->data)->transients,self);
625     } else if (self->transient_for) {        /* transient of window */
626         self->transient_for->transients =
627             g_slist_remove(self->transient_for->transients, self);
628     }
629
630     /* tell our transients that we're gone */
631     for (it = self->transients; it; it = g_slist_next(it)) {
632         if (((ObClient*)it->data)->transient_for != OB_TRAN_GROUP) {
633             ((ObClient*)it->data)->transient_for = NULL;
634             client_calc_layer(it->data);
635         }
636     }
637
638     /* remove from its group */
639     if (self->group) {
640         group_remove(self->group, self);
641         self->group = NULL;
642     }
643
644     /* restore the window's original geometry so it is not lost */
645     {
646         Rect a;
647
648         /* give the client its border back */
649         client_toggle_border(self, TRUE);
650
651         a = self->area;
652
653         if (self->fullscreen)
654             a = self->pre_fullscreen_area;
655         else if (self->max_horz || self->max_vert) {
656             if (self->max_horz) {
657                 a.x = self->pre_max_area.x;
658                 a.width = self->pre_max_area.width;
659             }
660             if (self->max_vert) {
661                 a.y = self->pre_max_area.y;
662                 a.height = self->pre_max_area.height;
663             }
664         }
665
666         self->fullscreen = self->max_horz = self->max_vert = FALSE;
667         self->decorations = 0; /* unmanaged windows have no decor */
668
669         client_move_resize(self, a.x, a.y, a.width, a.height);
670     }
671
672     /* reparent the window out of the frame, and free the frame */
673     frame_release_client(self->frame);
674     frame_free(self->frame);
675     self->frame = NULL;
676
677     if (ob_state() != OB_STATE_EXITING) {
678         /* these values should not be persisted across a window
679            unmapping/mapping */
680         PROP_ERASE(self->window, net_wm_desktop);
681         PROP_ERASE(self->window, net_wm_state);
682         PROP_ERASE(self->window, wm_state);
683     } else {
684         /* if we're left in an unmapped state, the client wont be mapped.
685            this is bad, since we will no longer be managing the window on
686            restart */
687         XMapWindow(ob_display, self->window);
688     }
689
690     /* update the list hints */
691     client_set_list();
692
693     ob_debug("Unmanaged window 0x%lx\n", self->window);
694
695     /* free all data allocated in the client struct */
696     g_slist_free(self->transients);
697     for (j = 0; j < self->nicons; ++j)
698         g_free(self->icons[j].data);
699     if (self->nicons > 0)
700         g_free(self->icons);
701     g_free(self->wm_command);
702     g_free(self->title);
703     g_free(self->icon_title);
704     g_free(self->name);
705     g_free(self->class);
706     g_free(self->role);
707     g_free(self->client_machine);
708     g_free(self->sm_client_id);
709     g_free(self);
710 }
711
712 void client_fake_unmanage(ObClient *self)
713 {
714     /* this is all that got allocated to get the decorations */
715
716     frame_free(self->frame);
717     g_free(self);
718 }
719
720 static ObAppSettings *client_get_settings_state(ObClient *self)
721 {
722     ObAppSettings *settings = NULL;
723     GSList *it;
724
725     for (it = config_per_app_settings; it; it = g_slist_next(it)) {
726         ObAppSettings *app = it->data;
727         
728         if ((app->name && !app->class && !strcmp(app->name, self->name))
729             || (app->class && !app->name && !strcmp(app->class, self->class))
730             || (app->class && app->name && !strcmp(app->class, self->class)
731                 && !strcmp(app->name, self->name)))
732         {
733             /* Match if no role was specified in the per app setting, or if the
734              * string matches the beginning of the role, since apps like to set
735              * the role to things like browser-window-23c4b2f */
736             if (!app->role
737                 || !strncmp(app->role, self->role, strlen(app->role)))
738             {
739                 ob_debug("Window matching: %s\n", app->name);
740                 /* use this one */
741                 settings = app;
742                 break;
743             }
744         }
745     }
746
747     if (settings) {
748         if (settings->shade != -1)
749             self->shaded = !!settings->shade;
750         if (settings->decor != -1)
751             self->undecorated = !settings->decor;
752         if (settings->iconic != -1)
753             self->iconic = !!settings->iconic;
754         if (settings->skip_pager != -1)
755             self->skip_pager = !!settings->skip_pager;
756         if (settings->skip_taskbar != -1)
757             self->skip_taskbar = !!settings->skip_taskbar;
758
759         if (settings->max_vert != -1)
760             self->max_vert = !!settings->max_vert;
761         if (settings->max_horz != -1)
762             self->max_horz = !!settings->max_horz;
763
764         if (settings->fullscreen != -1)
765             self->fullscreen = !!settings->fullscreen;
766
767         if (settings->desktop) {
768             if (settings->desktop == DESKTOP_ALL)
769                 self->desktop = settings->desktop;
770             else if (settings->desktop > 0 &&
771                      settings->desktop <= screen_num_desktops)
772                 self->desktop = settings->desktop - 1;
773         }
774
775         if (settings->layer == -1) {
776             self->below = TRUE;
777             self->above = FALSE;
778         }
779         else if (settings->layer == 0) {
780             self->below = FALSE;
781             self->above = FALSE;
782         }
783         else if (settings->layer == 1) {
784             self->below = FALSE;
785             self->above = TRUE;
786         }
787     }
788     return settings;
789 }
790
791 static void client_restore_session_state(ObClient *self)
792 {
793     GList *it;
794
795     ob_debug_type(OB_DEBUG_SM,
796                   "Restore session for client %s\n", self->title);
797
798     if (!(it = session_state_find(self))) {
799         ob_debug_type(OB_DEBUG_SM,
800                       "Session data not found for client %s\n", self->title);
801         return;
802     }
803
804     self->session = it->data;
805
806     ob_debug_type(OB_DEBUG_SM, "Session data loaded for client %s\n",
807                   self->title);
808
809     RECT_SET_POINT(self->area, self->session->x, self->session->y);
810     self->positioned = USPosition;
811     if (self->session->w > 0)
812         self->area.width = self->session->w;
813     if (self->session->h > 0)
814         self->area.height = self->session->h;
815     XResizeWindow(ob_display, self->window,
816                   self->area.width, self->area.height);
817
818     self->desktop = (self->session->desktop == DESKTOP_ALL ?
819                      self->session->desktop :
820                      MIN(screen_num_desktops - 1, self->session->desktop));
821     PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop);
822
823     self->shaded = self->session->shaded;
824     self->iconic = self->session->iconic;
825     self->skip_pager = self->session->skip_pager;
826     self->skip_taskbar = self->session->skip_taskbar;
827     self->fullscreen = self->session->fullscreen;
828     self->above = self->session->above;
829     self->below = self->session->below;
830     self->max_horz = self->session->max_horz;
831     self->max_vert = self->session->max_vert;
832     self->undecorated = self->session->undecorated;
833 }
834
835 static gboolean client_restore_session_stacking(ObClient *self)
836 {
837     GList *it, *mypos;
838
839     if (!self->session) return FALSE;
840
841     mypos = g_list_find(session_saved_state, self->session);
842     if (!mypos) return FALSE;
843
844     /* start above me and look for the first client */
845     for (it = g_list_previous(mypos); it; it = g_list_previous(it)) {
846         GList *cit;
847
848         for (cit = client_list; cit; cit = g_list_next(cit)) {
849             ObClient *c = cit->data;
850             /* found a client that was in the session, so go below it */
851             if (c->session == it->data) {
852                 stacking_below(CLIENT_AS_WINDOW(self),
853                                CLIENT_AS_WINDOW(cit->data));
854                 return TRUE;
855             }
856         }
857     }
858     return FALSE;
859 }
860
861 void client_move_onscreen(ObClient *self, gboolean rude)
862 {
863     gint x = self->area.x;
864     gint y = self->area.y;
865     if (client_find_onscreen(self, &x, &y,
866                              self->area.width,
867                              self->area.height, rude)) {
868         client_move(self, x, y);
869     }
870 }
871
872 gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
873                               gboolean rude)
874 {
875     Rect *mon_a, *all_a;
876     gint ox = *x, oy = *y;
877     gboolean rudel = rude, ruder = rude, rudet = rude, rudeb = rude;
878     gint fw, fh;
879
880     all_a = screen_area(self->desktop);
881     mon_a = screen_area_monitor(self->desktop, client_monitor(self));
882
883     /* get where the frame would be */
884     frame_client_gravity(self->frame, x, y, w, h);
885
886     /* get the requested size of the window with decorations */
887     fw = self->frame->size.left + w + self->frame->size.right;
888     fh = self->frame->size.top + h + self->frame->size.bottom;
889
890     /* This makes sure windows aren't entirely outside of the screen so you
891        can't see them at all.
892        It makes sure 10% of the window is on the screen at least. At don't let
893        it move itself off the top of the screen, which would hide the titlebar
894        on you. (The user can still do this if they want too, it's only limiting
895        the application.
896
897        XXX watch for xinerama dead areas...
898     */
899     if (client_normal(self)) {
900         if (!self->strut.right && *x + fw/10 >= all_a->x + all_a->width - 1)
901             *x = all_a->x + all_a->width - fw/10;
902         if (!self->strut.bottom && *y + fh/10 >= all_a->y + all_a->height - 1)
903             *y = all_a->y + all_a->height - fh/10;
904         if (!self->strut.left && *x + fw*9/10 - 1 < all_a->x)
905             *x = all_a->x - fw*9/10;
906         if (!self->strut.top && *y + fh*9/10 - 1 < all_a->y)
907             *y = all_a->y - fw*9/10;
908     }
909
910     /* If rudeness wasn't requested, then figure out of the client is currently
911        entirely on the screen. If it is, and the position isn't changing by
912        request, and it is enlarging, then be rude even though it wasn't
913        requested */
914     if (!rude) {
915         Point oldtl, oldtr, oldbl, oldbr;
916         Point newtl, newtr, newbl, newbr;
917         gboolean stationary_l, stationary_r, stationary_t, stationary_b;
918
919         POINT_SET(oldtl, self->frame->area.x, self->frame->area.y);
920         POINT_SET(oldbr, self->frame->area.x + self->frame->area.width - 1,
921                   self->frame->area.y + self->frame->area.height - 1);
922         POINT_SET(oldtr, oldbr.x, oldtl.y);
923         POINT_SET(oldbl, oldtl.x, oldbr.y);
924
925         POINT_SET(newtl, *x, *y);
926         POINT_SET(newbr, *x + fw - 1, *y + fh - 1);
927         POINT_SET(newtr, newbr.x, newtl.y);
928         POINT_SET(newbl, newtl.x, newbr.y);
929
930         /* is it moving or just resizing from some corner? */
931         stationary_l = oldtl.x == oldtl.x;
932         stationary_r = oldtr.x == oldtr.x;
933         stationary_t = oldtl.y == oldtl.y;
934         stationary_b = oldbl.y == oldbl.y;
935
936         /* if left edge is growing and didnt move right edge */
937         if (stationary_r && newtl.x < oldtl.x)
938             rudel = TRUE;
939         /* if right edge is growing and didnt move left edge */
940         if (stationary_l && newtr.x > oldtr.x)
941             ruder = TRUE;
942         /* if top edge is growing and didnt move bottom edge */
943         if (stationary_b && newtl.y < oldtl.y)
944             rudet = TRUE;
945         /* if bottom edge is growing and didnt move top edge */
946         if (stationary_t && newbl.y > oldbl.y)
947             rudeb = TRUE;
948     }
949
950     /* This here doesn't let windows even a pixel outside the struts/screen.
951      * When called from client_manage, programs placing themselves are
952      * forced completely onscreen, while things like
953      * xterm -geometry resolution-width/2 will work fine. Trying to
954      * place it completely offscreen will be handled in the above code.
955      * Sorry for this confused comment, i am tired. */
956     if (fw <= mon_a->width) {
957         if (rudel && !self->strut.left && *x < mon_a->x) *x = mon_a->x;
958         if (ruder && !self->strut.right && *x + fw > mon_a->x + mon_a->width)
959             *x = mon_a->x + mon_a->width - fw;
960     }
961     if (fh <= mon_a->height) {
962         if (rudet && !self->strut.top && *y < mon_a->y) *y = mon_a->y;
963         if (rudeb && !self->strut.bottom && *y + fh > mon_a->y + mon_a->height)
964             *y = mon_a->y + mon_a->height - fh;
965     }
966
967     /* get where the client should be */
968     frame_frame_gravity(self->frame, x, y, w, h);
969
970     return ox != *x || oy != *y;
971 }
972
973 static void client_toggle_border(ObClient *self, gboolean show)
974 {
975     /* adjust our idea of where the client is, based on its border. When the
976        border is removed, the client should now be considered to be in a
977        different position.
978        when re-adding the border to the client, the same operation needs to be
979        reversed. */
980     gint oldx = self->area.x, oldy = self->area.y;
981     gint x = oldx, y = oldy;
982     switch(self->gravity) {
983     default:
984     case NorthWestGravity:
985     case WestGravity:
986     case SouthWestGravity:
987         break;
988     case NorthEastGravity:
989     case EastGravity:
990     case SouthEastGravity:
991         if (show) x -= self->border_width * 2;
992         else      x += self->border_width * 2;
993         break;
994     case NorthGravity:
995     case SouthGravity:
996     case CenterGravity:
997     case ForgetGravity:
998     case StaticGravity:
999         if (show) x -= self->border_width;
1000         else      x += self->border_width;
1001         break;
1002     }
1003     switch(self->gravity) {
1004     default:
1005     case NorthWestGravity:
1006     case NorthGravity:
1007     case NorthEastGravity:
1008         break;
1009     case SouthWestGravity:
1010     case SouthGravity:
1011     case SouthEastGravity:
1012         if (show) y -= self->border_width * 2;
1013         else      y += self->border_width * 2;
1014         break;
1015     case WestGravity:
1016     case EastGravity:
1017     case CenterGravity:
1018     case ForgetGravity:
1019     case StaticGravity:
1020         if (show) y -= self->border_width;
1021         else      y += self->border_width;
1022         break;
1023     }
1024     self->area.x = x;
1025     self->area.y = y;
1026
1027     if (show) {
1028         XSetWindowBorderWidth(ob_display, self->window, self->border_width);
1029
1030         /* set border_width to 0 because there is no border to add into
1031            calculations anymore */
1032         self->border_width = 0;
1033     } else
1034         XSetWindowBorderWidth(ob_display, self->window, 0);
1035 }
1036
1037
1038 static void client_get_all(ObClient *self, gboolean real)
1039 {
1040     /* this is needed for the frame to set itself up */
1041     client_get_area(self);
1042
1043     /* these things can change the decor and functions of the window */
1044
1045     client_get_mwm_hints(self);
1046     /* this can change the mwmhints for special cases */
1047     client_get_type_and_transientness(self);
1048     client_get_state(self);
1049     client_update_normal_hints(self);
1050
1051     /* get the session related properties, these can change decorations
1052        from per-app settings */
1053     client_get_session_ids(self);
1054
1055     /* now we got everything that can affect the decorations */
1056     if (!real)
1057         return;
1058
1059     /* get this early so we have it for debugging */
1060     client_update_title(self);
1061
1062     client_update_protocols(self);
1063
1064     client_update_wmhints(self);
1065     /* this may have already been called from client_update_wmhints */
1066     if (self->transient_for == NULL)
1067         client_update_transient_for(self);
1068
1069     client_get_startup_id(self);
1070     client_get_desktop(self);/* uses transient data/group/startup id if a
1071                                 desktop is not specified */
1072     client_get_shaped(self);
1073
1074     {
1075         /* a couple type-based defaults for new windows */
1076
1077         /* this makes sure that these windows appear on all desktops */
1078         if (self->type == OB_CLIENT_TYPE_DESKTOP)
1079             self->desktop = DESKTOP_ALL;
1080     }
1081   
1082 #ifdef SYNC
1083     client_update_sync_request_counter(self);
1084 #endif
1085
1086     client_get_colormap(self);
1087     client_update_strut(self);
1088     client_update_icons(self);
1089     client_update_user_time_window(self);
1090     if (!self->user_time_window) /* check if this would have been called */
1091         client_update_user_time(self);
1092     client_update_icon_geometry(self);
1093 }
1094
1095 static void client_get_startup_id(ObClient *self)
1096 {
1097     if (!(PROP_GETS(self->window, net_startup_id, utf8, &self->startup_id)))
1098         if (self->group)
1099             PROP_GETS(self->group->leader,
1100                       net_startup_id, utf8, &self->startup_id);
1101 }
1102
1103 static void client_get_area(ObClient *self)
1104 {
1105     XWindowAttributes wattrib;
1106     Status ret;
1107   
1108     ret = XGetWindowAttributes(ob_display, self->window, &wattrib);
1109     g_assert(ret != BadWindow);
1110
1111     RECT_SET(self->area, wattrib.x, wattrib.y, wattrib.width, wattrib.height);
1112     POINT_SET(self->root_pos, wattrib.x, wattrib.y);
1113     self->border_width = wattrib.border_width;
1114
1115     ob_debug("client area: %d %d  %d %d\n", wattrib.x, wattrib.y,
1116              wattrib.width, wattrib.height);
1117 }
1118
1119 static void client_get_desktop(ObClient *self)
1120 {
1121     guint32 d = screen_num_desktops; /* an always-invalid value */
1122
1123     if (PROP_GET32(self->window, net_wm_desktop, cardinal, &d)) {
1124         if (d >= screen_num_desktops && d != DESKTOP_ALL)
1125             self->desktop = screen_num_desktops - 1;
1126         else
1127             self->desktop = d;
1128     } else {
1129         gboolean trdesk = FALSE;
1130
1131         if (self->transient_for) {
1132             if (self->transient_for != OB_TRAN_GROUP) {
1133                 self->desktop = self->transient_for->desktop;
1134                 trdesk = TRUE;
1135             } else {
1136                 GSList *it;
1137
1138                 for (it = self->group->members; it; it = g_slist_next(it))
1139                     if (it->data != self &&
1140                         !((ObClient*)it->data)->transient_for) {
1141                         self->desktop = ((ObClient*)it->data)->desktop;
1142                         trdesk = TRUE;
1143                         break;
1144                     }
1145             }
1146         }
1147         if (!trdesk) {
1148             /* try get from the startup-notification protocol */
1149             if (sn_get_desktop(self->startup_id, &self->desktop)) {
1150                 if (self->desktop >= screen_num_desktops &&
1151                     self->desktop != DESKTOP_ALL)
1152                     self->desktop = screen_num_desktops - 1;
1153             } else
1154                 /* defaults to the current desktop */
1155                 self->desktop = screen_desktop;
1156         }
1157     }
1158 }
1159
1160 static void client_get_state(ObClient *self)
1161 {
1162     guint32 *state;
1163     guint num;
1164   
1165     if (PROP_GETA32(self->window, net_wm_state, atom, &state, &num)) {
1166         gulong i;
1167         for (i = 0; i < num; ++i) {
1168             if (state[i] == prop_atoms.net_wm_state_modal)
1169                 self->modal = TRUE;
1170             else if (state[i] == prop_atoms.net_wm_state_shaded)
1171                 self->shaded = TRUE;
1172             else if (state[i] == prop_atoms.net_wm_state_hidden)
1173                 self->iconic = TRUE;
1174             else if (state[i] == prop_atoms.net_wm_state_skip_taskbar)
1175                 self->skip_taskbar = TRUE;
1176             else if (state[i] == prop_atoms.net_wm_state_skip_pager)
1177                 self->skip_pager = TRUE;
1178             else if (state[i] == prop_atoms.net_wm_state_fullscreen)
1179                 self->fullscreen = TRUE;
1180             else if (state[i] == prop_atoms.net_wm_state_maximized_vert)
1181                 self->max_vert = TRUE;
1182             else if (state[i] == prop_atoms.net_wm_state_maximized_horz)
1183                 self->max_horz = TRUE;
1184             else if (state[i] == prop_atoms.net_wm_state_above)
1185                 self->above = TRUE;
1186             else if (state[i] == prop_atoms.net_wm_state_below)
1187                 self->below = TRUE;
1188             else if (state[i] == prop_atoms.net_wm_state_demands_attention)
1189                 self->demands_attention = TRUE;
1190             else if (state[i] == prop_atoms.ob_wm_state_undecorated)
1191                 self->undecorated = TRUE;
1192         }
1193
1194         g_free(state);
1195     }
1196 }
1197
1198 static void client_get_shaped(ObClient *self)
1199 {
1200     self->shaped = FALSE;
1201 #ifdef   SHAPE
1202     if (extensions_shape) {
1203         gint foo;
1204         guint ufoo;
1205         gint s;
1206
1207         XShapeSelectInput(ob_display, self->window, ShapeNotifyMask);
1208
1209         XShapeQueryExtents(ob_display, self->window, &s, &foo,
1210                            &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo,
1211                            &ufoo);
1212         self->shaped = (s != 0);
1213     }
1214 #endif
1215 }
1216
1217 void client_update_transient_for(ObClient *self)
1218 {
1219     Window t = None;
1220     ObClient *target = NULL;
1221
1222     if (XGetTransientForHint(ob_display, self->window, &t)) {
1223         if (t != self->window) { /* cant be transient to itself! */
1224             target = g_hash_table_lookup(window_map, &t);
1225             /* if this happens then we need to check for it*/
1226             g_assert(target != self);
1227             if (target && !WINDOW_IS_CLIENT(target)) {
1228                 /* this can happen when a dialog is a child of
1229                    a dockapp, for example */
1230                 target = NULL;
1231             }
1232
1233             /* THIS IS SO ANNOYING ! ! ! ! Let me explain.... have a seat..
1234
1235                Setting the transient_for to Root is actually illegal, however
1236                applications from time have done this to specify transient for
1237                their group.
1238
1239                Now you can do that by being a TYPE_DIALOG and not setting
1240                the transient_for hint at all on your window. But people still
1241                use Root, and Kwin is very strange in this regard.
1242
1243                KWin 3.0 will not consider windows with transient_for set to
1244                Root as transient for their group *UNLESS* they are also modal.
1245                In that case, it will make them transient for the group. This
1246                leads to all sorts of weird behavior from KDE apps which are
1247                only tested in KWin. I'd like to follow their behavior just to
1248                make this work right with KDE stuff, but that seems wrong.
1249             */
1250             if (!target && self->group) {
1251                 /* not transient to a client, see if it is transient for a
1252                    group */
1253                 if (t == RootWindow(ob_display, ob_screen)) {
1254                     /* window is a transient for its group! */
1255                     target = OB_TRAN_GROUP;
1256                 }
1257             }
1258         }
1259     } else if (self->transient && self->group)
1260         target = OB_TRAN_GROUP;
1261
1262     client_update_transient_tree(self, self->group, self->group,
1263                                  self->transient_for, target);
1264     self->transient_for = target;
1265                           
1266 }
1267
1268 static void client_update_transient_tree(ObClient *self,
1269                                          ObGroup *oldgroup, ObGroup *newgroup,
1270                                          ObClient* oldparent,
1271                                          ObClient *newparent)
1272 {
1273     GSList *it, *next;
1274     ObClient *c;
1275
1276     /* * *
1277       Group transient windows are not allowed to have other group
1278       transient windows as their children.
1279       * * */
1280
1281
1282     /* No change has occured */
1283     if (oldgroup == newgroup && oldparent == newparent) return;
1284
1285     /** Remove the client from the transient tree wherever it has changed **/
1286
1287     /* If the window is becoming a direct transient for a window in its group
1288        then any group transients which were our children and are now becoming
1289        our parents need to stop being our children.
1290
1291        Group transients can't be children of group transients already, but
1292        we could have any number of direct parents above up, any of which could
1293        be transient for the group, and we need to remove it from our children.
1294     */
1295     if (oldparent != newparent &&
1296         newparent != NULL && newparent != OB_TRAN_GROUP &&
1297         newgroup != NULL && newgroup == oldgroup)
1298     {
1299         ObClient *look = newparent;
1300         do {
1301             self->transients = g_slist_remove(self->transients, look);
1302             look = look->transient_for;
1303         } while (look != NULL && look != OB_TRAN_GROUP);
1304     }
1305             
1306
1307     /* If the group changed, or if we are just becoming transient for the
1308        group, then we need to remove any old group transient windows
1309        from our children. But if we were already transient for the group, then
1310        other group transients are not our children. */
1311     if ((oldgroup != newgroup ||
1312          (newparent == OB_TRAN_GROUP && oldparent != newparent)) &&
1313         oldgroup != NULL && oldparent != OB_TRAN_GROUP)
1314     {
1315         for (it = self->transients; it; it = next) {
1316             next = g_slist_next(it);
1317             c = it->data;
1318             if (c->group == oldgroup)
1319                 self->transients = g_slist_delete_link(self->transients, it);
1320         }
1321     }
1322
1323     /* If we used to be transient for a group and now we are not, or we're
1324        transient for a new group, then we need to remove ourselves from all
1325        our ex-parents */
1326     if (oldparent == OB_TRAN_GROUP && (oldgroup != newgroup ||
1327                                        oldparent != newparent))
1328     {
1329         for (it = oldgroup->members; it; it = g_slist_next(it)) {
1330             c = it->data;
1331             if (c != self && (!c->transient_for ||
1332                               c->transient_for != OB_TRAN_GROUP))
1333                 c->transients = g_slist_remove(c->transients, self);
1334         }
1335     }
1336     /* If we used to be transient for a single window and we are no longer
1337        transient for it, then we need to remove ourself from its children */
1338     else if (oldparent != NULL && oldparent != OB_TRAN_GROUP &&
1339              oldparent != newparent)
1340         oldparent->transients = g_slist_remove(oldparent->transients, self);
1341
1342
1343     /** Re-add the client to the transient tree wherever it has changed **/
1344
1345     /* If we're now transient for a group and we weren't transient for it
1346        before then we need to add ourselves to all our new parents */
1347     if (newparent == OB_TRAN_GROUP && (oldgroup != newgroup ||
1348                                        oldparent != newparent))
1349     {
1350         for (it = oldgroup->members; it; it = g_slist_next(it)) {
1351             c = it->data;
1352             if (c != self && (!c->transient_for ||
1353                               c->transient_for != OB_TRAN_GROUP))
1354                 c->transients = g_slist_prepend(c->transients, self);
1355         }
1356     }
1357     /* If we are now transient for a single window which we weren't before,
1358        we need to add ourselves to its children
1359
1360        WARNING: Cyclical transient ness is possible if two windows are
1361        transient for eachother.
1362     */
1363     else if (newparent != NULL && newparent != OB_TRAN_GROUP &&
1364              newparent != oldparent &&
1365              /* don't make ourself its child if it is already our child */
1366              !client_is_direct_child(self, newparent))
1367         newparent->transients = g_slist_prepend(newparent->transients, self);
1368
1369     /* If the group changed then we need to add any new group transient
1370        windows to our children. But if we're transient for the group, then
1371        other group transients are not our children.
1372
1373        WARNING: Cyclical transient-ness is possible. For e.g. if:
1374        A is transient for the group
1375        B is transient for A
1376        C is transient for B
1377        A can't be transient for C or we have a cycle
1378     */
1379     if (oldgroup != newgroup && newgroup != NULL &&
1380         newparent != OB_TRAN_GROUP)
1381     {
1382         for (it = newgroup->members; it; it = g_slist_next(it)) {
1383             c = it->data;
1384             if (c != self && c->transient_for == OB_TRAN_GROUP &&
1385                 /* Don't make it our child if it is already our parent */
1386                 !client_is_direct_child(c, self))
1387             {
1388                 self->transients = g_slist_prepend(self->transients, c);
1389             }
1390         }
1391     }
1392 }
1393
1394 static void client_get_mwm_hints(ObClient *self)
1395 {
1396     guint num;
1397     guint32 *hints;
1398
1399     self->mwmhints.flags = 0; /* default to none */
1400
1401     if (PROP_GETA32(self->window, motif_wm_hints, motif_wm_hints,
1402                     &hints, &num)) {
1403         if (num >= OB_MWM_ELEMENTS) {
1404             self->mwmhints.flags = hints[0];
1405             self->mwmhints.functions = hints[1];
1406             self->mwmhints.decorations = hints[2];
1407         }
1408         g_free(hints);
1409     }
1410 }
1411
1412 void client_get_type_and_transientness(ObClient *self)
1413 {
1414     guint num, i;
1415     guint32 *val;
1416     Window t;
1417
1418     self->type = -1;
1419     self->transient = FALSE;
1420   
1421     if (PROP_GETA32(self->window, net_wm_window_type, atom, &val, &num)) {
1422         /* use the first value that we know about in the array */
1423         for (i = 0; i < num; ++i) {
1424             if (val[i] == prop_atoms.net_wm_window_type_desktop)
1425                 self->type = OB_CLIENT_TYPE_DESKTOP;
1426             else if (val[i] == prop_atoms.net_wm_window_type_dock)
1427                 self->type = OB_CLIENT_TYPE_DOCK;
1428             else if (val[i] == prop_atoms.net_wm_window_type_toolbar)
1429                 self->type = OB_CLIENT_TYPE_TOOLBAR;
1430             else if (val[i] == prop_atoms.net_wm_window_type_menu)
1431                 self->type = OB_CLIENT_TYPE_MENU;
1432             else if (val[i] == prop_atoms.net_wm_window_type_utility)
1433                 self->type = OB_CLIENT_TYPE_UTILITY;
1434             else if (val[i] == prop_atoms.net_wm_window_type_splash)
1435                 self->type = OB_CLIENT_TYPE_SPLASH;
1436             else if (val[i] == prop_atoms.net_wm_window_type_dialog)
1437                 self->type = OB_CLIENT_TYPE_DIALOG;
1438             else if (val[i] == prop_atoms.net_wm_window_type_normal)
1439                 self->type = OB_CLIENT_TYPE_NORMAL;
1440             else if (val[i] == prop_atoms.kde_net_wm_window_type_override) {
1441                 /* prevent this window from getting any decor or
1442                    functionality */
1443                 self->mwmhints.flags &= (OB_MWM_FLAG_FUNCTIONS |
1444                                          OB_MWM_FLAG_DECORATIONS);
1445                 self->mwmhints.decorations = 0;
1446                 self->mwmhints.functions = 0;
1447             }
1448             if (self->type != (ObClientType) -1)
1449                 break; /* grab the first legit type */
1450         }
1451         g_free(val);
1452     }
1453
1454     if (XGetTransientForHint(ob_display, self->window, &t))
1455         self->transient = TRUE;
1456             
1457     if (self->type == (ObClientType) -1) {
1458         /*the window type hint was not set, which means we either classify
1459           ourself as a normal window or a dialog, depending on if we are a
1460           transient. */
1461         if (self->transient)
1462             self->type = OB_CLIENT_TYPE_DIALOG;
1463         else
1464             self->type = OB_CLIENT_TYPE_NORMAL;
1465     }
1466
1467     /* then, based on our type, we can update our transientness.. */
1468     if (self->type == OB_CLIENT_TYPE_DIALOG ||
1469         self->type == OB_CLIENT_TYPE_TOOLBAR ||
1470         self->type == OB_CLIENT_TYPE_MENU ||
1471         self->type == OB_CLIENT_TYPE_UTILITY)
1472     {
1473         self->transient = TRUE;
1474     }
1475 }
1476
1477 void client_update_protocols(ObClient *self)
1478 {
1479     guint32 *proto;
1480     guint num_return, i;
1481
1482     self->focus_notify = FALSE;
1483     self->delete_window = FALSE;
1484
1485     if (PROP_GETA32(self->window, wm_protocols, atom, &proto, &num_return)) {
1486         for (i = 0; i < num_return; ++i) {
1487             if (proto[i] == prop_atoms.wm_delete_window)
1488                 /* this means we can request the window to close */
1489                 self->delete_window = TRUE;
1490             else if (proto[i] == prop_atoms.wm_take_focus)
1491                 /* if this protocol is requested, then the window will be
1492                    notified whenever we want it to receive focus */
1493                 self->focus_notify = TRUE;
1494 #ifdef SYNC
1495             else if (proto[i] == prop_atoms.net_wm_sync_request) 
1496                 /* if this protocol is requested, then resizing the
1497                    window will be synchronized between the frame and the
1498                    client */
1499                 self->sync_request = TRUE;
1500 #endif
1501         }
1502         g_free(proto);
1503     }
1504 }
1505
1506 #ifdef SYNC
1507 void client_update_sync_request_counter(ObClient *self)
1508 {
1509     guint32 i;
1510
1511     if (PROP_GET32(self->window, net_wm_sync_request_counter, cardinal, &i)) {
1512         self->sync_counter = i;
1513     } else
1514         self->sync_counter = None;
1515 }
1516 #endif
1517
1518 void client_get_colormap(ObClient *self)
1519 {
1520     XWindowAttributes wa;
1521
1522     if (XGetWindowAttributes(ob_display, self->window, &wa))
1523         client_update_colormap(self, wa.colormap);
1524 }
1525
1526 void client_update_colormap(ObClient *self, Colormap colormap)
1527 {
1528     self->colormap = colormap;
1529 }
1530
1531 void client_update_normal_hints(ObClient *self)
1532 {
1533     XSizeHints size;
1534     glong ret;
1535     gint oldgravity = self->gravity;
1536
1537     /* defaults */
1538     self->min_ratio = 0.0f;
1539     self->max_ratio = 0.0f;
1540     SIZE_SET(self->size_inc, 1, 1);
1541     SIZE_SET(self->base_size, 0, 0);
1542     SIZE_SET(self->min_size, 0, 0);
1543     SIZE_SET(self->max_size, G_MAXINT, G_MAXINT);
1544
1545     /* get the hints from the window */
1546     if (XGetWMNormalHints(ob_display, self->window, &size, &ret)) {
1547         /* normal windows can't request placement! har har
1548         if (!client_normal(self))
1549         */
1550         self->positioned = (size.flags & (PPosition|USPosition));
1551
1552         if (size.flags & PWinGravity) {
1553             self->gravity = size.win_gravity;
1554       
1555             /* if the client has a frame, i.e. has already been mapped and
1556                is changing its gravity */
1557             if (self->frame && self->gravity != oldgravity) {
1558                 /* move our idea of the client's position based on its new
1559                    gravity */
1560                 client_convert_gravity(self, oldgravity,
1561                                        &self->area.x, &self->area.y,
1562                                        self->area.width, self->area.height);
1563             }
1564         }
1565
1566         if (size.flags & PAspect) {
1567             if (size.min_aspect.y)
1568                 self->min_ratio =
1569                     (gfloat) size.min_aspect.x / size.min_aspect.y;
1570             if (size.max_aspect.y)
1571                 self->max_ratio =
1572                     (gfloat) size.max_aspect.x / size.max_aspect.y;
1573         }
1574
1575         if (size.flags & PMinSize)
1576             SIZE_SET(self->min_size, size.min_width, size.min_height);
1577     
1578         if (size.flags & PMaxSize)
1579             SIZE_SET(self->max_size, size.max_width, size.max_height);
1580     
1581         if (size.flags & PBaseSize)
1582             SIZE_SET(self->base_size, size.base_width, size.base_height);
1583     
1584         if (size.flags & PResizeInc && size.width_inc && size.height_inc)
1585             SIZE_SET(self->size_inc, size.width_inc, size.height_inc);
1586     }
1587 }
1588
1589 void client_setup_decor_and_functions(ObClient *self)
1590 {
1591     /* start with everything (cept fullscreen) */
1592     self->decorations =
1593         (OB_FRAME_DECOR_TITLEBAR |
1594          OB_FRAME_DECOR_HANDLE |
1595          OB_FRAME_DECOR_GRIPS |
1596          OB_FRAME_DECOR_BORDER |
1597          OB_FRAME_DECOR_ICON |
1598          OB_FRAME_DECOR_ALLDESKTOPS |
1599          OB_FRAME_DECOR_ICONIFY |
1600          OB_FRAME_DECOR_MAXIMIZE |
1601          OB_FRAME_DECOR_SHADE |
1602          OB_FRAME_DECOR_CLOSE);
1603     self->functions =
1604         (OB_CLIENT_FUNC_RESIZE |
1605          OB_CLIENT_FUNC_MOVE |
1606          OB_CLIENT_FUNC_ICONIFY |
1607          OB_CLIENT_FUNC_MAXIMIZE |
1608          OB_CLIENT_FUNC_SHADE |
1609          OB_CLIENT_FUNC_CLOSE |
1610          OB_CLIENT_FUNC_BELOW |
1611          OB_CLIENT_FUNC_ABOVE);
1612
1613     if (!(self->min_size.width < self->max_size.width ||
1614           self->min_size.height < self->max_size.height))
1615         self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1616
1617     switch (self->type) {
1618     case OB_CLIENT_TYPE_NORMAL:
1619         /* normal windows retain all of the possible decorations and
1620            functionality, and are the only windows that you can fullscreen */
1621         self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
1622         break;
1623
1624     case OB_CLIENT_TYPE_DIALOG:
1625     case OB_CLIENT_TYPE_UTILITY:
1626         /* these windows cannot be maximized */
1627         self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1628         break;
1629
1630     case OB_CLIENT_TYPE_MENU:
1631     case OB_CLIENT_TYPE_TOOLBAR:
1632         /* these windows get less functionality */
1633         self->functions &= ~(OB_CLIENT_FUNC_ICONIFY | OB_CLIENT_FUNC_RESIZE);
1634         break;
1635
1636     case OB_CLIENT_TYPE_SPLASH:
1637         /* these don't get get any decorations, and the only thing you can
1638            do with them is move them */
1639         self->decorations = 0;
1640         self->functions = OB_CLIENT_FUNC_MOVE;
1641
1642     case OB_CLIENT_TYPE_DESKTOP:
1643         /* these windows are not manipulated by the window manager */
1644         self->decorations = 0;
1645         self->functions = 0;
1646
1647     case OB_CLIENT_TYPE_DOCK:
1648         /* these windows are not manipulated by the window manager, but they
1649            can set below layer which has a special meaning */
1650         self->decorations = 0;
1651         self->functions = OB_CLIENT_FUNC_BELOW;
1652         break;
1653     }
1654
1655     /* Mwm Hints are applied subtractively to what has already been chosen for
1656        decor and functionality */
1657     if (self->mwmhints.flags & OB_MWM_FLAG_DECORATIONS) {
1658         if (! (self->mwmhints.decorations & OB_MWM_DECOR_ALL)) {
1659             if (! ((self->mwmhints.decorations & OB_MWM_DECOR_HANDLE) ||
1660                    (self->mwmhints.decorations & OB_MWM_DECOR_TITLE)))
1661             {
1662                 /* if the mwm hints request no handle or title, then all
1663                    decorations are disabled, but keep the border if that's
1664                    specified */
1665                 if (self->mwmhints.decorations & OB_MWM_DECOR_BORDER)
1666                     self->decorations = OB_FRAME_DECOR_BORDER;
1667                 else
1668                     self->decorations = 0;
1669             }
1670         }
1671     }
1672
1673     if (self->mwmhints.flags & OB_MWM_FLAG_FUNCTIONS) {
1674         if (! (self->mwmhints.functions & OB_MWM_FUNC_ALL)) {
1675             if (! (self->mwmhints.functions & OB_MWM_FUNC_RESIZE))
1676                 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1677             if (! (self->mwmhints.functions & OB_MWM_FUNC_MOVE))
1678                 self->functions &= ~OB_CLIENT_FUNC_MOVE;
1679             /* dont let mwm hints kill any buttons
1680                if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1681                self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1682                if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1683                self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1684             */
1685             /* dont let mwm hints kill the close button
1686                if (! (self->mwmhints.functions & MwmFunc_Close))
1687                self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1688         }
1689     }
1690
1691     if (!(self->functions & OB_CLIENT_FUNC_SHADE))
1692         self->decorations &= ~OB_FRAME_DECOR_SHADE;
1693     if (!(self->functions & OB_CLIENT_FUNC_ICONIFY))
1694         self->decorations &= ~OB_FRAME_DECOR_ICONIFY;
1695     if (!(self->functions & OB_CLIENT_FUNC_RESIZE))
1696         self->decorations &= ~(OB_FRAME_DECOR_GRIPS | OB_FRAME_DECOR_HANDLE);
1697
1698     /* can't maximize without moving/resizing */
1699     if (!((self->functions & OB_CLIENT_FUNC_MAXIMIZE) &&
1700           (self->functions & OB_CLIENT_FUNC_MOVE) &&
1701           (self->functions & OB_CLIENT_FUNC_RESIZE))) {
1702         self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1703         self->decorations &= ~OB_FRAME_DECOR_MAXIMIZE;
1704     }
1705
1706     /* kill the handle on fully maxed windows */
1707     if (self->max_vert && self->max_horz)
1708         self->decorations &= ~(OB_FRAME_DECOR_HANDLE | OB_FRAME_DECOR_GRIPS);
1709
1710     /* finally, the user can have requested no decorations, which overrides
1711        everything (but doesnt give it a border if it doesnt have one) */
1712     if (self->undecorated) {
1713         if (config_theme_keepborder)
1714             self->decorations &= OB_FRAME_DECOR_BORDER;
1715         else
1716             self->decorations = 0;
1717     }
1718
1719     /* if we don't have a titlebar, then we cannot shade! */
1720     if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
1721         self->functions &= ~OB_CLIENT_FUNC_SHADE;
1722
1723     /* now we need to check against rules for the client's current state */
1724     if (self->fullscreen) {
1725         self->functions &= (OB_CLIENT_FUNC_CLOSE |
1726                             OB_CLIENT_FUNC_FULLSCREEN |
1727                             OB_CLIENT_FUNC_ICONIFY);
1728         self->decorations = 0;
1729     }
1730
1731     client_change_allowed_actions(self);
1732
1733     if (self->frame) {
1734         /* adjust the client's decorations, etc. */
1735         client_reconfigure(self);
1736     }
1737 }
1738
1739 static void client_change_allowed_actions(ObClient *self)
1740 {
1741     gulong actions[11];
1742     gint num = 0;
1743
1744     /* desktop windows are kept on all desktops */
1745     if (self->type != OB_CLIENT_TYPE_DESKTOP)
1746         actions[num++] = prop_atoms.net_wm_action_change_desktop;
1747
1748     if (self->functions & OB_CLIENT_FUNC_SHADE)
1749         actions[num++] = prop_atoms.net_wm_action_shade;
1750     if (self->functions & OB_CLIENT_FUNC_CLOSE)
1751         actions[num++] = prop_atoms.net_wm_action_close;
1752     if (self->functions & OB_CLIENT_FUNC_MOVE)
1753         actions[num++] = prop_atoms.net_wm_action_move;
1754     if (self->functions & OB_CLIENT_FUNC_ICONIFY)
1755         actions[num++] = prop_atoms.net_wm_action_minimize;
1756     if (self->functions & OB_CLIENT_FUNC_RESIZE)
1757         actions[num++] = prop_atoms.net_wm_action_resize;
1758     if (self->functions & OB_CLIENT_FUNC_FULLSCREEN)
1759         actions[num++] = prop_atoms.net_wm_action_fullscreen;
1760     if (self->functions & OB_CLIENT_FUNC_MAXIMIZE) {
1761         actions[num++] = prop_atoms.net_wm_action_maximize_horz;
1762         actions[num++] = prop_atoms.net_wm_action_maximize_vert;
1763     }
1764     if (self->functions & OB_CLIENT_FUNC_ABOVE)
1765         actions[num++] = prop_atoms.net_wm_action_above;
1766     if (self->functions & OB_CLIENT_FUNC_BELOW)
1767         actions[num++] = prop_atoms.net_wm_action_below;
1768
1769     PROP_SETA32(self->window, net_wm_allowed_actions, atom, actions, num);
1770
1771     /* make sure the window isn't breaking any rules now */
1772
1773     if (!(self->functions & OB_CLIENT_FUNC_SHADE) && self->shaded) {
1774         if (self->frame) client_shade(self, FALSE);
1775         else self->shaded = FALSE;
1776     }
1777     if (!(self->functions & OB_CLIENT_FUNC_ICONIFY) && self->iconic) {
1778         if (self->frame) client_iconify(self, FALSE, TRUE, FALSE);
1779         else self->iconic = FALSE;
1780     }
1781     if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) && self->fullscreen) {
1782         if (self->frame) client_fullscreen(self, FALSE);
1783         else self->fullscreen = FALSE;
1784     }
1785     if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && (self->max_horz ||
1786                                                          self->max_vert)) {
1787         if (self->frame) client_maximize(self, FALSE, 0);
1788         else self->max_vert = self->max_horz = FALSE;
1789     }
1790 }
1791
1792 void client_reconfigure(ObClient *self)
1793 {
1794     /* by making this pass FALSE for user, we avoid the emacs event storm where
1795        every configurenotify causes an update in its normal hints, i think this
1796        is generally what we want anyways... */
1797     client_configure(self, self->area.x, self->area.y,
1798                      self->area.width, self->area.height, FALSE, TRUE);
1799 }
1800
1801 void client_update_wmhints(ObClient *self)
1802 {
1803     XWMHints *hints;
1804
1805     /* assume a window takes input if it doesnt specify */
1806     self->can_focus = TRUE;
1807   
1808     if ((hints = XGetWMHints(ob_display, self->window)) != NULL) {
1809         gboolean ur;
1810
1811         if (hints->flags & InputHint)
1812             self->can_focus = hints->input;
1813
1814         /* only do this when first managing the window *AND* when we aren't
1815            starting up! */
1816         if (ob_state() != OB_STATE_STARTING && self->frame == NULL)
1817             if (hints->flags & StateHint)
1818                 self->iconic = hints->initial_state == IconicState;
1819
1820         ur = self->urgent;
1821         self->urgent = (hints->flags & XUrgencyHint);
1822         if (self->urgent && !ur)
1823             client_hilite(self, TRUE);
1824         else if (!self->urgent && ur && self->demands_attention)
1825             client_hilite(self, FALSE);
1826
1827         if (!(hints->flags & WindowGroupHint))
1828             hints->window_group = None;
1829
1830         /* did the group state change? */
1831         if (hints->window_group !=
1832             (self->group ? self->group->leader : None))
1833         {
1834             ObGroup *oldgroup = self->group;
1835
1836             /* remove from the old group if there was one */
1837             if (self->group != NULL) {
1838                 group_remove(self->group, self);
1839                 self->group = NULL;
1840             }
1841
1842             /* add ourself to the group if we have one */
1843             if (hints->window_group != None) {
1844                 self->group = group_add(hints->window_group, self);
1845             }
1846
1847             /* Put ourselves into the new group's transient tree, and remove
1848                ourselves from the old group's */
1849             client_update_transient_tree(self, oldgroup, self->group,
1850                                          self->transient_for,
1851                                          self->transient_for);
1852
1853             /* Lastly, being in a group, or not, can change if the window is
1854                transient for anything.
1855
1856                The logic for this is:
1857                self->transient = TRUE always if the window wants to be
1858                transient for something, even if transient_for was NULL because
1859                it wasn't in a group before.
1860
1861                If transient_for was NULL and oldgroup was NULL we can assume
1862                that when we add the new group, it will become transient for
1863                something.
1864
1865                If transient_for was OB_TRAN_GROUP, then it must have already
1866                had a group. If it is getting a new group, the above call to
1867                client_update_transient_tree has already taken care of
1868                everything ! If it is losing all group status then it will
1869                no longer be transient for anything and that needs to be
1870                updated.
1871             */
1872             if (self->transient &&
1873                 ((self->transient_for == NULL && oldgroup == NULL) ||
1874                  (self->transient_for == OB_TRAN_GROUP && !self->group)))
1875                 client_update_transient_for(self);
1876         }
1877
1878         /* the WM_HINTS can contain an icon */
1879         client_update_icons(self);
1880
1881         XFree(hints);
1882     }
1883 }
1884
1885 void client_update_title(ObClient *self)
1886 {
1887     gchar *data = NULL;
1888     gchar *visible = NULL;
1889
1890     g_free(self->title);
1891      
1892     /* try netwm */
1893     if (!PROP_GETS(self->window, net_wm_name, utf8, &data)) {
1894         /* try old x stuff */
1895         if (!(PROP_GETS(self->window, wm_name, locale, &data)
1896               || PROP_GETS(self->window, wm_name, utf8, &data))) {
1897             if (self->transient) {
1898                 /*
1899                   GNOME alert windows are not given titles:
1900                   http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
1901                 */
1902                 data = g_strdup("");
1903             } else
1904                 data = g_strdup("Unnamed Window");
1905         }
1906     }
1907
1908     if (self->client_machine) {
1909         visible = g_strdup_printf("%s (%s)", data, self->client_machine);
1910         g_free(data);
1911     } else
1912         visible = data;
1913
1914     PROP_SETS(self->window, net_wm_visible_name, visible);
1915     self->title = visible;
1916
1917     if (self->frame)
1918         frame_adjust_title(self->frame);
1919
1920     /* update the icon title */
1921     data = NULL;
1922     g_free(self->icon_title);
1923
1924     /* try netwm */
1925     if (!PROP_GETS(self->window, net_wm_icon_name, utf8, &data))
1926         /* try old x stuff */
1927         if (!(PROP_GETS(self->window, wm_icon_name, locale, &data) ||
1928               PROP_GETS(self->window, wm_icon_name, utf8, &data)))
1929             data = g_strdup(self->title);
1930
1931     PROP_SETS(self->window, net_wm_visible_icon_name, data);
1932     self->icon_title = data;
1933 }
1934
1935 void client_update_strut(ObClient *self)
1936 {
1937     guint num;
1938     guint32 *data;
1939     gboolean got = FALSE;
1940     StrutPartial strut;
1941
1942     if (PROP_GETA32(self->window, net_wm_strut_partial, cardinal,
1943                     &data, &num)) {
1944         if (num == 12) {
1945             got = TRUE;
1946             STRUT_PARTIAL_SET(strut,
1947                               data[0], data[2], data[1], data[3],
1948                               data[4], data[5], data[8], data[9],
1949                               data[6], data[7], data[10], data[11]);
1950         }
1951         g_free(data);
1952     }
1953
1954     if (!got &&
1955         PROP_GETA32(self->window, net_wm_strut, cardinal, &data, &num)) {
1956         if (num == 4) {
1957             const Rect *a;
1958
1959             got = TRUE;
1960
1961             /* use the screen's width/height */
1962             a = screen_physical_area();
1963
1964             STRUT_PARTIAL_SET(strut,
1965                               data[0], data[2], data[1], data[3],
1966                               a->y, a->y + a->height - 1,
1967                               a->x, a->x + a->width - 1,
1968                               a->y, a->y + a->height - 1,
1969                               a->x, a->x + a->width - 1);
1970         }
1971         g_free(data);
1972     }
1973
1974     if (!got)
1975         STRUT_PARTIAL_SET(strut, 0, 0, 0, 0,
1976                           0, 0, 0, 0, 0, 0, 0, 0);
1977
1978     if (!STRUT_EQUAL(strut, self->strut)) {
1979         self->strut = strut;
1980
1981         /* updating here is pointless while we're being mapped cuz we're not in
1982            the client list yet */
1983         if (self->frame)
1984             screen_update_areas();
1985     }
1986 }
1987
1988 void client_update_icons(ObClient *self)
1989 {
1990     guint num;
1991     guint32 *data;
1992     guint w, h, i, j;
1993
1994     for (i = 0; i < self->nicons; ++i)
1995         g_free(self->icons[i].data);
1996     if (self->nicons > 0)
1997         g_free(self->icons);
1998     self->nicons = 0;
1999
2000     if (PROP_GETA32(self->window, net_wm_icon, cardinal, &data, &num)) {
2001         /* figure out how many valid icons are in here */
2002         i = 0;
2003         while (num - i > 2) {
2004             w = data[i++];
2005             h = data[i++];
2006             i += w * h;
2007             if (i > num || w*h == 0) break;
2008             ++self->nicons;
2009         }
2010
2011         self->icons = g_new(ObClientIcon, self->nicons);
2012     
2013         /* store the icons */
2014         i = 0;
2015         for (j = 0; j < self->nicons; ++j) {
2016             guint x, y, t;
2017
2018             w = self->icons[j].width = data[i++];
2019             h = self->icons[j].height = data[i++];
2020
2021             if (w*h == 0) continue;
2022
2023             self->icons[j].data = g_new(RrPixel32, w * h);
2024             for (x = 0, y = 0, t = 0; t < w * h; ++t, ++x, ++i) {
2025                 if (x >= w) {
2026                     x = 0;
2027                     ++y;
2028                 }
2029                 self->icons[j].data[t] =
2030                     (((data[i] >> 24) & 0xff) << RrDefaultAlphaOffset) +
2031                     (((data[i] >> 16) & 0xff) << RrDefaultRedOffset) +
2032                     (((data[i] >> 8) & 0xff) << RrDefaultGreenOffset) +
2033                     (((data[i] >> 0) & 0xff) << RrDefaultBlueOffset);
2034             }
2035             g_assert(i <= num);
2036         }
2037
2038         g_free(data);
2039     } else {
2040         XWMHints *hints;
2041
2042         if ((hints = XGetWMHints(ob_display, self->window))) {
2043             if (hints->flags & IconPixmapHint) {
2044                 self->nicons++;
2045                 self->icons = g_new(ObClientIcon, self->nicons);
2046                 xerror_set_ignore(TRUE);
2047                 if (!RrPixmapToRGBA(ob_rr_inst,
2048                                     hints->icon_pixmap,
2049                                     (hints->flags & IconMaskHint ?
2050                                      hints->icon_mask : None),
2051                                     &self->icons[self->nicons-1].width,
2052                                     &self->icons[self->nicons-1].height,
2053                                     &self->icons[self->nicons-1].data)){
2054                     g_free(&self->icons[self->nicons-1]);
2055                     self->nicons--;
2056                 }
2057                 xerror_set_ignore(FALSE);
2058             }
2059             XFree(hints);
2060         }
2061     }
2062
2063     /* set the default icon onto the window
2064        in theory, this could be a race, but if a window doesn't set an icon
2065        or removes it entirely, it's not very likely it is going to set one
2066        right away afterwards */
2067     if (self->nicons == 0) {
2068         RrPixel32 *icon = ob_rr_theme->def_win_icon;
2069         gulong *data;
2070
2071         data = g_new(gulong, 48*48+2);
2072         data[0] = data[1] =  48;
2073         for (i = 0; i < 48*48; ++i)
2074             data[i+2] = (((icon[i] >> RrDefaultAlphaOffset) & 0xff) << 24) +
2075                 (((icon[i] >> RrDefaultRedOffset) & 0xff) << 16) +
2076                 (((icon[i] >> RrDefaultGreenOffset) & 0xff) << 8) +
2077                 (((icon[i] >> RrDefaultBlueOffset) & 0xff) << 0);
2078         PROP_SETA32(self->window, net_wm_icon, cardinal, data, 48*48+2);
2079         g_free(data);
2080     } else if (self->frame)
2081         /* don't draw the icon empty if we're just setting one now anyways,
2082            we'll get the property change any second */
2083         frame_adjust_icon(self->frame);
2084 }
2085
2086 void client_update_user_time(ObClient *self)
2087 {
2088     guint32 time;
2089     gboolean got = FALSE;
2090
2091     if (self->user_time_window)
2092         got = PROP_GET32(self->user_time_window,
2093                          net_wm_user_time, cardinal, &time);
2094     if (!got)
2095         got = PROP_GET32(self->window, net_wm_user_time, cardinal, &time);
2096
2097     if (got) {
2098         /* we set this every time, not just when it grows, because in practice
2099            sometimes time goes backwards! (ntpdate.. yay....) so.. if it goes
2100            backward we don't want all windows to stop focusing. we'll just
2101            assume noone is setting times older than the last one, cuz that
2102            would be pretty stupid anyways
2103         */
2104         self->user_time = time;
2105
2106         /*ob_debug("window %s user time %u\n", self->title, time);*/
2107     }
2108 }
2109
2110 void client_update_user_time_window(ObClient *self)
2111 {
2112     guint32 w;
2113
2114     if (!PROP_GET32(self->window, net_wm_user_time_window, window, &w))
2115         w = None;
2116
2117     if (w != self->user_time_window) {
2118         /* remove the old window */
2119         propwin_remove(self->user_time_window, OB_PROPWIN_USER_TIME, self);
2120         self->user_time_window = None;
2121
2122         if (self->group && self->group->leader == w) {
2123             ob_debug_type(OB_DEBUG_APP_BUGS, "Window is setting its "
2124                           "_NET_WM_USER_TYPE_WINDOW to its group leader\n");
2125             /* do it anyways..? */
2126         }
2127         else if (w == self->window) {
2128             ob_debug_type(OB_DEBUG_APP_BUGS, "Window is setting its "
2129                           "_NET_WM_USER_TIME_WINDOW to itself\n");
2130             w = None; /* don't do it */
2131         }
2132
2133         /* add the new window */
2134         propwin_add(w, OB_PROPWIN_USER_TIME, self);
2135         self->user_time_window = w;
2136
2137         /* and update from it */
2138         client_update_user_time(self);
2139     }
2140 }
2141
2142 void client_update_icon_geometry(ObClient *self)
2143 {
2144     guint num;
2145     guint32 *data;
2146
2147     RECT_SET(self->icon_geometry, 0, 0, 0, 0);
2148
2149     if (PROP_GETA32(self->window, net_wm_icon_geometry, cardinal, &data, &num)
2150         && num == 4)
2151     {
2152         /* don't let them set it with an area < 0 */
2153         RECT_SET(self->icon_geometry, data[0], data[1],
2154                  MAX(data[2],0), MAX(data[3],0));
2155     }
2156 }
2157
2158 static void client_get_session_ids(ObClient *self)
2159 {
2160     guint32 leader;
2161     gboolean got;
2162     gchar *s;
2163     gchar **ss;
2164
2165     if (!PROP_GET32(self->window, wm_client_leader, window, &leader))
2166         leader = None;
2167
2168     /* get the SM_CLIENT_ID */
2169     got = FALSE;
2170     if (leader)
2171         got = PROP_GETS(leader, sm_client_id, locale, &self->sm_client_id);
2172     if (!got)
2173         PROP_GETS(self->window, sm_client_id, locale, &self->sm_client_id);
2174
2175     /* get the WM_CLASS (name and class). make them "" if they are not
2176        provided */
2177     got = FALSE;
2178     if (leader)
2179         got = PROP_GETSS(leader, wm_class, locale, &ss);
2180     if (!got)
2181         got = PROP_GETSS(self->window, wm_class, locale, &ss);
2182
2183     if (got) {
2184         if (ss[0]) {
2185             self->name = g_strdup(ss[0]);
2186             if (ss[1])
2187                 self->class = g_strdup(ss[1]);
2188         }
2189         g_strfreev(ss);
2190     }
2191
2192     if (self->name == NULL) self->name = g_strdup("");
2193     if (self->class == NULL) self->class = g_strdup("");
2194
2195     /* get the WM_WINDOW_ROLE. make it "" if it is not provided */
2196     got = FALSE;
2197     if (leader)
2198         got = PROP_GETS(leader, wm_window_role, locale, &s);
2199     if (!got)
2200         got = PROP_GETS(self->window, wm_window_role, locale, &s);
2201
2202     if (got)
2203         self->role = s;
2204     else
2205         self->role = g_strdup("");
2206
2207     /* get the WM_COMMAND */
2208     got = FALSE;
2209
2210     if (leader)
2211         got = PROP_GETSS(leader, wm_command, locale, &ss);
2212     if (!got)
2213         got = PROP_GETSS(self->window, wm_command, locale, &ss);
2214
2215     if (got) {
2216         /* merge/mash them all together */
2217         gchar *merge = NULL;
2218         gint i;
2219
2220         for (i = 0; ss[i]; ++i) {
2221             gchar *tmp = merge;
2222             if (merge)
2223                 merge = g_strconcat(merge, ss[i], NULL);
2224             else
2225                 merge = g_strconcat(ss[i], NULL);
2226             g_free(tmp);
2227         }
2228         g_strfreev(ss);
2229
2230         self->wm_command = merge;
2231     }
2232
2233     /* get the WM_CLIENT_MACHINE */
2234     got = FALSE;
2235     if (leader)
2236         got = PROP_GETS(leader, wm_client_machine, locale, &s);
2237     if (!got)
2238         got = PROP_GETS(self->window, wm_client_machine, locale, &s);
2239
2240     if (got) {
2241         gchar localhost[128];
2242
2243         gethostname(localhost, 127);
2244         localhost[127] = '\0';
2245         if (strcmp(localhost, s) != 0)
2246             self->client_machine = s;
2247         else
2248             g_free(s);
2249     }
2250 }
2251
2252 static void client_change_wm_state(ObClient *self)
2253 {
2254     gulong state[2];
2255     glong old;
2256
2257     old = self->wmstate;
2258
2259     if (self->shaded || self->iconic ||
2260         (self->desktop != DESKTOP_ALL && self->desktop != screen_desktop))
2261     {
2262         self->wmstate = IconicState;
2263     } else
2264         self->wmstate = NormalState;
2265
2266     if (old != self->wmstate) {
2267         PROP_MSG(self->window, kde_wm_change_state,
2268                  self->wmstate, 1, 0, 0);
2269
2270         state[0] = self->wmstate;
2271         state[1] = None;
2272         PROP_SETA32(self->window, wm_state, wm_state, state, 2);
2273     }
2274 }
2275
2276 static void client_change_state(ObClient *self)
2277 {
2278     gulong netstate[11];
2279     guint num;
2280
2281     num = 0;
2282     if (self->modal)
2283         netstate[num++] = prop_atoms.net_wm_state_modal;
2284     if (self->shaded)
2285         netstate[num++] = prop_atoms.net_wm_state_shaded;
2286     if (self->iconic)
2287         netstate[num++] = prop_atoms.net_wm_state_hidden;
2288     if (self->skip_taskbar)
2289         netstate[num++] = prop_atoms.net_wm_state_skip_taskbar;
2290     if (self->skip_pager)
2291         netstate[num++] = prop_atoms.net_wm_state_skip_pager;
2292     if (self->fullscreen)
2293         netstate[num++] = prop_atoms.net_wm_state_fullscreen;
2294     if (self->max_vert)
2295         netstate[num++] = prop_atoms.net_wm_state_maximized_vert;
2296     if (self->max_horz)
2297         netstate[num++] = prop_atoms.net_wm_state_maximized_horz;
2298     if (self->above)
2299         netstate[num++] = prop_atoms.net_wm_state_above;
2300     if (self->below)
2301         netstate[num++] = prop_atoms.net_wm_state_below;
2302     if (self->demands_attention)
2303         netstate[num++] = prop_atoms.net_wm_state_demands_attention;
2304     if (self->undecorated)
2305         netstate[num++] = prop_atoms.ob_wm_state_undecorated;
2306     PROP_SETA32(self->window, net_wm_state, atom, netstate, num);
2307
2308     if (self->frame)
2309         frame_adjust_state(self->frame);
2310 }
2311
2312 ObClient *client_search_focus_tree(ObClient *self)
2313 {
2314     GSList *it;
2315     ObClient *ret;
2316
2317     for (it = self->transients; it; it = g_slist_next(it)) {
2318         if (client_focused(it->data)) return it->data;
2319         if ((ret = client_search_focus_tree(it->data))) return ret;
2320     }
2321     return NULL;
2322 }
2323
2324 ObClient *client_search_focus_tree_full(ObClient *self)
2325 {
2326     if (self->transient_for) {
2327         if (self->transient_for != OB_TRAN_GROUP) {
2328             return client_search_focus_tree_full(self->transient_for);
2329         } else {
2330             GSList *it;
2331             gboolean recursed = FALSE;
2332         
2333             for (it = self->group->members; it; it = g_slist_next(it))
2334                 if (!((ObClient*)it->data)->transient_for) {
2335                     ObClient *c;
2336                     if ((c = client_search_focus_tree_full(it->data)))
2337                         return c;
2338                     recursed = TRUE;
2339                 }
2340             if (recursed)
2341                 return NULL;
2342         }
2343     }
2344
2345     /* this function checks the whole tree, the client_search_focus_tree~
2346        does not, so we need to check this window */
2347     if (client_focused(self))
2348         return self;
2349     return client_search_focus_tree(self);
2350 }
2351
2352 static ObStackingLayer calc_layer(ObClient *self)
2353 {
2354     ObStackingLayer l;
2355
2356     if (self->type == OB_CLIENT_TYPE_DESKTOP)
2357         l = OB_STACKING_LAYER_DESKTOP;
2358     else if (self->type == OB_CLIENT_TYPE_DOCK) {
2359         if (self->below) l = OB_STACKING_LAYER_NORMAL;
2360         else l = OB_STACKING_LAYER_ABOVE;
2361     }
2362     else if ((self->fullscreen ||
2363               /* no decorations and fills the monitor = oldskool fullscreen */
2364               (self->frame != NULL &&
2365                (self->frame->size.right == 0 && self->frame->size.left == 0 &&
2366                 self->frame->size.bottom == 0 && self->frame->size.top == 0 &&
2367                 RECT_EQUAL(self->area,
2368                            *screen_physical_area_monitor
2369                            (client_monitor(self)))))) &&
2370              (client_focused(self) || client_search_focus_tree(self)))
2371         l = OB_STACKING_LAYER_FULLSCREEN;
2372     else if (self->above) l = OB_STACKING_LAYER_ABOVE;
2373     else if (self->below) l = OB_STACKING_LAYER_BELOW;
2374     else l = OB_STACKING_LAYER_NORMAL;
2375
2376     return l;
2377 }
2378
2379 static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
2380                                         ObStackingLayer min)
2381 {
2382     ObStackingLayer old, own;
2383     GSList *it;
2384
2385     old = self->layer;
2386     own = calc_layer(self);
2387     self->layer = MAX(own, min);
2388
2389     if (self->layer != old) {
2390         stacking_remove(CLIENT_AS_WINDOW(self));
2391         stacking_add_nonintrusive(CLIENT_AS_WINDOW(self));
2392     }
2393
2394     for (it = self->transients; it; it = g_slist_next(it))
2395         client_calc_layer_recursive(it->data, orig,
2396                                     self->layer);
2397 }
2398
2399 void client_calc_layer(ObClient *self)
2400 {
2401     ObClient *orig;
2402     GSList *it;
2403
2404     orig = self;
2405
2406     /* transients take on the layer of their parents */
2407     it = client_search_all_top_parents(self);
2408
2409     for (; it; it = g_slist_next(it))
2410         client_calc_layer_recursive(it->data, orig, 0);
2411 }
2412
2413 gboolean client_should_show(ObClient *self)
2414 {
2415     if (self->iconic)
2416         return FALSE;
2417     if (client_normal(self) && screen_showing_desktop)
2418         return FALSE;
2419     if (self->desktop == screen_desktop || self->desktop == DESKTOP_ALL)
2420         return TRUE;
2421     
2422     return FALSE;
2423 }
2424
2425 void client_show(ObClient *self)
2426 {
2427
2428     if (client_should_show(self)) {
2429         frame_show(self->frame);
2430     }
2431
2432     /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2433        needs to be in IconicState. This includes when it is on another
2434        desktop!
2435     */
2436     client_change_wm_state(self);
2437 }
2438
2439 void client_hide(ObClient *self)
2440 {
2441     if (!client_should_show(self)) {
2442         frame_hide(self->frame);
2443
2444         client_call_notifies(self, client_hide_notifies);
2445     }
2446
2447     /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2448        needs to be in IconicState. This includes when it is on another
2449        desktop!
2450     */
2451     client_change_wm_state(self);
2452 }
2453
2454 void client_showhide(ObClient *self)
2455 {
2456
2457     if (client_should_show(self)) {
2458         frame_show(self->frame);
2459     }
2460     else {
2461         frame_hide(self->frame);
2462
2463         client_call_notifies(self, client_hide_notifies);
2464     }
2465
2466     /* According to the ICCCM (sec 4.1.3.1) when a window is not visible, it
2467        needs to be in IconicState. This includes when it is on another
2468        desktop!
2469     */
2470     client_change_wm_state(self);
2471 }
2472
2473 gboolean client_normal(ObClient *self) {
2474     return ! (self->type == OB_CLIENT_TYPE_DESKTOP ||
2475               self->type == OB_CLIENT_TYPE_DOCK ||
2476               self->type == OB_CLIENT_TYPE_SPLASH);
2477 }
2478
2479 gboolean client_helper(ObClient *self)
2480 {
2481     return (self->type == OB_CLIENT_TYPE_UTILITY ||
2482             self->type == OB_CLIENT_TYPE_MENU ||
2483             self->type == OB_CLIENT_TYPE_TOOLBAR);
2484 }
2485
2486 gboolean client_mouse_focusable(ObClient *self)
2487 {
2488     return !(self->type == OB_CLIENT_TYPE_MENU ||
2489              self->type == OB_CLIENT_TYPE_TOOLBAR ||
2490              self->type == OB_CLIENT_TYPE_SPLASH ||
2491              self->type == OB_CLIENT_TYPE_DOCK);
2492 }
2493
2494 gboolean client_enter_focusable(ObClient *self)
2495 {
2496     /* you can focus desktops but it shouldn't on enter */
2497     return (client_mouse_focusable(self) &&
2498             self->type != OB_CLIENT_TYPE_DESKTOP);
2499 }
2500
2501
2502 static void client_apply_startup_state(ObClient *self)
2503 {
2504     /* set the desktop hint, to make sure that it always exists */
2505     PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop);
2506
2507     /* these are in a carefully crafted order.. */
2508
2509     if (self->iconic) {
2510         self->iconic = FALSE;
2511         client_iconify(self, TRUE, FALSE, TRUE);
2512     }
2513     if (self->fullscreen) {
2514         self->fullscreen = FALSE;
2515         client_fullscreen(self, TRUE);
2516     }
2517     if (self->undecorated) {
2518         self->undecorated = FALSE;
2519         client_set_undecorated(self, TRUE);
2520     }
2521     if (self->shaded) {
2522         self->shaded = FALSE;
2523         client_shade(self, TRUE);
2524     }
2525     if (self->demands_attention) {
2526         self->demands_attention = FALSE;
2527         client_hilite(self, TRUE);
2528     }
2529   
2530     if (self->max_vert && self->max_horz) {
2531         self->max_vert = self->max_horz = FALSE;
2532         client_maximize(self, TRUE, 0);
2533     } else if (self->max_vert) {
2534         self->max_vert = FALSE;
2535         client_maximize(self, TRUE, 2);
2536     } else if (self->max_horz) {
2537         self->max_horz = FALSE;
2538         client_maximize(self, TRUE, 1);
2539     }
2540
2541     /* nothing to do for the other states:
2542        skip_taskbar
2543        skip_pager
2544        modal
2545        above
2546        below
2547     */
2548 }
2549
2550 void client_convert_gravity(ObClient *self, gint gravity, gint *x, gint *y,
2551                             gint w, gint h)
2552 {
2553     gint oldg = self->gravity;
2554
2555     /* get the frame's position from the requested stuff */
2556     self->gravity = gravity;
2557     frame_client_gravity(self->frame, x, y, w, h);
2558     self->gravity = oldg;
2559
2560     /* get the client's position in its true gravity from that */
2561     frame_frame_gravity(self->frame, x, y, w, h);
2562 }
2563
2564 void client_try_configure(ObClient *self, gint *x, gint *y, gint *w, gint *h,
2565                           gint *logicalw, gint *logicalh,
2566                           gboolean user)
2567 {
2568     Rect desired_area = {*x, *y, *w, *h};
2569
2570     /* make the frame recalculate its dimentions n shit without changing
2571        anything visible for real, this way the constraints below can work with
2572        the updated frame dimensions. */
2573     frame_adjust_area(self->frame, TRUE, TRUE, TRUE);
2574
2575     /* work within the prefered sizes given by the window */
2576     if (!(*w == self->area.width && *h == self->area.height)) {
2577         gint basew, baseh, minw, minh;
2578
2579         /* base size is substituted with min size if not specified */
2580         if (self->base_size.width || self->base_size.height) {
2581             basew = self->base_size.width;
2582             baseh = self->base_size.height;
2583         } else {
2584             basew = self->min_size.width;
2585             baseh = self->min_size.height;
2586         }
2587         /* min size is substituted with base size if not specified */
2588         if (self->min_size.width || self->min_size.height) {
2589             minw = self->min_size.width;
2590             minh = self->min_size.height;
2591         } else {
2592             minw = self->base_size.width;
2593             minh = self->base_size.height;
2594         }
2595
2596         /* if this is a user-requested resize, then check against min/max
2597            sizes */
2598
2599         /* smaller than min size or bigger than max size? */
2600         if (*w > self->max_size.width) *w = self->max_size.width;
2601         if (*w < minw) *w = minw;
2602         if (*h > self->max_size.height) *h = self->max_size.height;
2603         if (*h < minh) *h = minh;
2604
2605         *w -= basew;
2606         *h -= baseh;
2607
2608         /* keep to the increments */
2609         *w /= self->size_inc.width;
2610         *h /= self->size_inc.height;
2611
2612         /* you cannot resize to nothing */
2613         if (basew + *w < 1) *w = 1 - basew;
2614         if (baseh + *h < 1) *h = 1 - baseh;
2615   
2616         /* save the logical size */
2617         *logicalw = self->size_inc.width > 1 ? *w : *w + basew;
2618         *logicalh = self->size_inc.height > 1 ? *h : *h + baseh;
2619
2620         *w *= self->size_inc.width;
2621         *h *= self->size_inc.height;
2622
2623         *w += basew;
2624         *h += baseh;
2625
2626         /* adjust the height to match the width for the aspect ratios.
2627            for this, min size is not substituted for base size ever. */
2628         *w -= self->base_size.width;
2629         *h -= self->base_size.height;
2630
2631         if (!self->fullscreen) {
2632             if (self->min_ratio)
2633                 if (*h * self->min_ratio > *w) {
2634                     *h = (gint)(*w / self->min_ratio);
2635
2636                     /* you cannot resize to nothing */
2637                     if (*h < 1) {
2638                         *h = 1;
2639                         *w = (gint)(*h * self->min_ratio);
2640                     }
2641                 }
2642             if (self->max_ratio)
2643                 if (*h * self->max_ratio < *w) {
2644                     *h = (gint)(*w / self->max_ratio);
2645
2646                     /* you cannot resize to nothing */
2647                     if (*h < 1) {
2648                         *h = 1;
2649                         *w = (gint)(*h * self->min_ratio);
2650                     }
2651                 }
2652         }
2653
2654         *w += self->base_size.width;
2655         *h += self->base_size.height;
2656     }
2657
2658     /* gets the frame's position */
2659     frame_client_gravity(self->frame, x, y, *w, *h);
2660
2661     /* these positions are frame positions, not client positions */
2662
2663     /* set the size and position if fullscreen */
2664     if (self->fullscreen) {
2665         Rect *a;
2666         guint i;
2667
2668         i = screen_find_monitor(&desired_area);
2669         a = screen_physical_area_monitor(i);
2670
2671         *x = a->x;
2672         *y = a->y;
2673         *w = a->width;
2674         *h = a->height;
2675
2676         user = FALSE; /* ignore if the client can't be moved/resized when it
2677                          is entering fullscreen */
2678     } else if (self->max_horz || self->max_vert) {
2679         Rect *a;
2680         guint i;
2681
2682         i = screen_find_monitor(&desired_area);
2683         a = screen_area_monitor(self->desktop, i);
2684
2685         /* set the size and position if maximized */
2686         if (self->max_horz) {
2687             *x = a->x;
2688             *w = a->width - self->frame->size.left - self->frame->size.right;
2689         }
2690         if (self->max_vert) {
2691             *y = a->y;
2692             *h = a->height - self->frame->size.top - self->frame->size.bottom;
2693         }
2694
2695         /* maximizing is not allowed if the user can't move+resize the window
2696          */
2697     }
2698
2699     /* gets the client's position */
2700     frame_frame_gravity(self->frame, x, y, *w, *h);
2701
2702     /* these override the above states! if you cant move you can't move! */
2703     if (user) {
2704         if (!(self->functions & OB_CLIENT_FUNC_MOVE)) {
2705             *x = self->area.x;
2706             *y = self->area.y;
2707         }
2708         if (!(self->functions & OB_CLIENT_FUNC_RESIZE)) {
2709             *w = self->area.width;
2710             *h = self->area.height;
2711         }
2712     }
2713
2714     g_assert(*w > 0);
2715     g_assert(*h > 0);
2716 }
2717
2718
2719 void client_configure_full(ObClient *self, gint x, gint y, gint w, gint h,
2720                            gboolean user, gboolean final)
2721 {
2722     gint oldw, oldh;
2723     gboolean send_resize_client;
2724     gboolean moved = FALSE, resized = FALSE;
2725     guint fdecor = self->frame->decorations;
2726     gboolean fhorz = self->frame->max_horz;
2727     gint logicalw, logicalh;
2728
2729     /* find the new x, y, width, and height (and logical size) */
2730     client_try_configure(self, &x, &y, &w, &h, &logicalw, &logicalh, user);
2731
2732     /* set the logical size if things changed */
2733     if (!(w == self->area.width && h == self->area.height))
2734         SIZE_SET(self->logical_size, logicalw, logicalh);
2735
2736     /* figure out if we moved or resized or what */
2737     moved = x != self->area.x || y != self->area.y;
2738     resized = w != self->area.width || h != self->area.height;
2739
2740     oldw = self->area.width;
2741     oldh = self->area.height;
2742     RECT_SET(self->area, x, y, w, h);
2743
2744     /* for app-requested resizes, always resize if 'resized' is true.
2745        for user-requested ones, only resize if final is true, or when
2746        resizing in redraw mode */
2747     send_resize_client = ((!user && resized) ||
2748                           (user && (final ||
2749                                     (resized && config_resize_redraw))));
2750
2751     /* if the client is enlarging, then resize the client before the frame */
2752     if (send_resize_client && (w > oldw || h > oldh)) {
2753         XResizeWindow(ob_display, self->window,
2754                       MAX(w, oldw), MAX(h, oldh));
2755         /* resize the plate to show the client padding color underneath */
2756         frame_adjust_client_area(self->frame);
2757     }
2758
2759     /* find the frame's dimensions and move/resize it */
2760     if (self->decorations != fdecor || self->max_horz != fhorz)
2761         moved = resized = TRUE;
2762     if (moved || resized)
2763         frame_adjust_area(self->frame, moved, resized, FALSE);
2764
2765     if ((!user || (user && final)) && !resized)
2766     {
2767         XEvent event;
2768
2769         POINT_SET(self->root_pos,
2770                   self->frame->area.x + self->frame->size.left -
2771                   self->border_width,
2772                   self->frame->area.y + self->frame->size.top -
2773                   self->border_width);
2774
2775         event.type = ConfigureNotify;
2776         event.xconfigure.display = ob_display;
2777         event.xconfigure.event = self->window;
2778         event.xconfigure.window = self->window;
2779
2780         ob_debug("Sending ConfigureNotify to %s for %d,%d %dx%d\n",
2781                  self->title, self->root_pos.x, self->root_pos.y, w, h);
2782
2783         /* root window real coords */
2784         event.xconfigure.x = self->root_pos.x;
2785         event.xconfigure.y = self->root_pos.y;
2786         event.xconfigure.width = w;
2787         event.xconfigure.height = h;
2788         event.xconfigure.border_width = 0;
2789         event.xconfigure.above = self->frame->plate;
2790         event.xconfigure.override_redirect = FALSE;
2791         XSendEvent(event.xconfigure.display, event.xconfigure.window,
2792                    FALSE, StructureNotifyMask, &event);
2793     }
2794
2795     /* if the client is shrinking, then resize the frame before the client */
2796     if (send_resize_client && (w <= oldw || h <= oldh)) {
2797         /* resize the plate to show the client padding color underneath */
2798         frame_adjust_client_area(self->frame);
2799
2800         if (send_resize_client)
2801             XResizeWindow(ob_display, self->window, w, h);
2802     }
2803
2804     XFlush(ob_display);
2805 }
2806
2807 void client_fullscreen(ObClient *self, gboolean fs)
2808 {
2809     gint x, y, w, h;
2810
2811     if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) || /* can't */
2812         self->fullscreen == fs) return;                   /* already done */
2813
2814     self->fullscreen = fs;
2815     client_change_state(self); /* change the state hints on the client */
2816     client_calc_layer(self);   /* and adjust out layer/stacking */
2817
2818     if (fs) {
2819         self->pre_fullscreen_area = self->area;
2820         /* if the window is maximized, its area isn't all that meaningful.
2821            save it's premax area instead. */
2822         if (self->max_horz) {
2823             self->pre_fullscreen_area.x = self->pre_max_area.x;
2824             self->pre_fullscreen_area.width = self->pre_max_area.width;
2825         }
2826         if (self->max_vert) {
2827             self->pre_fullscreen_area.y = self->pre_max_area.y;
2828             self->pre_fullscreen_area.height = self->pre_max_area.height;
2829         }
2830
2831         /* these will help configure_full figure out where to fullscreen
2832            the window */
2833         x = self->area.x;
2834         y = self->area.y;
2835         w = self->area.width;
2836         h = self->area.height;
2837     } else {
2838         g_assert(self->pre_fullscreen_area.width > 0 &&
2839                  self->pre_fullscreen_area.height > 0);
2840
2841         x = self->pre_fullscreen_area.x;
2842         y = self->pre_fullscreen_area.y;
2843         w = self->pre_fullscreen_area.width;
2844         h = self->pre_fullscreen_area.height;
2845         RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0);
2846     }
2847
2848     client_setup_decor_and_functions(self);
2849
2850     client_move_resize(self, x, y, w, h);
2851
2852     /* try focus us when we go into fullscreen mode */
2853     client_focus(self);
2854 }
2855
2856 static void client_iconify_recursive(ObClient *self,
2857                                      gboolean iconic, gboolean curdesk,
2858                                      gboolean hide_animation)
2859 {
2860     GSList *it;
2861     gboolean changed = FALSE;
2862
2863
2864     if (self->iconic != iconic) {
2865         ob_debug("%sconifying window: 0x%lx\n", (iconic ? "I" : "Uni"),
2866                  self->window);
2867
2868         if (iconic) {
2869             /* don't let non-normal windows iconify along with their parents
2870                or whatever */
2871             if (client_normal(self)) {
2872                 self->iconic = iconic;
2873
2874                 /* update the focus lists.. iconic windows go to the bottom of
2875                    the list, put the new iconic window at the 'top of the
2876                    bottom'. */
2877                 focus_order_to_top(self);
2878
2879                 changed = TRUE;
2880             }
2881         } else {
2882             self->iconic = iconic;
2883
2884             if (curdesk && self->desktop != screen_desktop &&
2885                 self->desktop != DESKTOP_ALL)
2886                 client_set_desktop(self, screen_desktop, FALSE);
2887
2888             /* this puts it after the current focused window */
2889             focus_order_remove(self);
2890             focus_order_add_new(self);
2891
2892             changed = TRUE;
2893         }
2894     }
2895
2896     if (changed) {
2897         client_change_state(self);
2898         if (config_animate_iconify && !hide_animation)
2899             frame_begin_iconify_animation(self->frame, iconic);
2900         /* do this after starting the animation so it doesn't flash */
2901         client_showhide(self);
2902     }
2903
2904     /* iconify all direct transients, and deiconify all transients
2905        (non-direct too) */
2906     for (it = self->transients; it; it = g_slist_next(it))
2907         if (it->data != self)
2908             if (client_is_direct_child(self, it->data) || !iconic)
2909                 client_iconify_recursive(it->data, iconic, curdesk,
2910                                          hide_animation);
2911 }
2912
2913 void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk,
2914                     gboolean hide_animation)
2915 {
2916     if (self->functions & OB_CLIENT_FUNC_ICONIFY || !iconic) {
2917         /* move up the transient chain as far as possible first */
2918         self = client_search_top_normal_parent(self);
2919         client_iconify_recursive(self, iconic, curdesk, hide_animation);
2920     }
2921 }
2922
2923 void client_maximize(ObClient *self, gboolean max, gint dir)
2924 {
2925     gint x, y, w, h;
2926      
2927     g_assert(dir == 0 || dir == 1 || dir == 2);
2928     if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE)) return; /* can't */
2929
2930     /* check if already done */
2931     if (max) {
2932         if (dir == 0 && self->max_horz && self->max_vert) return;
2933         if (dir == 1 && self->max_horz) return;
2934         if (dir == 2 && self->max_vert) return;
2935     } else {
2936         if (dir == 0 && !self->max_horz && !self->max_vert) return;
2937         if (dir == 1 && !self->max_horz) return;
2938         if (dir == 2 && !self->max_vert) return;
2939     }
2940
2941     /* these will help configure_full figure out which screen to fill with
2942        the window */
2943     x = self->area.x;
2944     y = self->area.y;
2945     w = self->area.width;
2946     h = self->area.height;
2947
2948     if (max) {
2949         if ((dir == 0 || dir == 1) && !self->max_horz) { /* horz */
2950             RECT_SET(self->pre_max_area,
2951                      self->area.x, self->pre_max_area.y,
2952                      self->area.width, self->pre_max_area.height);
2953         }
2954         if ((dir == 0 || dir == 2) && !self->max_vert) { /* vert */
2955             RECT_SET(self->pre_max_area,
2956                      self->pre_max_area.x, self->area.y,
2957                      self->pre_max_area.width, self->area.height);
2958         }
2959     } else {
2960         if ((dir == 0 || dir == 1) && self->max_horz) { /* horz */
2961             g_assert(self->pre_max_area.width > 0);
2962
2963             x = self->pre_max_area.x;
2964             w = self->pre_max_area.width;
2965
2966             RECT_SET(self->pre_max_area, 0, self->pre_max_area.y,
2967                      0, self->pre_max_area.height);
2968         }
2969         if ((dir == 0 || dir == 2) && self->max_vert) { /* vert */
2970             g_assert(self->pre_max_area.height > 0);
2971
2972             y = self->pre_max_area.y;
2973             h = self->pre_max_area.height;
2974
2975             RECT_SET(self->pre_max_area, self->pre_max_area.x, 0,
2976                      self->pre_max_area.width, 0);
2977         }
2978     }
2979
2980     if (dir == 0 || dir == 1) /* horz */
2981         self->max_horz = max;
2982     if (dir == 0 || dir == 2) /* vert */
2983         self->max_vert = max;
2984
2985     client_change_state(self); /* change the state hints on the client */
2986
2987     client_setup_decor_and_functions(self);
2988
2989     client_move_resize(self, x, y, w, h);
2990 }
2991
2992 void client_shade(ObClient *self, gboolean shade)
2993 {
2994     if ((!(self->functions & OB_CLIENT_FUNC_SHADE) &&
2995          shade) ||                         /* can't shade */
2996         self->shaded == shade) return;     /* already done */
2997
2998     self->shaded = shade;
2999     client_change_state(self);
3000     client_change_wm_state(self); /* the window is being hidden/shown */
3001     /* resize the frame to just the titlebar */
3002     frame_adjust_area(self->frame, FALSE, FALSE, FALSE);
3003 }
3004
3005 void client_close(ObClient *self)
3006 {
3007     XEvent ce;
3008
3009     if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return;
3010
3011     /* in the case that the client provides no means to requesting that it
3012        close, we just kill it */
3013     if (!self->delete_window)
3014         client_kill(self);
3015     
3016     /*
3017       XXX: itd be cool to do timeouts and shit here for killing the client's
3018       process off
3019       like... if the window is around after 5 seconds, then the close button
3020       turns a nice red, and if this function is called again, the client is
3021       explicitly killed.
3022     */
3023
3024     ce.xclient.type = ClientMessage;
3025     ce.xclient.message_type =  prop_atoms.wm_protocols;
3026     ce.xclient.display = ob_display;
3027     ce.xclient.window = self->window;
3028     ce.xclient.format = 32;
3029     ce.xclient.data.l[0] = prop_atoms.wm_delete_window;
3030     ce.xclient.data.l[1] = event_curtime;
3031     ce.xclient.data.l[2] = 0l;
3032     ce.xclient.data.l[3] = 0l;
3033     ce.xclient.data.l[4] = 0l;
3034     XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
3035 }
3036
3037 void client_kill(ObClient *self)
3038 {
3039     XKillClient(ob_display, self->window);
3040 }
3041
3042 void client_hilite(ObClient *self, gboolean hilite)
3043 {
3044     if (self->demands_attention == hilite)
3045         return; /* no change */
3046
3047     /* don't allow focused windows to hilite */
3048     self->demands_attention = hilite && !client_focused(self);
3049     if (self->frame != NULL) { /* if we're mapping, just set the state */
3050         if (self->demands_attention)
3051             frame_flash_start(self->frame);
3052         else
3053             frame_flash_stop(self->frame);
3054         client_change_state(self);
3055     }
3056 }
3057
3058 void client_set_desktop_recursive(ObClient *self,
3059                                   guint target,
3060                                   gboolean donthide)
3061 {
3062     guint old;
3063     GSList *it;
3064
3065     if (target != self->desktop) {
3066
3067         ob_debug("Setting desktop %u\n", target+1);
3068
3069         g_assert(target < screen_num_desktops || target == DESKTOP_ALL);
3070
3071         old = self->desktop;
3072         self->desktop = target;
3073         PROP_SET32(self->window, net_wm_desktop, cardinal, target);
3074         /* the frame can display the current desktop state */
3075         frame_adjust_state(self->frame);
3076         /* 'move' the window to the new desktop */
3077         if (!donthide)
3078             client_showhide(self);
3079         /* raise if it was not already on the desktop */
3080         if (old != DESKTOP_ALL)
3081             stacking_raise(CLIENT_AS_WINDOW(self));
3082         if (STRUT_EXISTS(self->strut))
3083             screen_update_areas();
3084     }
3085
3086     /* move all transients */
3087     for (it = self->transients; it; it = g_slist_next(it))
3088         if (it->data != self)
3089             if (client_is_direct_child(self, it->data))
3090                 client_set_desktop_recursive(it->data, target, donthide);
3091 }
3092
3093 void client_set_desktop(ObClient *self, guint target,
3094                         gboolean donthide)
3095 {
3096     self = client_search_top_normal_parent(self);
3097     client_set_desktop_recursive(self, target, donthide);
3098 }
3099
3100 gboolean client_is_direct_child(ObClient *parent, ObClient *child)
3101 {
3102     while (child != parent &&
3103            child->transient_for && child->transient_for != OB_TRAN_GROUP)
3104         child = child->transient_for;
3105     return child == parent;
3106 }
3107
3108 ObClient *client_search_modal_child(ObClient *self)
3109 {
3110     GSList *it;
3111     ObClient *ret;
3112   
3113     for (it = self->transients; it; it = g_slist_next(it)) {
3114         ObClient *c = it->data;
3115         if ((ret = client_search_modal_child(c))) return ret;
3116         if (c->modal) return c;
3117     }
3118     return NULL;
3119 }
3120
3121 gboolean client_validate(ObClient *self)
3122 {
3123     XEvent e; 
3124
3125     XSync(ob_display, FALSE); /* get all events on the server */
3126
3127     if (XCheckTypedWindowEvent(ob_display, self->window, DestroyNotify, &e) ||
3128         XCheckTypedWindowEvent(ob_display, self->window, UnmapNotify, &e)) {
3129         XPutBackEvent(ob_display, &e);
3130         return FALSE;
3131     }
3132
3133     return TRUE;
3134 }
3135
3136 void client_set_wm_state(ObClient *self, glong state)
3137 {
3138     if (state == self->wmstate) return; /* no change */
3139   
3140     switch (state) {
3141     case IconicState:
3142         client_iconify(self, TRUE, TRUE, FALSE);
3143         break;
3144     case NormalState:
3145         client_iconify(self, FALSE, TRUE, FALSE);
3146         break;
3147     }
3148 }
3149
3150 void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
3151 {
3152     gboolean shaded = self->shaded;
3153     gboolean fullscreen = self->fullscreen;
3154     gboolean undecorated = self->undecorated;
3155     gboolean max_horz = self->max_horz;
3156     gboolean max_vert = self->max_vert;
3157     gboolean modal = self->modal;
3158     gboolean iconic = self->iconic;
3159     gboolean demands_attention = self->demands_attention;
3160     gboolean above = self->above;
3161     gboolean below = self->below;
3162     gint i;
3163
3164     if (!(action == prop_atoms.net_wm_state_add ||
3165           action == prop_atoms.net_wm_state_remove ||
3166           action == prop_atoms.net_wm_state_toggle))
3167         /* an invalid action was passed to the client message, ignore it */
3168         return; 
3169
3170     for (i = 0; i < 2; ++i) {
3171         Atom state = i == 0 ? data1 : data2;
3172     
3173         if (!state) continue;
3174
3175         /* if toggling, then pick whether we're adding or removing */
3176         if (action == prop_atoms.net_wm_state_toggle) {
3177             if (state == prop_atoms.net_wm_state_modal)
3178                 action = modal ? prop_atoms.net_wm_state_remove :
3179                     prop_atoms.net_wm_state_add;
3180             else if (state == prop_atoms.net_wm_state_maximized_vert)
3181                 action = self->max_vert ? prop_atoms.net_wm_state_remove :
3182                     prop_atoms.net_wm_state_add;
3183             else if (state == prop_atoms.net_wm_state_maximized_horz)
3184                 action = self->max_horz ? prop_atoms.net_wm_state_remove :
3185                     prop_atoms.net_wm_state_add;
3186             else if (state == prop_atoms.net_wm_state_shaded)
3187                 action = shaded ? prop_atoms.net_wm_state_remove :
3188                     prop_atoms.net_wm_state_add;
3189             else if (state == prop_atoms.net_wm_state_skip_taskbar)
3190                 action = self->skip_taskbar ?
3191                     prop_atoms.net_wm_state_remove :
3192                     prop_atoms.net_wm_state_add;
3193             else if (state == prop_atoms.net_wm_state_skip_pager)
3194                 action = self->skip_pager ?
3195                     prop_atoms.net_wm_state_remove :
3196                     prop_atoms.net_wm_state_add;
3197             else if (state == prop_atoms.net_wm_state_hidden)
3198                 action = self->iconic ?
3199                     prop_atoms.net_wm_state_remove :
3200                     prop_atoms.net_wm_state_add;
3201             else if (state == prop_atoms.net_wm_state_fullscreen)
3202                 action = fullscreen ?
3203                     prop_atoms.net_wm_state_remove :
3204                     prop_atoms.net_wm_state_add;
3205             else if (state == prop_atoms.net_wm_state_above)
3206                 action = self->above ? prop_atoms.net_wm_state_remove :
3207                     prop_atoms.net_wm_state_add;
3208             else if (state == prop_atoms.net_wm_state_below)
3209                 action = self->below ? prop_atoms.net_wm_state_remove :
3210                     prop_atoms.net_wm_state_add;
3211             else if (state == prop_atoms.net_wm_state_demands_attention)
3212                 action = self->demands_attention ?
3213                     prop_atoms.net_wm_state_remove :
3214                     prop_atoms.net_wm_state_add;
3215             else if (state == prop_atoms.ob_wm_state_undecorated)
3216                 action = undecorated ? prop_atoms.net_wm_state_remove :
3217                     prop_atoms.net_wm_state_add;
3218         }
3219     
3220         if (action == prop_atoms.net_wm_state_add) {
3221             if (state == prop_atoms.net_wm_state_modal) {
3222                 modal = TRUE;
3223             } else if (state == prop_atoms.net_wm_state_maximized_vert) {
3224                 max_vert = TRUE;
3225             } else if (state == prop_atoms.net_wm_state_maximized_horz) {
3226                 max_horz = TRUE;
3227             } else if (state == prop_atoms.net_wm_state_shaded) {
3228                 shaded = TRUE;
3229             } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
3230                 self->skip_taskbar = TRUE;
3231             } else if (state == prop_atoms.net_wm_state_skip_pager) {
3232                 self->skip_pager = TRUE;
3233             } else if (state == prop_atoms.net_wm_state_hidden) {
3234                 iconic = TRUE;
3235             } else if (state == prop_atoms.net_wm_state_fullscreen) {
3236                 fullscreen = TRUE;
3237             } else if (state == prop_atoms.net_wm_state_above) {
3238                 above = TRUE;
3239                 below = FALSE;
3240             } else if (state == prop_atoms.net_wm_state_below) {
3241                 above = FALSE;
3242                 below = TRUE;
3243             } else if (state == prop_atoms.net_wm_state_demands_attention) {
3244                 demands_attention = TRUE;
3245             } else if (state == prop_atoms.ob_wm_state_undecorated) {
3246                 undecorated = TRUE;
3247             }
3248
3249         } else { /* action == prop_atoms.net_wm_state_remove */
3250             if (state == prop_atoms.net_wm_state_modal) {
3251                 modal = FALSE;
3252             } else if (state == prop_atoms.net_wm_state_maximized_vert) {
3253                 max_vert = FALSE;
3254             } else if (state == prop_atoms.net_wm_state_maximized_horz) {
3255                 max_horz = FALSE;
3256             } else if (state == prop_atoms.net_wm_state_shaded) {
3257                 shaded = FALSE;
3258             } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
3259                 self->skip_taskbar = FALSE;
3260             } else if (state == prop_atoms.net_wm_state_skip_pager) {
3261                 self->skip_pager = FALSE;
3262             } else if (state == prop_atoms.net_wm_state_hidden) {
3263                 iconic = FALSE;
3264             } else if (state == prop_atoms.net_wm_state_fullscreen) {
3265                 fullscreen = FALSE;
3266             } else if (state == prop_atoms.net_wm_state_above) {
3267                 above = FALSE;
3268             } else if (state == prop_atoms.net_wm_state_below) {
3269                 below = FALSE;
3270             } else if (state == prop_atoms.net_wm_state_demands_attention) {
3271                 demands_attention = FALSE;
3272             } else if (state == prop_atoms.ob_wm_state_undecorated) {
3273                 undecorated = FALSE;
3274             }
3275         }
3276     }
3277     if (max_horz != self->max_horz || max_vert != self->max_vert) {
3278         if (max_horz != self->max_horz && max_vert != self->max_vert) {
3279             /* toggling both */
3280             if (max_horz == max_vert) { /* both going the same way */
3281                 client_maximize(self, max_horz, 0);
3282             } else {
3283                 client_maximize(self, max_horz, 1);
3284                 client_maximize(self, max_vert, 2);
3285             }
3286         } else {
3287             /* toggling one */
3288             if (max_horz != self->max_horz)
3289                 client_maximize(self, max_horz, 1);
3290             else
3291                 client_maximize(self, max_vert, 2);
3292         }
3293     }
3294     /* change fullscreen state before shading, as it will affect if the window
3295        can shade or not */
3296     if (fullscreen != self->fullscreen)
3297         client_fullscreen(self, fullscreen);
3298     if (shaded != self->shaded)
3299         client_shade(self, shaded);
3300     if (undecorated != self->undecorated)
3301         client_set_undecorated(self, undecorated);
3302     if (modal != self->modal) {
3303         self->modal = modal;
3304         /* when a window changes modality, then its stacking order with its
3305            transients needs to change */
3306         stacking_raise(CLIENT_AS_WINDOW(self));
3307     }
3308     if (iconic != self->iconic)
3309         client_iconify(self, iconic, FALSE, FALSE);
3310
3311     if (demands_attention != self->demands_attention)
3312         client_hilite(self, demands_attention);
3313
3314     if (above != self->above || below != self->below) {
3315         self->above = above;
3316         self->below = below;
3317         client_calc_layer(self);
3318     }
3319
3320     client_change_state(self); /* change the hint to reflect these changes */
3321 }
3322
3323 ObClient *client_focus_target(ObClient *self)
3324 {
3325     ObClient *child = NULL;
3326
3327     child = client_search_modal_child(self);
3328     if (child) return child;
3329     return self;
3330 }
3331
3332 gboolean client_can_focus(ObClient *self)
3333 {
3334     XEvent ev;
3335
3336     /* choose the correct target */
3337     self = client_focus_target(self);
3338
3339     if (!self->frame->visible)
3340         return FALSE;
3341
3342     if (!(self->can_focus || self->focus_notify))
3343         return FALSE;
3344
3345     /* do a check to see if the window has already been unmapped or destroyed
3346        do this intelligently while watching out for unmaps we've generated
3347        (ignore_unmaps > 0) */
3348     if (XCheckTypedWindowEvent(ob_display, self->window,
3349                                DestroyNotify, &ev)) {
3350         XPutBackEvent(ob_display, &ev);
3351         return FALSE;
3352     }
3353     while (XCheckTypedWindowEvent(ob_display, self->window,
3354                                   UnmapNotify, &ev)) {
3355         if (self->ignore_unmaps) {
3356             self->ignore_unmaps--;
3357         } else {
3358             XPutBackEvent(ob_display, &ev);
3359             return FALSE;
3360         }
3361     }
3362
3363     return TRUE;
3364 }
3365
3366 gboolean client_focus(ObClient *self)
3367 {
3368     /* choose the correct target */
3369     self = client_focus_target(self);
3370
3371     if (!client_can_focus(self)) {
3372         if (!self->frame->visible) {
3373             /* update the focus lists */
3374             focus_order_to_top(self);
3375         }
3376         return FALSE;
3377     }
3378
3379     ob_debug_type(OB_DEBUG_FOCUS,
3380                   "Focusing client \"%s\" at time %u\n",
3381                   self->title, event_curtime);
3382
3383     /* if there is a grab going on, then we need to cancel it. if we move
3384        focus during the grab, applications will get NotifyWhileGrabbed events
3385        and ignore them !
3386
3387        actions should not rely on being able to move focus during an
3388        interactive grab.
3389     */
3390     if (keyboard_interactively_grabbed())
3391         keyboard_interactive_cancel();
3392
3393     if (self->can_focus) {
3394         /* This can cause a BadMatch error with CurrentTime, or if an app
3395            passed in a bad time for _NET_WM_ACTIVE_WINDOW. */
3396         xerror_set_ignore(TRUE);
3397         XSetInputFocus(ob_display, self->window, RevertToPointerRoot,
3398                        event_curtime);
3399         xerror_set_ignore(FALSE);
3400     }
3401
3402     if (self->focus_notify) {
3403         XEvent ce;
3404         ce.xclient.type = ClientMessage;
3405         ce.xclient.message_type = prop_atoms.wm_protocols;
3406         ce.xclient.display = ob_display;
3407         ce.xclient.window = self->window;
3408         ce.xclient.format = 32;
3409         ce.xclient.data.l[0] = prop_atoms.wm_take_focus;
3410         ce.xclient.data.l[1] = event_curtime;
3411         ce.xclient.data.l[2] = 0l;
3412         ce.xclient.data.l[3] = 0l;
3413         ce.xclient.data.l[4] = 0l;
3414         XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
3415     }
3416
3417 #ifdef DEBUG_FOCUS
3418     ob_debug("%sively focusing %lx at %d\n",
3419              (self->can_focus ? "act" : "pass"),
3420              self->window, (gint) event_curtime);
3421 #endif
3422
3423     /* Cause the FocusIn to come back to us. Important for desktop switches,
3424        since otherwise we'll have no FocusIn on the queue and send it off to
3425        the focus_backup. */
3426     XSync(ob_display, FALSE);
3427     return TRUE;
3428 }
3429
3430 /*! Present the client to the user.
3431   @param raise If the client should be raised or not. You should only set
3432                raise to false if you don't care if the window is completely
3433                hidden.
3434 */
3435 static void client_present(ObClient *self, gboolean here, gboolean raise)
3436 {
3437     /* if using focus_delay, stop the timer now so that focus doesn't
3438        go moving on us */
3439     event_halt_focus_delay();
3440
3441     if (client_normal(self) && screen_showing_desktop)
3442         screen_show_desktop(FALSE, self);
3443     if (self->iconic)
3444         client_iconify(self, FALSE, here, FALSE);
3445     if (self->desktop != DESKTOP_ALL &&
3446         self->desktop != screen_desktop)
3447     {
3448         if (here)
3449             client_set_desktop(self, screen_desktop, FALSE);
3450         else
3451             screen_set_desktop(self->desktop, FALSE);
3452     } else if (!self->frame->visible)
3453         /* if its not visible for other reasons, then don't mess
3454            with it */
3455         return;
3456     if (self->shaded)
3457         client_shade(self, FALSE);
3458     if (raise)
3459         stacking_raise(CLIENT_AS_WINDOW(self));
3460
3461     client_focus(self);
3462 }
3463
3464 void client_activate(ObClient *self, gboolean here, gboolean user)
3465 {
3466     guint32 last_time = focus_client ? focus_client->user_time : CurrentTime;
3467     gboolean allow = FALSE;
3468
3469     /* if the request came from the user, or if nothing is focused, then grant
3470        the request.
3471        if the currently focused app doesn't set a user_time, then it can't
3472        benefit from any focus stealing prevention.
3473     */
3474     if (user || !focus_client || !last_time)
3475         allow = TRUE;
3476     /* otherwise, if they didn't give a time stamp or if it is too old, they
3477        don't get focus */
3478     else
3479         allow = event_curtime && event_time_after(event_curtime, last_time);
3480
3481     ob_debug_type(OB_DEBUG_FOCUS,
3482                   "Want to activate window 0x%x with time %u (last time %u), "
3483                   "source=%s allowing? %d\n",
3484                   self->window, event_curtime, last_time,
3485                   (user ? "user" : "application"), allow);
3486
3487     if (allow) {
3488         if (event_curtime != CurrentTime)
3489             self->user_time = event_curtime;
3490
3491         client_present(self, here, TRUE);
3492     } else
3493         /* don't focus it but tell the user it wants attention */
3494         client_hilite(self, TRUE);
3495 }
3496
3497 static void client_bring_helper_windows_recursive(ObClient *self,
3498                                                   guint desktop)
3499 {
3500     GSList *it;
3501
3502     for (it = self->transients; it; it = g_slist_next(it))
3503         client_bring_helper_windows_recursive(it->data, desktop);
3504
3505     if (client_helper(self) &&
3506         self->desktop != desktop && self->desktop != DESKTOP_ALL)
3507     {
3508         client_set_desktop(self, desktop, FALSE);
3509     }
3510 }
3511
3512 void client_bring_helper_windows(ObClient *self)
3513 {
3514     client_bring_helper_windows_recursive(self, self->desktop);
3515 }
3516
3517 gboolean client_focused(ObClient *self)
3518 {
3519     return self == focus_client;
3520 }
3521
3522 static ObClientIcon* client_icon_recursive(ObClient *self, gint w, gint h)
3523 {
3524     guint i;
3525     gulong min_diff, min_i;
3526
3527     if (!self->nicons) {
3528         ObClientIcon *parent = NULL;
3529
3530         if (self->transient_for) {
3531             if (self->transient_for != OB_TRAN_GROUP)
3532                 parent = client_icon_recursive(self->transient_for, w, h);
3533             else {
3534                 GSList *it;
3535                 for (it = self->group->members; it; it = g_slist_next(it)) {
3536                     ObClient *c = it->data;
3537                     if (c != self && !c->transient_for) {
3538                         if ((parent = client_icon_recursive(c, w, h)))
3539                             break;
3540                     }
3541                 }
3542             }
3543         }
3544         
3545         return parent;
3546     }
3547
3548     /* some kind of crappy approximation to find the icon closest in size to
3549        what we requested, but icons are generally all the same ratio as
3550        eachother so it's good enough. */
3551
3552     min_diff = ABS(self->icons[0].width - w) + ABS(self->icons[0].height - h);
3553     min_i = 0;
3554
3555     for (i = 1; i < self->nicons; ++i) {
3556         gulong diff;
3557
3558         diff = ABS(self->icons[0].width - w) + ABS(self->icons[0].height - h);
3559         if (diff < min_diff) {
3560             min_diff = diff;
3561             min_i = i;
3562         }
3563     }
3564     return &self->icons[min_i];
3565 }
3566
3567 const ObClientIcon* client_icon(ObClient *self, gint w, gint h)
3568 {
3569     ObClientIcon *ret;
3570     static ObClientIcon deficon;
3571
3572     if (!(ret = client_icon_recursive(self, w, h))) {
3573         deficon.width = deficon.height = 48;
3574         deficon.data = ob_rr_theme->def_win_icon;
3575         ret = &deficon;
3576     }
3577     return ret;
3578 }
3579
3580 void client_set_layer(ObClient *self, gint layer)
3581 {
3582     if (layer < 0) {
3583         self->below = TRUE;
3584         self->above = FALSE;
3585     } else if (layer == 0) {
3586         self->below = self->above = FALSE;
3587     } else {
3588         self->below = FALSE;
3589         self->above = TRUE;
3590     }
3591     client_calc_layer(self);
3592     client_change_state(self); /* reflect this in the state hints */
3593 }
3594
3595 void client_set_undecorated(ObClient *self, gboolean undecorated)
3596 {
3597     if (self->undecorated != undecorated) {
3598         self->undecorated = undecorated;
3599         client_setup_decor_and_functions(self);
3600         /* Make sure the client knows it might have moved. Maybe there is a
3601          * better way of doing this so only one client_configure is sent, but
3602          * since 125 of these are sent per second when moving the window (with
3603          * user = FALSE) i doubt it matters much.
3604          */
3605         client_configure(self, self->area.x, self->area.y,
3606                          self->area.width, self->area.height, TRUE, TRUE);
3607         client_change_state(self); /* reflect this in the state hints */
3608     }
3609 }
3610
3611 guint client_monitor(ObClient *self)
3612 {
3613     return screen_find_monitor(&self->frame->area);
3614 }
3615
3616 ObClient *client_search_top_normal_parent(ObClient *self)
3617 {
3618     while (self->transient_for && self->transient_for != OB_TRAN_GROUP &&
3619            client_normal(self->transient_for))
3620         self = self->transient_for;
3621     return self;
3622 }
3623
3624 static GSList *client_search_all_top_parents_internal(ObClient *self,
3625                                                       gboolean bylayer,
3626                                                       ObStackingLayer layer)
3627 {
3628     GSList *ret = NULL;
3629     
3630     /* move up the direct transient chain as far as possible */
3631     while (self->transient_for && self->transient_for != OB_TRAN_GROUP &&
3632            (!bylayer || self->transient_for->layer == layer) &&
3633            client_normal(self->transient_for))
3634         self = self->transient_for;
3635
3636     if (!self->transient_for)
3637         ret = g_slist_prepend(ret, self);
3638     else {
3639             GSList *it;
3640
3641             g_assert(self->group);
3642
3643             for (it = self->group->members; it; it = g_slist_next(it)) {
3644                 ObClient *c = it->data;
3645
3646                 if (!c->transient_for && client_normal(c) &&
3647                     (!bylayer || c->layer == layer))
3648                 {
3649                     ret = g_slist_prepend(ret, c);
3650                 }
3651             }
3652
3653             if (ret == NULL) /* no group parents */
3654                 ret = g_slist_prepend(ret, self);
3655     }
3656
3657     return ret;
3658 }
3659
3660 GSList *client_search_all_top_parents(ObClient *self)
3661 {
3662     return client_search_all_top_parents_internal(self, FALSE, 0);
3663 }
3664
3665 GSList *client_search_all_top_parents_layer(ObClient *self)
3666 {
3667     return client_search_all_top_parents_internal(self, TRUE, self->layer);
3668 }
3669
3670 ObClient *client_search_focus_parent(ObClient *self)
3671 {
3672     if (self->transient_for) {
3673         if (self->transient_for != OB_TRAN_GROUP) {
3674             if (client_focused(self->transient_for))
3675                 return self->transient_for;
3676         } else {
3677             GSList *it;
3678
3679             for (it = self->group->members; it; it = g_slist_next(it)) {
3680                 ObClient *c = it->data;
3681
3682                 /* checking transient_for prevents infinate loops! */
3683                 if (c != self && !c->transient_for)
3684                     if (client_focused(c))
3685                         return c;
3686             }
3687         }
3688     }
3689
3690     return NULL;
3691 }
3692
3693 ObClient *client_search_parent(ObClient *self, ObClient *search)
3694 {
3695     if (self->transient_for) {
3696         if (self->transient_for != OB_TRAN_GROUP) {
3697             if (self->transient_for == search)
3698                 return search;
3699         } else {
3700             GSList *it;
3701
3702             for (it = self->group->members; it; it = g_slist_next(it)) {
3703                 ObClient *c = it->data;
3704
3705                 /* checking transient_for prevents infinate loops! */
3706                 if (c != self && !c->transient_for)
3707                     if (c == search)
3708                         return search;
3709             }
3710         }
3711     }
3712
3713     return NULL;
3714 }
3715
3716 ObClient *client_search_transient(ObClient *self, ObClient *search)
3717 {
3718     GSList *sit;
3719
3720     for (sit = self->transients; sit; sit = g_slist_next(sit)) {
3721         if (sit->data == search)
3722             return search;
3723         if (client_search_transient(sit->data, search))
3724             return search;
3725     }
3726     return NULL;
3727 }
3728
3729 #define WANT_EDGE(cur, c) \
3730             if(cur == c)                                                      \
3731                 continue;                                                     \
3732             if(!client_normal(cur))                                           \
3733                 continue;                                                     \
3734             if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL) \
3735                 continue;                                                     \
3736             if(cur->iconic)                                                   \
3737                 continue;
3738
3739 #define HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end) \
3740             if ((his_edge_start >= my_edge_start && \
3741                  his_edge_start <= my_edge_end) ||  \
3742                 (my_edge_start >= his_edge_start && \
3743                  my_edge_start <= his_edge_end))    \
3744                 dest = his_offset;
3745
3746 /* finds the nearest edge in the given direction from the current client
3747  * note to self: the edge is the -frame- edge (the actual one), not the
3748  * client edge.
3749  */
3750 gint client_directional_edge_search(ObClient *c, ObDirection dir, gboolean hang)
3751 {
3752     gint dest, monitor_dest;
3753     gint my_edge_start, my_edge_end, my_offset;
3754     GList *it;
3755     Rect *a, *monitor;
3756     
3757     if(!client_list)
3758         return -1;
3759
3760     a = screen_area(c->desktop);
3761     monitor = screen_area_monitor(c->desktop, client_monitor(c));
3762
3763     switch(dir) {
3764     case OB_DIRECTION_NORTH:
3765         my_edge_start = c->frame->area.x;
3766         my_edge_end = c->frame->area.x + c->frame->area.width;
3767         my_offset = c->frame->area.y + (hang ? c->frame->area.height : 0);
3768         
3769         /* default: top of screen */
3770         dest = a->y + (hang ? c->frame->area.height : 0);
3771         monitor_dest = monitor->y + (hang ? c->frame->area.height : 0);
3772         /* if the monitor edge comes before the screen edge, */
3773         /* use that as the destination instead. (For xinerama) */
3774         if (monitor_dest != dest && my_offset > monitor_dest)
3775             dest = monitor_dest; 
3776
3777         for(it = client_list; it && my_offset != dest; it = g_list_next(it)) {
3778             gint his_edge_start, his_edge_end, his_offset;
3779             ObClient *cur = it->data;
3780
3781             WANT_EDGE(cur, c)
3782
3783             his_edge_start = cur->frame->area.x;
3784             his_edge_end = cur->frame->area.x + cur->frame->area.width;
3785             his_offset = cur->frame->area.y + 
3786                          (hang ? 0 : cur->frame->area.height);
3787
3788             if(his_offset + 1 > my_offset)
3789                 continue;
3790
3791             if(his_offset < dest)
3792                 continue;
3793
3794             HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
3795         }
3796         break;
3797     case OB_DIRECTION_SOUTH:
3798         my_edge_start = c->frame->area.x;
3799         my_edge_end = c->frame->area.x + c->frame->area.width;
3800         my_offset = c->frame->area.y + (hang ? 0 : c->frame->area.height);
3801
3802         /* default: bottom of screen */
3803         dest = a->y + a->height - (hang ? c->frame->area.height : 0);
3804         monitor_dest = monitor->y + monitor->height -
3805                        (hang ? c->frame->area.height : 0);
3806         /* if the monitor edge comes before the screen edge, */
3807         /* use that as the destination instead. (For xinerama) */
3808         if (monitor_dest != dest && my_offset < monitor_dest)
3809             dest = monitor_dest; 
3810
3811         for(it = client_list; it && my_offset != dest; it = g_list_next(it)) {
3812             gint his_edge_start, his_edge_end, his_offset;
3813             ObClient *cur = it->data;
3814
3815             WANT_EDGE(cur, c)
3816
3817             his_edge_start = cur->frame->area.x;
3818             his_edge_end = cur->frame->area.x + cur->frame->area.width;
3819             his_offset = cur->frame->area.y +
3820                          (hang ? cur->frame->area.height : 0);
3821
3822
3823             if(his_offset - 1 < my_offset)
3824                 continue;
3825             
3826             if(his_offset > dest)
3827                 continue;
3828
3829             HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
3830         }
3831         break;
3832     case OB_DIRECTION_WEST:
3833         my_edge_start = c->frame->area.y;
3834         my_edge_end = c->frame->area.y + c->frame->area.height;
3835         my_offset = c->frame->area.x + (hang ? c->frame->area.width : 0);
3836
3837         /* default: leftmost egde of screen */
3838         dest = a->x + (hang ? c->frame->area.width : 0);
3839         monitor_dest = monitor->x + (hang ? c->frame->area.width : 0);
3840         /* if the monitor edge comes before the screen edge, */
3841         /* use that as the destination instead. (For xinerama) */
3842         if (monitor_dest != dest && my_offset > monitor_dest)
3843             dest = monitor_dest;            
3844
3845         for(it = client_list; it && my_offset != dest; it = g_list_next(it)) {
3846             gint his_edge_start, his_edge_end, his_offset;
3847             ObClient *cur = it->data;
3848
3849             WANT_EDGE(cur, c)
3850
3851             his_edge_start = cur->frame->area.y;
3852             his_edge_end = cur->frame->area.y + cur->frame->area.height;
3853             his_offset = cur->frame->area.x +
3854                          (hang ? 0 : cur->frame->area.width);
3855
3856             if(his_offset + 1 > my_offset)
3857                 continue;
3858
3859             if(his_offset < dest)
3860                 continue;
3861
3862             HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
3863         }
3864        break;
3865     case OB_DIRECTION_EAST:
3866         my_edge_start = c->frame->area.y;
3867         my_edge_end = c->frame->area.y + c->frame->area.height;
3868         my_offset = c->frame->area.x + (hang ? 0 : c->frame->area.width);
3869         
3870         /* default: rightmost edge of screen */
3871         dest = a->x + a->width - (hang ? c->frame->area.width : 0);
3872         monitor_dest = monitor->x + monitor->width -
3873                        (hang ? c->frame->area.width : 0);
3874         /* if the monitor edge comes before the screen edge, */
3875         /* use that as the destination instead. (For xinerama) */
3876         if (monitor_dest != dest && my_offset < monitor_dest)
3877             dest = monitor_dest;            
3878
3879         for(it = client_list; it && my_offset != dest; it = g_list_next(it)) {
3880             gint his_edge_start, his_edge_end, his_offset;
3881             ObClient *cur = it->data;
3882
3883             WANT_EDGE(cur, c)
3884
3885             his_edge_start = cur->frame->area.y;
3886             his_edge_end = cur->frame->area.y + cur->frame->area.height;
3887             his_offset = cur->frame->area.x +
3888                          (hang ? cur->frame->area.width : 0);
3889
3890             if(his_offset - 1 < my_offset)
3891                 continue;
3892             
3893             if(his_offset > dest)
3894                 continue;
3895
3896             HIT_EDGE(my_edge_start, my_edge_end, his_edge_start, his_edge_end)
3897         }
3898         break;
3899     case OB_DIRECTION_NORTHEAST:
3900     case OB_DIRECTION_SOUTHEAST:
3901     case OB_DIRECTION_NORTHWEST:
3902     case OB_DIRECTION_SOUTHWEST:
3903         /* not implemented */
3904     default:
3905         g_assert_not_reached();
3906         dest = 0; /* suppress warning */
3907     }
3908     return dest;
3909 }
3910
3911 ObClient* client_under_pointer()
3912 {
3913     gint x, y;
3914     GList *it;
3915     ObClient *ret = NULL;
3916
3917     if (screen_pointer_pos(&x, &y)) {
3918         for (it = stacking_list; it; it = g_list_next(it)) {
3919             if (WINDOW_IS_CLIENT(it->data)) {
3920                 ObClient *c = WINDOW_AS_CLIENT(it->data);
3921                 if (c->frame->visible &&
3922                     /* ignore all animating windows */
3923                     !frame_iconify_animating(c->frame) &&
3924                     RECT_CONTAINS(c->frame->area, x, y))
3925                 {
3926                     ret = c;
3927                     break;
3928                 }
3929             }
3930         }
3931     }
3932     return ret;
3933 }
3934
3935 gboolean client_has_group_siblings(ObClient *self)
3936 {
3937     return self->group && self->group->members->next;
3938 }