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