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