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