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