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