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