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