]> icculus.org git repositories - mikachu/openbox.git/blob - openbox/client.c
halfway through client changes but...
[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) 2003        Ben Jansens
5
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 2 of the License, or
9    (at your option) any later version.
10
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15
16    See the COPYING file for a copy of the GNU General Public License.
17 */
18
19 #include "client.h"
20 #include "debug.h"
21 #include "startupnotify.h"
22 #include "dock.h"
23 #include "xerror.h"
24 #include "screen.h"
25 #include "moveresize.h"
26 #include "place.h"
27 #include "prop.h"
28 #include "extensions.h"
29 #include "frame.h"
30 #include "session.h"
31 #include "event.h"
32 #include "grab.h"
33 #include "focus.h"
34 #include "stacking.h"
35 #include "openbox.h"
36 #include "group.h"
37 #include "config.h"
38 #include "menuframe.h"
39 #include "keyboard.h"
40 #include "mouse.h"
41 #include "render/render.h"
42
43 #include <glib.h>
44 #include <X11/Xutil.h>
45
46 /*! The event mask to grab on client windows */
47 #define CLIENT_EVENTMASK (PropertyChangeMask | FocusChangeMask | \
48                           StructureNotifyMask)
49
50 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
51                                 ButtonMotionMask)
52
53 typedef struct
54 {
55     ObClientDestructor func;
56     gpointer data;
57 } Destructor;
58
59 GList      *client_list        = NULL;
60 GSList     *client_destructors = NULL;
61
62 static void client_get_all(ObClient *self);
63 static void client_toggle_border(ObClient *self, gboolean show);
64 static void client_get_startup_id(ObClient *self);
65 static void client_get_area(ObClient *self);
66 static void client_get_desktop(ObClient *self);
67 static void client_get_state(ObClient *self);
68 static void client_get_shaped(ObClient *self);
69 static void client_get_mwm_hints(ObClient *self);
70 static void client_get_gravity(ObClient *self);
71 static void client_showhide(ObClient *self);
72 static void client_change_allowed_actions(ObClient *self);
73 static void client_change_state(ObClient *self);
74 static void client_apply_startup_state(ObClient *self);
75 static void client_restore_session_state(ObClient *self);
76 static void client_restore_session_stacking(ObClient *self);
77 static void client_urgent_notify(ObClient *self);
78
79 void client_startup(gboolean reconfig)
80 {
81     if (reconfig) return;
82
83     client_set_list();
84 }
85
86 void client_shutdown(gboolean reconfig)
87 {
88 }
89
90 void client_add_destructor(ObClientDestructor func, gpointer data)
91 {
92     Destructor *d = g_new(Destructor, 1);
93     d->func = func;
94     d->data = data;
95     client_destructors = g_slist_prepend(client_destructors, d);
96 }
97
98 void client_remove_destructor(ObClientDestructor func)
99 {
100     GSList *it;
101
102     for (it = client_destructors; it; it = g_slist_next(it)) {
103         Destructor *d = it->data;
104         if (d->func == func) {
105             g_free(d);
106             client_destructors = g_slist_delete_link(client_destructors, it);
107             break;
108         }
109     }
110 }
111
112 void client_set_list()
113 {
114     Window *windows, *win_it;
115     GList *it;
116     guint size = g_list_length(client_list);
117
118     /* create an array of the window ids */
119     if (size > 0) {
120         windows = g_new(Window, size);
121         win_it = windows;
122         for (it = client_list; it != NULL; it = it->next, ++win_it)
123             *win_it = ((ObClient*)it->data)->window;
124     } else
125         windows = NULL;
126
127     PROP_SETA32(RootWindow(ob_display, ob_screen),
128                 net_client_list, window, (guint32*)windows, size);
129
130     if (windows)
131         g_free(windows);
132
133     stacking_set_list();
134 }
135
136 /*
137 void client_foreach_transient(ObClient *self, ObClientForeachFunc func, void *data)
138 {
139     GSList *it;
140
141     for (it = self->transients; it; it = it->next) {
142         if (!func(it->data, data)) return;
143         client_foreach_transient(it->data, func, data);
144     }
145 }
146
147 void client_foreach_ancestor(ObClient *self, ObClientForeachFunc func, void *data)
148 {
149     if (self->transient_for) {
150         if (self->transient_for != OB_TRAN_GROUP) {
151             if (!func(self->transient_for, data)) return;
152             client_foreach_ancestor(self->transient_for, func, data);
153         } else {
154             GSList *it;
155
156             for (it = self->group->members; it; it = it->next)
157                 if (it->data != self &&
158                     !((ObClient*)it->data)->transient_for) {
159                     if (!func(it->data, data)) return;
160                     client_foreach_ancestor(it->data, func, data);
161                 }
162         }
163     }
164 }
165 */
166
167 void client_manage_all()
168 {
169     unsigned int i, j, nchild;
170     Window w, *children;
171     XWMHints *wmhints;
172     XWindowAttributes attrib;
173
174     XQueryTree(ob_display, RootWindow(ob_display, ob_screen),
175                &w, &w, &children, &nchild);
176
177     /* remove all icon windows from the list */
178     for (i = 0; i < nchild; i++) {
179         if (children[i] == None) continue;
180         wmhints = XGetWMHints(ob_display, children[i]);
181         if (wmhints) {
182             if ((wmhints->flags & IconWindowHint) &&
183                 (wmhints->icon_window != children[i]))
184                 for (j = 0; j < nchild; j++)
185                     if (children[j] == wmhints->icon_window) {
186                         children[j] = None;
187                         break;
188                     }
189             XFree(wmhints);
190         }
191     }
192
193     for (i = 0; i < nchild; ++i) {
194         if (children[i] == None)
195             continue;
196         if (XGetWindowAttributes(ob_display, children[i], &attrib)) {
197             if (attrib.override_redirect) continue;
198
199             if (attrib.map_state != IsUnmapped)
200                 client_manage(children[i]);
201         }
202     }
203     XFree(children);
204 }
205
206 void client_manage(Window window)
207 {
208     ObClient *self;
209     XEvent e;
210     XWindowAttributes attrib;
211     XSetWindowAttributes attrib_set;
212     XWMHints *wmhint;
213     gboolean activate = FALSE;
214
215     grab_server(TRUE);
216
217     /* check if it has already been unmapped by the time we started mapping
218        the grab does a sync so we don't have to here */
219     if (XCheckTypedWindowEvent(ob_display, window, DestroyNotify, &e) ||
220         XCheckTypedWindowEvent(ob_display, window, UnmapNotify, &e)) {
221         XPutBackEvent(ob_display, &e);
222
223         grab_server(FALSE);
224         return; /* don't manage it */
225     }
226
227     /* make sure it isn't an override-redirect window */
228     if (!XGetWindowAttributes(ob_display, window, &attrib) ||
229         attrib.override_redirect) {
230         grab_server(FALSE);
231         return; /* don't manage it */
232     }
233   
234     /* is the window a docking app */
235     if ((wmhint = XGetWMHints(ob_display, window))) {
236         if ((wmhint->flags & StateHint) &&
237             wmhint->initial_state == WithdrawnState) {
238             dock_add(window, wmhint);
239             grab_server(FALSE);
240             XFree(wmhint);
241             return;
242         }
243         XFree(wmhint);
244     }
245
246     ob_debug("Managing window: %lx\n", window);
247
248     /* choose the events we want to receive on the CLIENT window */
249     attrib_set.event_mask = CLIENT_EVENTMASK;
250     attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK;
251     XChangeWindowAttributes(ob_display, window,
252                             CWEventMask|CWDontPropagate, &attrib_set);
253
254
255     /* create the ObClient struct, and populate it from the hints on the
256        window */
257     self = g_new0(ObClient, 1);
258     self->obwin.type = Window_Client;
259     self->window = window;
260
261     /* non-zero defaults */
262     self->title_count = 1;
263     self->wmstate = NormalState;
264     self->layer = -1;
265     self->desktop = screen_num_desktops; /* always an invalid value */
266
267     client_get_all(self);
268     client_restore_session_state(self);
269
270     sn_app_started(self->class);
271
272     client_change_state(self);
273
274     /* remove the client's border (and adjust re gravity) */
275     client_toggle_border(self, FALSE);
276      
277     /* specify that if we exit, the window should not be destroyed and should
278        be reparented back to root automatically */
279     XChangeSaveSet(ob_display, window, SetModeInsert);
280
281     /* create the decoration frame for the client window */
282     self->frame = frame_new();
283
284     frame_grab_client(self->frame, self);
285
286     grab_server(FALSE);
287
288     client_apply_startup_state(self);
289
290     /* update the focus lists */
291     focus_order_add_new(self);
292
293     stacking_add(CLIENT_AS_WINDOW(self));
294     client_restore_session_stacking(self);
295
296     /* focus the new window? */
297     if (ob_state() != OB_STATE_STARTING &&
298         (config_focus_new || client_search_focus_parent(self)) &&
299         /* note the check against Type_Normal/Dialog, not client_normal(self),
300            which would also include other types. in this case we want more
301            strict rules for focus */
302         (self->type == OB_CLIENT_TYPE_NORMAL ||
303          self->type == OB_CLIENT_TYPE_DIALOG))
304     {        
305         activate = TRUE;
306 #if 0
307         if (self->desktop != screen_desktop) {
308             /* activate the window */
309             activate = TRUE;
310         } else {
311             gboolean group_foc = FALSE;
312
313             if (self->group) {
314                 GSList *it;
315
316                 for (it = self->group->members; it; it = it->next)
317                 {
318                     if (client_focused(it->data))
319                     {
320                         group_foc = TRUE;
321                         break;
322                     }
323                 }
324             }
325             if ((group_foc ||
326                  (!self->transient_for && (!self->group ||
327                                            !self->group->members->next))) ||
328                 client_search_focus_tree_full(self) ||
329                 !focus_client ||
330                 !client_normal(focus_client))
331             {
332                 /* activate the window */
333                 activate = TRUE;
334             }
335         }
336 #endif
337     }
338
339     if (ob_state() == OB_STATE_RUNNING) {
340         int x = self->area.x, ox = x;
341         int y = self->area.y, oy = y;
342
343         place_client(self, &x, &y);
344
345         /* make sure the window is visible */
346         client_find_onscreen(self, &x, &y,
347                              self->frame->area.width,
348                              self->frame->area.height,
349                              client_normal(self));
350
351         if (x != ox || y != oy)
352             client_move(self, x, y);
353     }
354
355     client_showhide(self);
356
357     /* use client_focus instead of client_activate cuz client_activate does
358        stuff like switch desktops etc and I'm not interested in all that when
359        a window maps since its not based on an action from the user like
360        clicking a window to activate is. so keep the new window out of the way
361        but do focus it. */
362     if (activate) client_focus(self);
363
364     /* client_activate does this but we aret using it so we have to do it
365        here as well */
366     if (screen_showing_desktop)
367         screen_show_desktop(FALSE);
368
369     /* add to client list/map */
370     client_list = g_list_append(client_list, self);
371     g_hash_table_insert(window_map, &self->window, self);
372
373     /* this has to happen after we're in the client_list */
374     screen_update_areas();
375
376     /* update the list hints */
377     client_set_list();
378
379     keyboard_grab_for_client(self, TRUE);
380     mouse_grab_for_client(self, TRUE);
381
382     ob_debug("Managed window 0x%lx (%s)\n", window, self->class);
383 }
384
385 void client_unmanage_all()
386 {
387     while (client_list != NULL)
388         client_unmanage(client_list->data);
389 }
390
391 void client_unmanage(ObClient *self)
392 {
393     guint j;
394     GSList *it;
395
396     ob_debug("Unmanaging window: %lx (%s)\n", self->window, self->class);
397
398     g_assert(self != NULL);
399
400     keyboard_grab_for_client(self, FALSE);
401     mouse_grab_for_client(self, FALSE);
402
403     /* remove the window from our save set */
404     XChangeSaveSet(ob_display, self->window, SetModeDelete);
405
406     /* we dont want events no more */
407     XSelectInput(ob_display, self->window, NoEventMask);
408
409     frame_hide(self->frame);
410
411     client_list = g_list_remove(client_list, self);
412     stacking_remove(self);
413     g_hash_table_remove(window_map, &self->window);
414
415     /* update the focus lists */
416     focus_order_remove(self);
417
418     /* once the client is out of the list, update the struts to remove it's
419        influence */
420     screen_update_areas();
421
422     for (it = client_destructors; it; it = g_slist_next(it)) {
423         Destructor *d = it->data;
424         d->func(self, d->data);
425     }
426         
427     if (focus_client == self) {
428         XEvent e;
429
430         /* focus the last focused window on the desktop, and ignore enter
431            events from the unmap so it doesnt mess with the focus */
432         while (XCheckTypedEvent(ob_display, EnterNotify, &e));
433         /* remove these flags so we don't end up getting focused in the
434            fallback! */
435         self->can_focus = FALSE;
436         self->focus_notify = FALSE;
437         self->modal = FALSE;
438         client_unfocus(self);
439     }
440
441     /* tell our parent(s) that we're gone */
442     if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */
443         GSList *it;
444
445         for (it = self->group->members; it; it = it->next)
446             if (it->data != self)
447                 ((ObClient*)it->data)->transients =
448                     g_slist_remove(((ObClient*)it->data)->transients, self);
449     } else if (self->transient_for) {        /* transient of window */
450         self->transient_for->transients =
451             g_slist_remove(self->transient_for->transients, self);
452     }
453
454     /* tell our transients that we're gone */
455     for (it = self->transients; it != NULL; it = it->next) {
456         if (((ObClient*)it->data)->transient_for != OB_TRAN_GROUP) {
457             ((ObClient*)it->data)->transient_for = NULL;
458             client_calc_layer(it->data);
459         }
460     }
461
462     /* remove from its group */
463     if (self->group) {
464         group_remove(self->group, self);
465         self->group = NULL;
466     }
467
468     /* give the client its border back */
469     client_toggle_border(self, TRUE);
470
471     /* reparent the window out of the frame, and free the frame */
472     frame_release_client(self->frame, self);
473     self->frame = NULL;
474      
475     if (ob_state() != OB_STATE_EXITING) {
476         /* these values should not be persisted across a window
477            unmapping/mapping */
478         PROP_ERASE(self->window, net_wm_desktop);
479         PROP_ERASE(self->window, net_wm_state);
480         PROP_ERASE(self->window, wm_state);
481     } else {
482         /* if we're left in an iconic state, the client wont be mapped. this is
483            bad, since we will no longer be managing the window on restart */
484         if (self->iconic)
485             XMapWindow(ob_display, self->window);
486     }
487
488
489     ob_debug("Unmanaged window 0x%lx\n", self->window);
490
491     /* free all data allocated in the client struct */
492     g_slist_free(self->transients);
493     for (j = 0; j < self->nicons; ++j)
494         g_free(self->icons[j].data);
495     if (self->nicons > 0)
496         g_free(self->icons);
497     g_free(self->title);
498     g_free(self->icon_title);
499     g_free(self->name);
500     g_free(self->class);
501     g_free(self->role);
502     g_free(self->sm_client_id);
503     g_free(self);
504      
505     /* update the list hints */
506     client_set_list();
507 }
508
509 static void client_urgent_notify(ObClient *self)
510 {
511     if (self->urgent)
512         frame_flash_start(self->frame);
513     else
514         frame_flash_stop(self->frame);
515 }
516
517 static void client_restore_session_state(ObClient *self)
518 {
519     GList *it;
520
521     if (!(it = session_state_find(self)))
522         return;
523
524     self->session = it->data;
525
526     RECT_SET(self->area, self->session->x, self->session->y,
527              self->session->w, self->session->h);
528     self->positioned = TRUE;
529     XResizeWindow(ob_display, self->window,
530                   self->session->w, self->session->h);
531
532     self->desktop = (self->session->desktop == DESKTOP_ALL ?
533                      self->session->desktop :
534                      MIN(screen_num_desktops - 1, self->session->desktop));
535     PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop);
536
537     self->shaded = self->session->shaded;
538     self->iconic = self->session->iconic;
539     self->skip_pager = self->session->skip_pager;
540     self->skip_taskbar = self->session->skip_taskbar;
541     self->fullscreen = self->session->fullscreen;
542     self->above = self->session->above;
543     self->below = self->session->below;
544     self->max_horz = self->session->max_horz;
545     self->max_vert = self->session->max_vert;
546 }
547
548 static void client_restore_session_stacking(ObClient *self)
549 {
550     GList *it;
551
552     if (!self->session) return;
553
554     it = g_list_find(session_saved_state, self->session);
555     for (it = g_list_previous(it); it; it = g_list_previous(it)) {
556         GList *cit;
557
558         for (cit = client_list; cit; cit = g_list_next(cit))
559             if (session_state_cmp(it->data, cit->data))
560                 break;
561         if (cit) {
562             client_calc_layer(self);
563             stacking_below(CLIENT_AS_WINDOW(self),
564                            CLIENT_AS_WINDOW(cit->data));
565             break;
566         }
567     }
568 }
569
570 void client_move_onscreen(ObClient *self, gboolean rude)
571 {
572     int x = self->area.x;
573     int y = self->area.y;
574     if (client_find_onscreen(self, &x, &y,
575                              self->frame->area.width,
576                              self->frame->area.height, rude)) {
577         client_move(self, x, y);
578     }
579 }
580
581 gboolean client_find_onscreen(ObClient *self, int *x, int *y, int w, int h,
582                               gboolean rude)
583 {
584     Rect *a;
585     int ox = *x, oy = *y;
586
587     frame_client_gravity(self->frame, x, y); /* get where the frame
588                                                 would be */
589
590     /* XXX watch for xinerama dead areas */
591
592     a = screen_area(self->desktop);
593     if (client_normal(self)) {
594         if (!self->strut.right && *x >= a->x + a->width - 1)
595             *x = a->x + a->width - self->frame->area.width;
596         if (!self->strut.bottom && *y >= a->y + a->height - 1)
597             *y = a->y + a->height - self->frame->area.height;
598         if (!self->strut.left && *x + self->frame->area.width - 1 < a->x)
599             *x = a->x;
600         if (!self->strut.top && *y + self->frame->area.height - 1 < a->y)
601             *y = a->y;
602     }
603
604     if (rude) {
605         /* this is my MOZILLA BITCHSLAP. oh ya it fucking feels good.
606            Java can suck it too. */
607
608         /* dont let windows map/move into the strut unless they
609            are bigger than the available area */
610         if (w <= a->width) {
611             if (!self->strut.left && *x < a->x) *x = a->x;
612             if (!self->strut.right && *x + w > a->x + a->width)
613                 *x = a->x + a->width - w;
614         }
615         if (h <= a->height) {
616             if (!self->strut.top && *y < a->y) *y = a->y;
617             if (!self->strut.bottom && *y + h > a->y + a->height)
618                 *y = a->y + a->height - h;
619         }
620     }
621
622     frame_frame_gravity(self->frame, x, y); /* get where the client
623                                                should be */
624
625     return ox != *x || oy != *y;
626 }
627
628 static void client_toggle_border(ObClient *self, gboolean show)
629 {
630     /* adjust our idea of where the client is, based on its border. When the
631        border is removed, the client should now be considered to be in a
632        different position.
633        when re-adding the border to the client, the same operation needs to be
634        reversed. */
635     int oldx = self->area.x, oldy = self->area.y;
636     int x = oldx, y = oldy;
637     switch(self->gravity) {
638     default:
639     case NorthWestGravity:
640     case WestGravity:
641     case SouthWestGravity:
642         break;
643     case NorthEastGravity:
644     case EastGravity:
645     case SouthEastGravity:
646         if (show) x -= self->border_width * 2;
647         else      x += self->border_width * 2;
648         break;
649     case NorthGravity:
650     case SouthGravity:
651     case CenterGravity:
652     case ForgetGravity:
653     case StaticGravity:
654         if (show) x -= self->border_width;
655         else      x += self->border_width;
656         break;
657     }
658     switch(self->gravity) {
659     default:
660     case NorthWestGravity:
661     case NorthGravity:
662     case NorthEastGravity:
663         break;
664     case SouthWestGravity:
665     case SouthGravity:
666     case SouthEastGravity:
667         if (show) y -= self->border_width * 2;
668         else      y += self->border_width * 2;
669         break;
670     case WestGravity:
671     case EastGravity:
672     case CenterGravity:
673     case ForgetGravity:
674     case StaticGravity:
675         if (show) y -= self->border_width;
676         else      y += self->border_width;
677         break;
678     }
679     self->area.x = x;
680     self->area.y = y;
681
682     if (show) {
683         XSetWindowBorderWidth(ob_display, self->window, self->border_width);
684
685         /* move the client so it is back it the right spot _with_ its
686            border! */
687         if (x != oldx || y != oldy)
688             XMoveWindow(ob_display, self->window, x, y);
689     } else
690         XSetWindowBorderWidth(ob_display, self->window, 0);
691 }
692
693
694 static void client_get_all(ObClient *self)
695 {
696     client_get_area(self);
697     client_update_transient_for(self);
698     client_update_wmhints(self);
699     client_get_startup_id(self);
700     client_get_desktop(self);
701     client_get_state(self);
702     client_get_shaped(self);
703
704     client_get_mwm_hints(self);
705     client_get_type(self);/* this can change the mwmhints for special cases */
706
707     client_update_protocols(self);
708
709     client_get_gravity(self); /* get the attribute gravity */
710     client_update_normal_hints(self); /* this may override the attribute
711                                          gravity */
712
713     /* got the type, the mwmhints, the protocols, and the normal hints
714        (min/max sizes), so we're ready to set up the decorations/functions */
715     client_setup_decor_and_functions(self);
716   
717     client_update_title(self);
718     client_update_class(self);
719     client_update_sm_client_id(self);
720     client_update_strut(self);
721     client_update_icons(self);
722 }
723
724 static void client_get_startup_id(ObClient *self)
725 {
726     if (!(PROP_GETS(self->window, net_startup_id, utf8, &self->startup_id)))
727         if (self->group)
728             PROP_GETS(self->group->leader,
729                       net_startup_id, utf8, &self->startup_id);
730 }
731
732 static void client_get_area(ObClient *self)
733 {
734     XWindowAttributes wattrib;
735     Status ret;
736   
737     ret = XGetWindowAttributes(ob_display, self->window, &wattrib);
738     g_assert(ret != BadWindow);
739
740     RECT_SET(self->area, wattrib.x, wattrib.y, wattrib.width, wattrib.height);
741     self->border_width = wattrib.border_width;
742 }
743
744 static void client_get_desktop(ObClient *self)
745 {
746     guint32 d = screen_num_desktops; /* an always-invalid value */
747
748     if (PROP_GET32(self->window, net_wm_desktop, cardinal, &d)) {
749         if (d >= screen_num_desktops && d != DESKTOP_ALL)
750             self->desktop = screen_num_desktops - 1;
751         else
752             self->desktop = d;
753     } else {
754         gboolean trdesk = FALSE;
755
756        if (self->transient_for) {
757            if (self->transient_for != OB_TRAN_GROUP) {
758                 self->desktop = self->transient_for->desktop;
759                 trdesk = TRUE;
760             } else {
761                 GSList *it;
762
763                 for (it = self->group->members; it; it = it->next)
764                     if (it->data != self &&
765                         !((ObClient*)it->data)->transient_for) {
766                         self->desktop = ((ObClient*)it->data)->desktop;
767                         trdesk = TRUE;
768                         break;
769                     }
770             }
771        }
772        if (!trdesk) {
773            /* try get from the startup-notification protocol */
774            if (sn_get_desktop(self->startup_id, &self->desktop)) {
775                if (self->desktop >= screen_num_desktops &&
776                    self->desktop != DESKTOP_ALL)
777                    self->desktop = screen_num_desktops - 1;
778            } else
779                /* defaults to the current desktop */
780                self->desktop = screen_desktop;
781        }
782     }
783     if (self->desktop != d) {
784         /* set the desktop hint, to make sure that it always exists */
785         PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop);
786     }
787 }
788
789 static void client_get_state(ObClient *self)
790 {
791     guint32 *state;
792     guint num;
793   
794     if (PROP_GETA32(self->window, net_wm_state, atom, &state, &num)) {
795         gulong i;
796         for (i = 0; i < num; ++i) {
797             if (state[i] == prop_atoms.net_wm_state_modal)
798                 self->modal = TRUE;
799             else if (state[i] == prop_atoms.net_wm_state_shaded)
800                 self->shaded = TRUE;
801             else if (state[i] == prop_atoms.net_wm_state_hidden)
802                 self->iconic = TRUE;
803             else if (state[i] == prop_atoms.net_wm_state_skip_taskbar)
804                 self->skip_taskbar = TRUE;
805             else if (state[i] == prop_atoms.net_wm_state_skip_pager)
806                 self->skip_pager = TRUE;
807             else if (state[i] == prop_atoms.net_wm_state_fullscreen)
808                 self->fullscreen = TRUE;
809             else if (state[i] == prop_atoms.net_wm_state_maximized_vert)
810                 self->max_vert = TRUE;
811             else if (state[i] == prop_atoms.net_wm_state_maximized_horz)
812                 self->max_horz = TRUE;
813             else if (state[i] == prop_atoms.net_wm_state_above)
814                 self->above = TRUE;
815             else if (state[i] == prop_atoms.net_wm_state_below)
816                 self->below = TRUE;
817             else if (state[i] == prop_atoms.ob_wm_state_undecorated)
818                 self->undecorated = TRUE;
819         }
820
821         g_free(state);
822     }
823 }
824
825 static void client_get_shaped(ObClient *self)
826 {
827     self->shaped = FALSE;
828 #ifdef   SHAPE
829     if (extensions_shape) {
830         int foo;
831         guint ufoo;
832         int s;
833
834         XShapeSelectInput(ob_display, self->window, ShapeNotifyMask);
835
836         XShapeQueryExtents(ob_display, self->window, &s, &foo,
837                            &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo,
838                            &ufoo);
839         self->shaped = (s != 0);
840     }
841 #endif
842 }
843
844 void client_update_transient_for(ObClient *self)
845 {
846     Window t = None;
847     ObClient *target = NULL;
848
849     if (XGetTransientForHint(ob_display, self->window, &t)) {
850         self->transient = TRUE;
851         if (t != self->window) { /* cant be transient to itself! */
852             target = g_hash_table_lookup(window_map, &t);
853             /* if this happens then we need to check for it*/
854             g_assert(target != self);
855             if (target && !WINDOW_IS_CLIENT(target)) {
856                 /* this can happen when a dialog is a child of
857                    a dockapp, for example */
858                 target = NULL;
859             }
860             
861             if (!target && self->group) {
862                 /* not transient to a client, see if it is transient for a
863                    group */
864                 if (t == self->group->leader ||
865                     t == None ||
866                     t == RootWindow(ob_display, ob_screen))
867                 {
868                     /* window is a transient for its group! */
869                     target = OB_TRAN_GROUP;
870                 }
871             }
872         }
873     } else
874         self->transient = FALSE;
875
876     /* if anything has changed... */
877     if (target != self->transient_for) {
878         if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */
879             GSList *it;
880
881             /* remove from old parents */
882             for (it = self->group->members; it; it = g_slist_next(it)) {
883                 ObClient *c = it->data;
884                 if (c != self && !c->transient_for)
885                     c->transients = g_slist_remove(c->transients, self);
886             }
887         } else if (self->transient_for != NULL) { /* transient of window */
888             /* remove from old parent */
889             self->transient_for->transients =
890                 g_slist_remove(self->transient_for->transients, self);
891         }
892         self->transient_for = target;
893         if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */
894             GSList *it;
895
896             /* add to new parents */
897             for (it = self->group->members; it; it = g_slist_next(it)) {
898                 ObClient *c = it->data;
899                 if (c != self && !c->transient_for)
900                     c->transients = g_slist_append(c->transients, self);
901             }
902
903             /* remove all transients which are in the group, that causes
904                circlular pointer hell of doom */
905             for (it = self->group->members; it; it = g_slist_next(it)) {
906                 GSList *sit, *next;
907                 for (sit = self->transients; sit; sit = next) {
908                     next = g_slist_next(sit);
909                     if (sit->data == it->data)
910                         self->transients =
911                             g_slist_delete_link(self->transients, sit);
912                 }
913             }
914         } else if (self->transient_for != NULL) { /* transient of window */
915             /* add to new parent */
916             self->transient_for->transients =
917                 g_slist_append(self->transient_for->transients, self);
918         }
919     }
920 }
921
922 static void client_get_mwm_hints(ObClient *self)
923 {
924     guint num;
925     guint32 *hints;
926
927     self->mwmhints.flags = 0; /* default to none */
928
929     if (PROP_GETA32(self->window, motif_wm_hints, motif_wm_hints,
930                     &hints, &num)) {
931         if (num >= OB_MWM_ELEMENTS) {
932             self->mwmhints.flags = hints[0];
933             self->mwmhints.functions = hints[1];
934             self->mwmhints.decorations = hints[2];
935         }
936         g_free(hints);
937     }
938 }
939
940 void client_get_type(ObClient *self)
941 {
942     guint num, i;
943     guint32 *val;
944
945     self->type = -1;
946   
947     if (PROP_GETA32(self->window, net_wm_window_type, atom, &val, &num)) {
948         /* use the first value that we know about in the array */
949         for (i = 0; i < num; ++i) {
950             if (val[i] == prop_atoms.net_wm_window_type_desktop)
951                 self->type = OB_CLIENT_TYPE_DESKTOP;
952             else if (val[i] == prop_atoms.net_wm_window_type_dock)
953                 self->type = OB_CLIENT_TYPE_DOCK;
954             else if (val[i] == prop_atoms.net_wm_window_type_toolbar)
955                 self->type = OB_CLIENT_TYPE_TOOLBAR;
956             else if (val[i] == prop_atoms.net_wm_window_type_menu)
957                 self->type = OB_CLIENT_TYPE_MENU;
958             else if (val[i] == prop_atoms.net_wm_window_type_utility)
959                 self->type = OB_CLIENT_TYPE_UTILITY;
960             else if (val[i] == prop_atoms.net_wm_window_type_splash)
961                 self->type = OB_CLIENT_TYPE_SPLASH;
962             else if (val[i] == prop_atoms.net_wm_window_type_dialog)
963                 self->type = OB_CLIENT_TYPE_DIALOG;
964             else if (val[i] == prop_atoms.net_wm_window_type_normal)
965                 self->type = OB_CLIENT_TYPE_NORMAL;
966             else if (val[i] == prop_atoms.kde_net_wm_window_type_override) {
967                 /* prevent this window from getting any decor or
968                    functionality */
969                 self->mwmhints.flags &= (OB_MWM_FLAG_FUNCTIONS |
970                                          OB_MWM_FLAG_DECORATIONS);
971                 self->mwmhints.decorations = 0;
972                 self->mwmhints.functions = 0;
973             }
974             if (self->type != (ObClientType) -1)
975                 break; /* grab the first legit type */
976         }
977         g_free(val);
978     }
979     
980     if (self->type == (ObClientType) -1) {
981         /*the window type hint was not set, which means we either classify
982           ourself as a normal window or a dialog, depending on if we are a
983           transient. */
984         if (self->transient)
985             self->type = OB_CLIENT_TYPE_DIALOG;
986         else
987             self->type = OB_CLIENT_TYPE_NORMAL;
988     }
989 }
990
991 void client_update_protocols(ObClient *self)
992 {
993     guint32 *proto;
994     guint num_return, i;
995
996     self->focus_notify = FALSE;
997     self->delete_window = FALSE;
998
999     if (PROP_GETA32(self->window, wm_protocols, atom, &proto, &num_return)) {
1000         for (i = 0; i < num_return; ++i) {
1001             if (proto[i] == prop_atoms.wm_delete_window) {
1002                 /* this means we can request the window to close */
1003                 self->delete_window = TRUE;
1004             } else if (proto[i] == prop_atoms.wm_take_focus)
1005                 /* if this protocol is requested, then the window will be
1006                    notified whenever we want it to receive focus */
1007                 self->focus_notify = TRUE;
1008         }
1009         g_free(proto);
1010     }
1011 }
1012
1013 static void client_get_gravity(ObClient *self)
1014 {
1015     XWindowAttributes wattrib;
1016     Status ret;
1017
1018     ret = XGetWindowAttributes(ob_display, self->window, &wattrib);
1019     g_assert(ret != BadWindow);
1020     self->gravity = wattrib.win_gravity;
1021 }
1022
1023 void client_update_normal_hints(ObClient *self)
1024 {
1025     XSizeHints size;
1026     long ret;
1027     int oldgravity = self->gravity;
1028
1029     /* defaults */
1030     self->min_ratio = 0.0f;
1031     self->max_ratio = 0.0f;
1032     SIZE_SET(self->size_inc, 1, 1);
1033     SIZE_SET(self->base_size, 0, 0);
1034     SIZE_SET(self->min_size, 0, 0);
1035     SIZE_SET(self->max_size, G_MAXINT, G_MAXINT);
1036
1037     /* get the hints from the window */
1038     if (XGetWMNormalHints(ob_display, self->window, &size, &ret)) {
1039         self->positioned = !!(size.flags & (PPosition|USPosition));
1040
1041         if (size.flags & PWinGravity) {
1042             self->gravity = size.win_gravity;
1043       
1044             /* if the client has a frame, i.e. has already been mapped and
1045                is changing its gravity */
1046             if (self->frame && self->gravity != oldgravity) {
1047                 /* move our idea of the client's position based on its new
1048                    gravity */
1049                 self->area.x = self->frame->area.x;
1050                 self->area.y = self->frame->area.y;
1051                 frame_frame_gravity(self->frame, &self->area.x, &self->area.y);
1052             }
1053         }
1054
1055         if (size.flags & PAspect) {
1056             if (size.min_aspect.y)
1057                 self->min_ratio = (float)size.min_aspect.x / size.min_aspect.y;
1058             if (size.max_aspect.y)
1059                 self->max_ratio = (float)size.max_aspect.x / size.max_aspect.y;
1060         }
1061
1062         if (size.flags & PMinSize)
1063             SIZE_SET(self->min_size, size.min_width, size.min_height);
1064     
1065         if (size.flags & PMaxSize)
1066             SIZE_SET(self->max_size, size.max_width, size.max_height);
1067     
1068         if (size.flags & PBaseSize)
1069             SIZE_SET(self->base_size, size.base_width, size.base_height);
1070     
1071         if (size.flags & PResizeInc)
1072             SIZE_SET(self->size_inc, size.width_inc, size.height_inc);
1073     }
1074 }
1075
1076 void client_setup_decor_and_functions(ObClient *self)
1077 {
1078     /* start with everything (cept fullscreen) */
1079     self->decorations =
1080         (OB_FRAME_DECOR_TITLEBAR |
1081          (ob_rr_theme->show_handle ? OB_FRAME_DECOR_HANDLE : 0) |
1082          OB_FRAME_DECOR_GRIPS |
1083          OB_FRAME_DECOR_BORDER |
1084          OB_FRAME_DECOR_ICON |
1085          OB_FRAME_DECOR_ALLDESKTOPS |
1086          OB_FRAME_DECOR_ICONIFY |
1087          OB_FRAME_DECOR_MAXIMIZE |
1088          OB_FRAME_DECOR_SHADE);
1089     self->functions =
1090         (OB_CLIENT_FUNC_RESIZE |
1091          OB_CLIENT_FUNC_MOVE |
1092          OB_CLIENT_FUNC_ICONIFY |
1093          OB_CLIENT_FUNC_MAXIMIZE |
1094          OB_CLIENT_FUNC_SHADE);
1095     if (self->delete_window) {
1096         self->functions |= OB_CLIENT_FUNC_CLOSE;
1097         self->decorations |= OB_FRAME_DECOR_CLOSE;
1098     }
1099
1100     if (!(self->min_size.width < self->max_size.width ||
1101           self->min_size.height < self->max_size.height))
1102         self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1103
1104     switch (self->type) {
1105     case OB_CLIENT_TYPE_NORMAL:
1106         /* normal windows retain all of the possible decorations and
1107            functionality, and are the only windows that you can fullscreen */
1108         self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
1109         break;
1110
1111     case OB_CLIENT_TYPE_DIALOG:
1112     case OB_CLIENT_TYPE_UTILITY:
1113         /* these windows cannot be maximized */
1114         self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1115         break;
1116
1117     case OB_CLIENT_TYPE_MENU:
1118     case OB_CLIENT_TYPE_TOOLBAR:
1119         /* these windows get less functionality */
1120         self->functions &= ~(OB_CLIENT_FUNC_ICONIFY | OB_CLIENT_FUNC_RESIZE);
1121         break;
1122
1123     case OB_CLIENT_TYPE_DESKTOP:
1124     case OB_CLIENT_TYPE_DOCK:
1125     case OB_CLIENT_TYPE_SPLASH:
1126         /* none of these windows are manipulated by the window manager */
1127         self->decorations = 0;
1128         self->functions = 0;
1129         break;
1130     }
1131
1132     /* Mwm Hints are applied subtractively to what has already been chosen for
1133        decor and functionality */
1134     if (self->mwmhints.flags & OB_MWM_FLAG_DECORATIONS) {
1135         if (! (self->mwmhints.decorations & OB_MWM_DECOR_ALL)) {
1136             if (! ((self->mwmhints.decorations & OB_MWM_DECOR_HANDLE) ||
1137                    (self->mwmhints.decorations & OB_MWM_DECOR_TITLE)))
1138                 /* if the mwm hints request no handle or title, then all
1139                    decorations are disabled */
1140                 self->decorations = 0;
1141         }
1142     }
1143
1144     if (self->mwmhints.flags & OB_MWM_FLAG_FUNCTIONS) {
1145         if (! (self->mwmhints.functions & OB_MWM_FUNC_ALL)) {
1146             if (! (self->mwmhints.functions & OB_MWM_FUNC_RESIZE))
1147                 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1148             if (! (self->mwmhints.functions & OB_MWM_FUNC_MOVE))
1149                 self->functions &= ~OB_CLIENT_FUNC_MOVE;
1150             /* dont let mwm hints kill any buttons
1151             if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1152                 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1153             if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1154                 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1155             */
1156             /* dont let mwm hints kill the close button
1157                if (! (self->mwmhints.functions & MwmFunc_Close))
1158                self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1159         }
1160     }
1161
1162     if (!(self->functions & OB_CLIENT_FUNC_SHADE))
1163         self->decorations &= ~OB_FRAME_DECOR_SHADE;
1164     if (!(self->functions & OB_CLIENT_FUNC_ICONIFY))
1165         self->decorations &= ~OB_FRAME_DECOR_ICONIFY;
1166     if (!(self->functions & OB_CLIENT_FUNC_RESIZE))
1167         self->decorations &= ~OB_FRAME_DECOR_GRIPS;
1168
1169     /* can't maximize without moving/resizing */
1170     if (!((self->functions & OB_CLIENT_FUNC_MAXIMIZE) &&
1171           (self->functions & OB_CLIENT_FUNC_MOVE) &&
1172           (self->functions & OB_CLIENT_FUNC_RESIZE))) {
1173         self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1174         self->decorations &= ~OB_FRAME_DECOR_MAXIMIZE;
1175     }
1176
1177     /* kill the handle on fully maxed windows */
1178     if (self->max_vert && self->max_horz)
1179         self->decorations &= ~OB_FRAME_DECOR_HANDLE;
1180
1181     /* finally, the user can have requested no decorations, which overrides
1182        everything */
1183     if (self->undecorated)
1184         self->decorations = OB_FRAME_DECOR_BORDER;
1185
1186     /* if we don't have a titlebar, then we cannot shade! */
1187     if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
1188         self->functions &= ~OB_CLIENT_FUNC_SHADE;
1189
1190     /* now we need to check against rules for the client's current state */
1191     if (self->fullscreen) {
1192         self->functions &= (OB_CLIENT_FUNC_CLOSE |
1193                             OB_CLIENT_FUNC_FULLSCREEN |
1194                             OB_CLIENT_FUNC_ICONIFY);
1195         self->decorations = 0;
1196     }
1197
1198     client_change_allowed_actions(self);
1199
1200     if (self->frame) {
1201         /* adjust the client's decorations, etc. */
1202         client_reconfigure(self);
1203     } else {
1204         /* this makes sure that these windows appear on all desktops */
1205         if (self->type == OB_CLIENT_TYPE_DESKTOP &&
1206             self->desktop != DESKTOP_ALL)
1207         {
1208             self->desktop = DESKTOP_ALL;
1209         }
1210     }
1211 }
1212
1213 static void client_change_allowed_actions(ObClient *self)
1214 {
1215     guint32 actions[9];
1216     int num = 0;
1217
1218     /* desktop windows are kept on all desktops */
1219     if (self->type != OB_CLIENT_TYPE_DESKTOP)
1220         actions[num++] = prop_atoms.net_wm_action_change_desktop;
1221
1222     if (self->functions & OB_CLIENT_FUNC_SHADE)
1223         actions[num++] = prop_atoms.net_wm_action_shade;
1224     if (self->functions & OB_CLIENT_FUNC_CLOSE)
1225         actions[num++] = prop_atoms.net_wm_action_close;
1226     if (self->functions & OB_CLIENT_FUNC_MOVE)
1227         actions[num++] = prop_atoms.net_wm_action_move;
1228     if (self->functions & OB_CLIENT_FUNC_ICONIFY)
1229         actions[num++] = prop_atoms.net_wm_action_minimize;
1230     if (self->functions & OB_CLIENT_FUNC_RESIZE)
1231         actions[num++] = prop_atoms.net_wm_action_resize;
1232     if (self->functions & OB_CLIENT_FUNC_FULLSCREEN)
1233         actions[num++] = prop_atoms.net_wm_action_fullscreen;
1234     if (self->functions & OB_CLIENT_FUNC_MAXIMIZE) {
1235         actions[num++] = prop_atoms.net_wm_action_maximize_horz;
1236         actions[num++] = prop_atoms.net_wm_action_maximize_vert;
1237     }
1238
1239     PROP_SETA32(self->window, net_wm_allowed_actions, atom, actions, num);
1240
1241     /* make sure the window isn't breaking any rules now */
1242
1243     if (!(self->functions & OB_CLIENT_FUNC_SHADE) && self->shaded) {
1244         if (self->frame) client_shade(self, FALSE);
1245         else self->shaded = FALSE;
1246     }
1247     if (!(self->functions & OB_CLIENT_FUNC_ICONIFY) && self->iconic) {
1248         if (self->frame) client_iconify(self, FALSE, TRUE);
1249         else self->iconic = FALSE;
1250     }
1251     if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) && self->fullscreen) {
1252         if (self->frame) client_fullscreen(self, FALSE, TRUE);
1253         else self->fullscreen = FALSE;
1254     }
1255     if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && (self->max_horz ||
1256                                                          self->max_vert)) {
1257         if (self->frame) client_maximize(self, FALSE, 0, TRUE);
1258         else self->max_vert = self->max_horz = FALSE;
1259     }
1260 }
1261
1262 void client_reconfigure(ObClient *self)
1263 {
1264     /* by making this pass FALSE for user, we avoid the emacs event storm where
1265        every configurenotify causes an update in its normal hints, i think this
1266        is generally what we want anyways... */
1267     client_configure(self, OB_CORNER_TOPLEFT, self->area.x, self->area.y,
1268                      self->area.width, self->area.height, FALSE, TRUE);
1269 }
1270
1271 void client_update_wmhints(ObClient *self)
1272 {
1273     XWMHints *hints;
1274     gboolean ur = FALSE;
1275     GSList *it;
1276
1277     /* assume a window takes input if it doesnt specify */
1278     self->can_focus = TRUE;
1279   
1280     if ((hints = XGetWMHints(ob_display, self->window)) != NULL) {
1281         if (hints->flags & InputHint)
1282             self->can_focus = hints->input;
1283
1284         /* only do this when first managing the window *AND* when we aren't
1285            starting up! */
1286         if (ob_state() != OB_STATE_STARTING && self->frame == NULL)
1287             if (hints->flags & StateHint)
1288                 self->iconic = hints->initial_state == IconicState;
1289
1290         if (hints->flags & XUrgencyHint)
1291             ur = TRUE;
1292
1293         if (!(hints->flags & WindowGroupHint))
1294             hints->window_group = None;
1295
1296         /* did the group state change? */
1297         if (hints->window_group !=
1298             (self->group ? self->group->leader : None)) {
1299             /* remove from the old group if there was one */
1300             if (self->group != NULL) {
1301                 /* remove transients of the group */
1302                 for (it = self->group->members; it; it = it->next)
1303                     self->transients = g_slist_remove(self->transients,
1304                                                       it->data);
1305
1306                 /* remove myself from parents in the group */
1307                 if (self->transient_for == OB_TRAN_GROUP) {
1308                     for (it = self->group->members; it; it = it->next) {
1309                         ObClient *c = it->data;
1310
1311                         if (c != self && !c->transient_for)
1312                             c->transients = g_slist_remove(c->transients,
1313                                                            self);
1314                     }
1315                 }
1316
1317                 group_remove(self->group, self);
1318                 self->group = NULL;
1319             }
1320             if (hints->window_group != None) {
1321                 self->group = group_add(hints->window_group, self);
1322
1323                 /* i can only have transients from the group if i am not
1324                    transient myself */
1325                 if (!self->transient_for) {
1326                     /* add other transients of the group that are already
1327                        set up */
1328                     for (it = self->group->members; it; it = it->next) {
1329                         ObClient *c = it->data;
1330                         if (c != self && c->transient_for == OB_TRAN_GROUP)
1331                             self->transients =
1332                                 g_slist_append(self->transients, c);
1333                     }
1334                 }
1335             }
1336
1337             /* because the self->transient flag wont change from this call,
1338                we don't need to update the window's type and such, only its
1339                transient_for, and the transients lists of other windows in
1340                the group may be affected */
1341             client_update_transient_for(self);
1342         }
1343
1344         /* the WM_HINTS can contain an icon */
1345         client_update_icons(self);
1346
1347         XFree(hints);
1348     }
1349
1350     if (ur != self->urgent) {
1351         self->urgent = ur;
1352         /* fire the urgent callback if we're mapped, otherwise, wait until
1353            after we're mapped */
1354         if (self->frame)
1355             client_urgent_notify(self);
1356     }
1357 }
1358
1359 void client_update_title(ObClient *self)
1360 {
1361     GList *it;
1362     guint32 nums;
1363     guint i;
1364     gchar *data = NULL;
1365     gboolean read_title;
1366     gchar *old_title;
1367
1368     old_title = self->title;
1369      
1370     /* try netwm */
1371     if (!PROP_GETS(self->window, net_wm_name, utf8, &data))
1372         /* try old x stuff */
1373         if (!PROP_GETS(self->window, wm_name, locale, &data))
1374             data = g_strdup("Unnamed Window");
1375
1376     /* did the title change? then reset the title_count */
1377     if (old_title && 0 != strncmp(old_title, data, strlen(data)))
1378         self->title_count = 1;
1379
1380     /* look for duplicates and append a number */
1381     nums = 0;
1382     for (it = client_list; it; it = it->next)
1383         if (it->data != self) {
1384             ObClient *c = it->data;
1385             if (0 == strncmp(c->title, data, strlen(data)))
1386                 nums |= 1 << c->title_count;
1387         }
1388     /* find first free number */
1389     for (i = 1; i <= 32; ++i)
1390         if (!(nums & (1 << i))) {
1391             if (self->title_count == 1 || i == 1)
1392                 self->title_count = i;
1393             break;
1394         }
1395     /* dont display the number for the first window */
1396     if (self->title_count > 1) {
1397         char *ndata;
1398         ndata = g_strdup_printf("%s - [%u]", data, self->title_count);
1399         g_free(data);
1400         data = ndata;
1401     }
1402
1403     PROP_SETS(self->window, net_wm_visible_name, data);
1404
1405     self->title = data;
1406
1407     if (self->frame)
1408         frame_adjust_title(self->frame);
1409
1410     g_free(old_title);
1411
1412     /* update the icon title */
1413     data = NULL;
1414     g_free(self->icon_title);
1415
1416     read_title = TRUE;
1417     /* try netwm */
1418     if (!PROP_GETS(self->window, net_wm_icon_name, utf8, &data))
1419         /* try old x stuff */
1420         if (!PROP_GETS(self->window, wm_icon_name, locale, &data)) {
1421             data = g_strdup(self->title);
1422             read_title = FALSE;
1423         }
1424
1425     /* append the title count, dont display the number for the first window */
1426     if (read_title && self->title_count > 1) {
1427         char *vdata, *ndata;
1428         ndata = g_strdup_printf(" - [%u]", self->title_count);
1429         vdata = g_strconcat(data, ndata, NULL);
1430         g_free(ndata);
1431         g_free(data);
1432         data = vdata;
1433     }
1434
1435     PROP_SETS(self->window, net_wm_visible_icon_name, data);
1436
1437     self->icon_title = data;
1438 }
1439
1440 void client_update_class(ObClient *self)
1441 {
1442     char **data;
1443     char *s;
1444
1445     if (self->name) g_free(self->name);
1446     if (self->class) g_free(self->class);
1447     if (self->role) g_free(self->role);
1448
1449     self->name = self->class = self->role = NULL;
1450
1451     if (PROP_GETSS(self->window, wm_class, locale, &data)) {
1452         if (data[0]) {
1453             self->name = g_strdup(data[0]);
1454             if (data[1])
1455                 self->class = g_strdup(data[1]);
1456         }
1457         g_strfreev(data);     
1458     }
1459
1460     if (PROP_GETS(self->window, wm_window_role, locale, &s))
1461         self->role = s;
1462
1463     if (self->name == NULL) self->name = g_strdup("");
1464     if (self->class == NULL) self->class = g_strdup("");
1465     if (self->role == NULL) self->role = g_strdup("");
1466 }
1467
1468 void client_update_strut(ObClient *self)
1469 {
1470     guint num;
1471     guint32 *data;
1472     gboolean got = FALSE;
1473     StrutPartial strut;
1474
1475     if (PROP_GETA32(self->window, net_wm_strut_partial, cardinal,
1476                     &data, &num)) {
1477         if (num == 12) {
1478             got = TRUE;
1479             STRUT_PARTIAL_SET(strut,
1480                               data[0], data[2], data[1], data[3],
1481                               data[4], data[5], data[8], data[9],
1482                               data[6], data[7], data[10], data[11]);
1483         }
1484         g_free(data);
1485     }
1486
1487     if (!got &&
1488         PROP_GETA32(self->window, net_wm_strut, cardinal, &data, &num)) {
1489         if (num == 4) {
1490             got = TRUE;
1491             STRUT_PARTIAL_SET(strut,
1492                               data[0], data[2], data[1], data[3],
1493                               0, 0, 0, 0, 0, 0, 0, 0);
1494         }
1495         g_free(data);
1496     }
1497
1498     if (!got)
1499         STRUT_PARTIAL_SET(strut, 0, 0, 0, 0,
1500                           0, 0, 0, 0, 0, 0, 0, 0);
1501
1502     if (!STRUT_EQUAL(strut, self->strut)) {
1503         self->strut = strut;
1504
1505         /* updating here is pointless while we're being mapped cuz we're not in
1506            the client list yet */
1507         if (self->frame)
1508             screen_update_areas();
1509     }
1510 }
1511
1512 void client_update_icons(ObClient *self)
1513 {
1514     guint num;
1515     guint32 *data;
1516     guint w, h, i, j;
1517
1518     for (i = 0; i < self->nicons; ++i)
1519         g_free(self->icons[i].data);
1520     if (self->nicons > 0)
1521         g_free(self->icons);
1522     self->nicons = 0;
1523
1524     if (PROP_GETA32(self->window, net_wm_icon, cardinal, &data, &num)) {
1525         /* figure out how many valid icons are in here */
1526         i = 0;
1527         while (num - i > 2) {
1528             w = data[i++];
1529             h = data[i++];
1530             i += w * h;
1531             if (i > num || w*h == 0) break;
1532             ++self->nicons;
1533         }
1534
1535         self->icons = g_new(ObClientIcon, self->nicons);
1536     
1537         /* store the icons */
1538         i = 0;
1539         for (j = 0; j < self->nicons; ++j) {
1540             guint x, y, t;
1541
1542             w = self->icons[j].width = data[i++];
1543             h = self->icons[j].height = data[i++];
1544
1545             if (w*h == 0) continue;
1546
1547             self->icons[j].data = g_new(RrPixel32, w * h);
1548             for (x = 0, y = 0, t = 0; t < w * h; ++t, ++x, ++i) {
1549                 if (x >= w) {
1550                     x = 0;
1551                     ++y;
1552                 }
1553                 self->icons[j].data[t] =
1554                     (((data[i] >> 24) & 0xff) << RrDefaultAlphaOffset) +
1555                     (((data[i] >> 16) & 0xff) << RrDefaultRedOffset) +
1556                     (((data[i] >> 8) & 0xff) << RrDefaultGreenOffset) +
1557                     (((data[i] >> 0) & 0xff) << RrDefaultBlueOffset);
1558             }
1559             g_assert(i <= num);
1560         }
1561
1562         g_free(data);
1563     } else if (PROP_GETA32(self->window, kwm_win_icon,
1564                            kwm_win_icon, &data, &num)) {
1565         if (num == 2) {
1566             self->nicons++;
1567             self->icons = g_new(ObClientIcon, self->nicons);
1568             xerror_set_ignore(TRUE);
1569             if (!RrPixmapToRGBA(ob_rr_inst,
1570                                 data[0], data[1],
1571                                 &self->icons[self->nicons-1].width,
1572                                 &self->icons[self->nicons-1].height,
1573                                 &self->icons[self->nicons-1].data)) {
1574                 g_free(&self->icons[self->nicons-1]);
1575                 self->nicons--;
1576             }
1577             xerror_set_ignore(FALSE);
1578         }
1579         g_free(data);
1580     } else {
1581         XWMHints *hints;
1582
1583         if ((hints = XGetWMHints(ob_display, self->window))) {
1584             if (hints->flags & IconPixmapHint) {
1585                 self->nicons++;
1586                 self->icons = g_new(ObClientIcon, self->nicons);
1587                 xerror_set_ignore(TRUE);
1588                 if (!RrPixmapToRGBA(ob_rr_inst,
1589                                     hints->icon_pixmap,
1590                                     (hints->flags & IconMaskHint ?
1591                                      hints->icon_mask : None),
1592                                     &self->icons[self->nicons-1].width,
1593                                     &self->icons[self->nicons-1].height,
1594                                     &self->icons[self->nicons-1].data)){
1595                     g_free(&self->icons[self->nicons-1]);
1596                     self->nicons--;
1597                 }
1598                 xerror_set_ignore(FALSE);
1599             }
1600             XFree(hints);
1601         }
1602     }
1603
1604     if (self->frame)
1605         frame_adjust_icon(self->frame);
1606 }
1607
1608 static void client_change_state(ObClient *self)
1609 {
1610     guint32 state[2];
1611     guint32 netstate[11];
1612     guint num;
1613
1614     state[0] = self->wmstate;
1615     state[1] = None;
1616     PROP_SETA32(self->window, wm_state, wm_state, state, 2);
1617
1618     num = 0;
1619     if (self->modal)
1620         netstate[num++] = prop_atoms.net_wm_state_modal;
1621     if (self->shaded)
1622         netstate[num++] = prop_atoms.net_wm_state_shaded;
1623     if (self->iconic)
1624         netstate[num++] = prop_atoms.net_wm_state_hidden;
1625     if (self->skip_taskbar)
1626         netstate[num++] = prop_atoms.net_wm_state_skip_taskbar;
1627     if (self->skip_pager)
1628         netstate[num++] = prop_atoms.net_wm_state_skip_pager;
1629     if (self->fullscreen)
1630         netstate[num++] = prop_atoms.net_wm_state_fullscreen;
1631     if (self->max_vert)
1632         netstate[num++] = prop_atoms.net_wm_state_maximized_vert;
1633     if (self->max_horz)
1634         netstate[num++] = prop_atoms.net_wm_state_maximized_horz;
1635     if (self->above)
1636         netstate[num++] = prop_atoms.net_wm_state_above;
1637     if (self->below)
1638         netstate[num++] = prop_atoms.net_wm_state_below;
1639     if (self->undecorated)
1640         netstate[num++] = prop_atoms.ob_wm_state_undecorated;
1641     PROP_SETA32(self->window, net_wm_state, atom, netstate, num);
1642
1643     client_calc_layer(self);
1644
1645     if (self->frame)
1646         frame_adjust_state(self->frame);
1647 }
1648
1649 ObClient *client_search_focus_tree(ObClient *self)
1650 {
1651     GSList *it;
1652     ObClient *ret;
1653
1654     for (it = self->transients; it != NULL; it = it->next) {
1655         if (client_focused(it->data)) return it->data;
1656         if ((ret = client_search_focus_tree(it->data))) return ret;
1657     }
1658     return NULL;
1659 }
1660
1661 ObClient *client_search_focus_tree_full(ObClient *self)
1662 {
1663     if (self->transient_for) {
1664         if (self->transient_for != OB_TRAN_GROUP) {
1665             return client_search_focus_tree_full(self->transient_for);
1666         } else {
1667             GSList *it;
1668             gboolean recursed = FALSE;
1669         
1670             for (it = self->group->members; it; it = it->next)
1671                 if (!((ObClient*)it->data)->transient_for) {
1672                     ObClient *c;
1673                     if ((c = client_search_focus_tree_full(it->data)))
1674                         return c;
1675                     recursed = TRUE;
1676                 }
1677             if (recursed)
1678               return NULL;
1679         }
1680     }
1681
1682     /* this function checks the whole tree, the client_search_focus_tree~
1683        does not, so we need to check this window */
1684     if (client_focused(self))
1685       return self;
1686     return client_search_focus_tree(self);
1687 }
1688
1689 static ObStackingLayer calc_layer(ObClient *self)
1690 {
1691     ObStackingLayer l;
1692
1693     if (self->fullscreen &&
1694         (client_focused(self) || client_search_focus_tree(self)))
1695         l = OB_STACKING_LAYER_FULLSCREEN;
1696     else if (self->type == OB_CLIENT_TYPE_DESKTOP)
1697         l = OB_STACKING_LAYER_DESKTOP;
1698     else if (self->type == OB_CLIENT_TYPE_DOCK) {
1699         if (!self->below) l = OB_STACKING_LAYER_TOP;
1700         else l = OB_STACKING_LAYER_NORMAL;
1701     }
1702     else if (self->above) l = OB_STACKING_LAYER_ABOVE;
1703     else if (self->below) l = OB_STACKING_LAYER_BELOW;
1704     else l = OB_STACKING_LAYER_NORMAL;
1705
1706     return l;
1707 }
1708
1709 static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
1710                                         ObStackingLayer l, gboolean raised)
1711 {
1712     ObStackingLayer old, own;
1713     GSList *it;
1714
1715     old = self->layer;
1716     own = calc_layer(self);
1717     self->layer = l > own ? l : own;
1718
1719     for (it = self->transients; it; it = it->next)
1720         client_calc_layer_recursive(it->data, orig,
1721                                     l, raised ? raised : l != old);
1722
1723     if (!raised && l != old)
1724         if (orig->frame) { /* only restack if the original window is managed */
1725             /* XXX add_non_intrusive ever? */
1726             stacking_remove(CLIENT_AS_WINDOW(self));
1727             stacking_add(CLIENT_AS_WINDOW(self));
1728         }
1729 }
1730
1731 void client_calc_layer(ObClient *self)
1732 {
1733     ObStackingLayer l;
1734     ObClient *orig;
1735
1736     orig = self;
1737
1738     /* transients take on the layer of their parents */
1739     self = client_search_top_transient(self);
1740
1741     l = calc_layer(self);
1742
1743     client_calc_layer_recursive(self, orig, l, FALSE);
1744 }
1745
1746 gboolean client_should_show(ObClient *self)
1747 {
1748     if (self->iconic) return FALSE;
1749     else if (!(self->desktop == screen_desktop ||
1750                self->desktop == DESKTOP_ALL)) return FALSE;
1751     else if (client_normal(self) && screen_showing_desktop) return FALSE;
1752     
1753     return TRUE;
1754 }
1755
1756 static void client_showhide(ObClient *self)
1757 {
1758
1759     if (client_should_show(self))
1760         frame_show(self->frame);
1761     else
1762         frame_hide(self->frame);
1763 }
1764
1765 gboolean client_normal(ObClient *self) {
1766     return ! (self->type == OB_CLIENT_TYPE_DESKTOP ||
1767               self->type == OB_CLIENT_TYPE_DOCK ||
1768               self->type == OB_CLIENT_TYPE_SPLASH);
1769 }
1770
1771 static void client_apply_startup_state(ObClient *self)
1772 {
1773     /* these are in a carefully crafted order.. */
1774
1775     if (self->iconic) {
1776         self->iconic = FALSE;
1777         client_iconify(self, TRUE, FALSE);
1778     }
1779     if (self->fullscreen) {
1780         self->fullscreen = FALSE;
1781         client_fullscreen(self, TRUE, FALSE);
1782     }
1783     if (self->undecorated) {
1784         self->undecorated = FALSE;
1785         client_set_undecorated(self, TRUE);
1786     }
1787     if (self->shaded) {
1788         self->shaded = FALSE;
1789         client_shade(self, TRUE);
1790     }
1791     if (self->urgent)
1792         client_urgent_notify(self);
1793   
1794     if (self->max_vert && self->max_horz) {
1795         self->max_vert = self->max_horz = FALSE;
1796         client_maximize(self, TRUE, 0, FALSE);
1797     } else if (self->max_vert) {
1798         self->max_vert = FALSE;
1799         client_maximize(self, TRUE, 2, FALSE);
1800     } else if (self->max_horz) {
1801         self->max_horz = FALSE;
1802         client_maximize(self, TRUE, 1, FALSE);
1803     }
1804
1805     /* nothing to do for the other states:
1806        skip_taskbar
1807        skip_pager
1808        modal
1809        above
1810        below
1811     */
1812 }
1813
1814 void client_configure_full(ObClient *self, ObCorner anchor,
1815                            int x, int y, int w, int h,
1816                            gboolean user, gboolean final,
1817                            gboolean force_reply)
1818 {
1819     gint oldw, oldh;
1820     gboolean send_resize_client;
1821     gboolean moved = FALSE, resized = FALSE;
1822     guint fdecor = self->frame->decorations;
1823     gboolean fhorz = self->frame->max_horz;
1824
1825     /* make the frame recalculate its dimentions n shit without changing
1826        anything visible for real, this way the constraints below can work with
1827        the updated frame dimensions. */
1828     frame_adjust_area(self->frame, TRUE, TRUE, TRUE);
1829
1830     /* gets the frame's position */
1831     frame_client_gravity(self->frame, &x, &y);
1832
1833     /* these positions are frame positions, not client positions */
1834
1835     /* set the size and position if fullscreen */
1836     if (self->fullscreen) {
1837 #ifdef VIDMODE
1838         int dot;
1839         XF86VidModeModeLine mode;
1840 #endif
1841         Rect *a;
1842         guint i;
1843
1844         i = client_monitor(self);
1845         a = screen_physical_area_monitor(i);
1846
1847 #ifdef VIDMODE
1848         if (i == 0 && /* primary head */
1849             extensions_vidmode &&
1850             XF86VidModeGetViewPort(ob_display, ob_screen, &x, &y) &&
1851             /* get the mode last so the mode.privsize isnt freed incorrectly */
1852             XF86VidModeGetModeLine(ob_display, ob_screen, &dot, &mode)) {
1853             x += a->x;
1854             y += a->y;
1855             w = mode.hdisplay;
1856             h = mode.vdisplay;
1857             if (mode.privsize) XFree(mode.private);
1858         } else
1859 #endif
1860         {
1861             x = a->x;
1862             y = a->y;
1863             w = a->width;
1864             h = a->height;
1865         }
1866
1867         user = FALSE; /* ignore that increment etc shit when in fullscreen */
1868     } else {
1869         Rect *a;
1870
1871         a = screen_area_monitor(self->desktop, client_monitor(self));
1872
1873         /* set the size and position if maximized */
1874         if (self->max_horz) {
1875             x = a->x;
1876             w = a->width - self->frame->size.left - self->frame->size.right;
1877         }
1878         if (self->max_vert) {
1879             y = a->y;
1880             h = a->height - self->frame->size.top - self->frame->size.bottom;
1881         }
1882     }
1883
1884     /* gets the client's position */
1885     frame_frame_gravity(self->frame, &x, &y);
1886
1887     /* these override the above states! if you cant move you can't move! */
1888     if (user) {
1889         if (!(self->functions & OB_CLIENT_FUNC_MOVE)) {
1890             x = self->area.x;
1891             y = self->area.y;
1892         }
1893         if (!(self->functions & OB_CLIENT_FUNC_RESIZE)) {
1894             w = self->area.width;
1895             h = self->area.height;
1896         }
1897     }
1898
1899     if (!(w == self->area.width && h == self->area.height)) {
1900         int basew, baseh, minw, minh;
1901
1902         /* base size is substituted with min size if not specified */
1903         if (self->base_size.width || self->base_size.height) {
1904             basew = self->base_size.width;
1905             baseh = self->base_size.height;
1906         } else {
1907             basew = self->min_size.width;
1908             baseh = self->min_size.height;
1909         }
1910         /* min size is substituted with base size if not specified */
1911         if (self->min_size.width || self->min_size.height) {
1912             minw = self->min_size.width;
1913             minh = self->min_size.height;
1914         } else {
1915             minw = self->base_size.width;
1916             minh = self->base_size.height;
1917         }
1918
1919         /* if this is a user-requested resize, then check against min/max
1920            sizes */
1921
1922         /* smaller than min size or bigger than max size? */
1923         if (w > self->max_size.width) w = self->max_size.width;
1924         if (w < minw) w = minw;
1925         if (h > self->max_size.height) h = self->max_size.height;
1926         if (h < minh) h = minh;
1927
1928         w -= basew;
1929         h -= baseh;
1930
1931         /* keep to the increments */
1932         w /= self->size_inc.width;
1933         h /= self->size_inc.height;
1934
1935         /* you cannot resize to nothing */
1936         if (basew + w < 1) w = 1 - basew;
1937         if (baseh + h < 1) h = 1 - baseh;
1938   
1939         /* store the logical size */
1940         SIZE_SET(self->logical_size,
1941                  self->size_inc.width > 1 ? w : w + basew,
1942                  self->size_inc.height > 1 ? h : h + baseh);
1943
1944         w *= self->size_inc.width;
1945         h *= self->size_inc.height;
1946
1947         w += basew;
1948         h += baseh;
1949
1950         /* adjust the height to match the width for the aspect ratios.
1951            for this, min size is not substituted for base size ever. */
1952         w -= self->base_size.width;
1953         h -= self->base_size.height;
1954
1955         if (self->min_ratio)
1956             if (h * self->min_ratio > w) h = (int)(w / self->min_ratio);
1957         if (self->max_ratio)
1958             if (h * self->max_ratio < w) h = (int)(w / self->max_ratio);
1959
1960         w += self->base_size.width;
1961         h += self->base_size.height;
1962     }
1963
1964     switch (anchor) {
1965     case OB_CORNER_TOPLEFT:
1966         break;
1967     case OB_CORNER_TOPRIGHT:
1968         x -= w - self->area.width;
1969         break;
1970     case OB_CORNER_BOTTOMLEFT:
1971         y -= h - self->area.height;
1972         break;
1973     case OB_CORNER_BOTTOMRIGHT:
1974         x -= w - self->area.width;
1975         y -= h - self->area.height;
1976         break;
1977     }
1978
1979     moved = x != self->area.x || y != self->area.y;
1980     resized = w != self->area.width || h != self->area.height;
1981
1982     oldw = self->area.width;
1983     oldh = self->area.height;
1984     RECT_SET(self->area, x, y, w, h);
1985
1986     /* for app-requested resizes, always resize if 'resized' is true.
1987        for user-requested ones, only resize if final is true, or when
1988        resizing in redraw mode */
1989     send_resize_client = ((!user && resized) ||
1990                           (user && (final ||
1991                                     (resized && config_redraw_resize))));
1992
1993     /* if the client is enlarging, the resize the client before the frame */
1994     if (send_resize_client && user && (w > oldw || h > oldh))
1995         XResizeWindow(ob_display, self->window, MAX(w, oldw), MAX(h, oldh));
1996
1997     /* move/resize the frame to match the request */
1998     if (self->frame) {
1999         if (self->decorations != fdecor || self->max_horz != fhorz)
2000             moved = resized = TRUE;
2001
2002         if (moved || resized)
2003             frame_adjust_area(self->frame, moved, resized, FALSE);
2004
2005         if (!resized && (force_reply || ((!user && moved) || (user && final))))
2006         {
2007             XEvent event;
2008             event.type = ConfigureNotify;
2009             event.xconfigure.display = ob_display;
2010             event.xconfigure.event = self->window;
2011             event.xconfigure.window = self->window;
2012
2013             /* root window real coords */
2014             event.xconfigure.x = self->frame->area.x + self->frame->size.left -
2015                 self->border_width;
2016             event.xconfigure.y = self->frame->area.y + self->frame->size.top -
2017                 self->border_width;
2018             event.xconfigure.width = w;
2019             event.xconfigure.height = h;
2020             event.xconfigure.border_width = 0;
2021             event.xconfigure.above = self->frame->plate;
2022             event.xconfigure.override_redirect = FALSE;
2023             XSendEvent(event.xconfigure.display, event.xconfigure.window,
2024                        FALSE, StructureNotifyMask, &event);
2025         }
2026     }
2027
2028     /* if the client is shrinking, then resize the frame before the client */
2029     if (send_resize_client && (!user || (w <= oldw || h <= oldh)))
2030         XResizeWindow(ob_display, self->window, w, h);
2031
2032     XFlush(ob_display);
2033 }
2034
2035 void client_fullscreen(ObClient *self, gboolean fs, gboolean savearea)
2036 {
2037     int x, y, w, h;
2038
2039     if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) || /* can't */
2040         self->fullscreen == fs) return;                   /* already done */
2041
2042     self->fullscreen = fs;
2043     client_change_state(self); /* change the state hints on the client,
2044                                   and adjust out layer/stacking */
2045
2046     if (fs) {
2047         if (savearea)
2048             self->pre_fullscreen_area = self->area;
2049
2050         /* these are not actually used cuz client_configure will set them
2051            as appropriate when the window is fullscreened */
2052         x = y = w = h = 0;
2053     } else {
2054         Rect *a;
2055
2056         if (self->pre_fullscreen_area.width > 0 &&
2057             self->pre_fullscreen_area.height > 0)
2058         {
2059             x = self->pre_fullscreen_area.x;
2060             y = self->pre_fullscreen_area.y;
2061             w = self->pre_fullscreen_area.width;
2062             h = self->pre_fullscreen_area.height;
2063             RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0);
2064         } else {
2065             /* pick some fallbacks... */
2066             a = screen_area_monitor(self->desktop, 0);
2067             x = a->x + a->width / 4;
2068             y = a->y + a->height / 4;
2069             w = a->width / 2;
2070             h = a->height / 2;
2071         }
2072     }
2073
2074     client_setup_decor_and_functions(self);
2075
2076     client_move_resize(self, x, y, w, h);
2077
2078     /* try focus us when we go into fullscreen mode */
2079     client_focus(self);
2080 }
2081
2082 static void client_iconify_recursive(ObClient *self,
2083                                      gboolean iconic, gboolean curdesk)
2084 {
2085     GSList *it;
2086     gboolean changed = FALSE;
2087
2088
2089     if (self->iconic != iconic) {
2090         ob_debug("%sconifying window: 0x%lx\n", (iconic ? "I" : "Uni"),
2091                  self->window);
2092
2093         self->iconic = iconic;
2094
2095         if (iconic) {
2096             if (self->functions & OB_CLIENT_FUNC_ICONIFY) {
2097                 long old;
2098
2099                 old = self->wmstate;
2100                 self->wmstate = IconicState;
2101                 if (old != self->wmstate)
2102                     PROP_MSG(self->window, kde_wm_change_state,
2103                              self->wmstate, 1, 0, 0);
2104
2105                 self->ignore_unmaps++;
2106                 /* we unmap the client itself so that we can get MapRequest
2107                    events, and because the ICCCM tells us to! */
2108                 XUnmapWindow(ob_display, self->window);
2109
2110                 /* update the focus lists.. iconic windows go to the bottom of
2111                    the list, put the new iconic window at the 'top of the
2112                    bottom'. */
2113                 focus_order_to_top(self);
2114
2115                 changed = TRUE;
2116             }
2117         } else {
2118             long old;
2119
2120             if (curdesk)
2121                 client_set_desktop(self, screen_desktop, FALSE);
2122
2123             old = self->wmstate;
2124             self->wmstate = self->shaded ? IconicState : NormalState;
2125             if (old != self->wmstate)
2126                 PROP_MSG(self->window, kde_wm_change_state,
2127                          self->wmstate, 1, 0, 0);
2128
2129             XMapWindow(ob_display, self->window);
2130
2131             /* this puts it after the current focused window */
2132             focus_order_remove(self);
2133             focus_order_add_new(self);
2134
2135             /* this is here cuz with the VIDMODE extension, the viewport can
2136                change while a fullscreen window is iconic, and when it
2137                uniconifies, it would be nice if it did so to the new position
2138                of the viewport */
2139             client_reconfigure(self);
2140
2141             changed = TRUE;
2142         }
2143     }
2144
2145     if (changed) {
2146         client_change_state(self);
2147         client_showhide(self);
2148         screen_update_areas();
2149     }
2150
2151     /* iconify all transients */
2152     for (it = self->transients; it != NULL; it = it->next)
2153         if (it->data != self) client_iconify_recursive(it->data,
2154                                                        iconic, curdesk);
2155 }
2156
2157 void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk)
2158 {
2159     /* move up the transient chain as far as possible first */
2160     self = client_search_top_transient(self);
2161
2162     client_iconify_recursive(client_search_top_transient(self),
2163                              iconic, curdesk);
2164 }
2165
2166 void client_maximize(ObClient *self, gboolean max, int dir, gboolean savearea)
2167 {
2168     int x, y, w, h;
2169      
2170     g_assert(dir == 0 || dir == 1 || dir == 2);
2171     if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE)) return; /* can't */
2172
2173     /* check if already done */
2174     if (max) {
2175         if (dir == 0 && self->max_horz && self->max_vert) return;
2176         if (dir == 1 && self->max_horz) return;
2177         if (dir == 2 && self->max_vert) return;
2178     } else {
2179         if (dir == 0 && !self->max_horz && !self->max_vert) return;
2180         if (dir == 1 && !self->max_horz) return;
2181         if (dir == 2 && !self->max_vert) return;
2182     }
2183
2184     /* we just tell it to configure in the same place and client_configure
2185        worries about filling the screen with the window */
2186     x = self->area.x;
2187     y = self->area.y;
2188     w = self->area.width;
2189     h = self->area.height;
2190
2191     if (max) {
2192         if (savearea)
2193             self->pre_max_area = self->area;
2194     } else {
2195         Rect *a;
2196
2197         a = screen_area_monitor(self->desktop, 0);
2198         if ((dir == 0 || dir == 1) && self->max_horz) { /* horz */
2199             if (self->pre_max_area.width > 0) {
2200                 x = self->pre_max_area.x;
2201                 w = self->pre_max_area.width;
2202
2203                 RECT_SET(self->pre_max_area, 0, self->pre_max_area.y,
2204                          0, self->pre_max_area.height);
2205             } else {
2206                 /* pick some fallbacks... */
2207                 x = a->x + a->width / 4;
2208                 w = a->width / 2;
2209             }
2210         }
2211         if ((dir == 0 || dir == 2) && self->max_vert) { /* vert */
2212             if (self->pre_max_area.height > 0) {
2213                 y = self->pre_max_area.y;
2214                 h = self->pre_max_area.height;
2215
2216                 RECT_SET(self->pre_max_area, self->pre_max_area.x, 0,
2217                          self->pre_max_area.width, 0);
2218             } else {
2219                 /* pick some fallbacks... */
2220                 y = a->y + a->height / 4;
2221                 h = a->height / 2;
2222             }
2223         }
2224     }
2225
2226     if (dir == 0 || dir == 1) /* horz */
2227         self->max_horz = max;
2228     if (dir == 0 || dir == 2) /* vert */
2229         self->max_vert = max;
2230
2231     client_change_state(self); /* change the state hints on the client */
2232
2233     client_setup_decor_and_functions(self);
2234
2235     client_move_resize(self, x, y, w, h);
2236 }
2237
2238 void client_shade(ObClient *self, gboolean shade)
2239 {
2240     if ((!(self->functions & OB_CLIENT_FUNC_SHADE) &&
2241          shade) ||                         /* can't shade */
2242         self->shaded == shade) return;     /* already done */
2243
2244     /* when we're iconic, don't change the wmstate */
2245     if (!self->iconic) {
2246         long old;
2247
2248         old = self->wmstate;
2249         self->wmstate = shade ? IconicState : NormalState;
2250         if (old != self->wmstate)
2251             PROP_MSG(self->window, kde_wm_change_state,
2252                      self->wmstate, 1, 0, 0);
2253     }
2254
2255     self->shaded = shade;
2256     client_change_state(self);
2257     /* resize the frame to just the titlebar */
2258     frame_adjust_area(self->frame, FALSE, FALSE, FALSE);
2259 }
2260
2261 void client_close(ObClient *self)
2262 {
2263     XEvent ce;
2264
2265     if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return;
2266
2267     /*
2268       XXX: itd be cool to do timeouts and shit here for killing the client's
2269       process off
2270       like... if the window is around after 5 seconds, then the close button
2271       turns a nice red, and if this function is called again, the client is
2272       explicitly killed.
2273     */
2274
2275     ce.xclient.type = ClientMessage;
2276     ce.xclient.message_type =  prop_atoms.wm_protocols;
2277     ce.xclient.display = ob_display;
2278     ce.xclient.window = self->window;
2279     ce.xclient.format = 32;
2280     ce.xclient.data.l[0] = prop_atoms.wm_delete_window;
2281     ce.xclient.data.l[1] = event_lasttime;
2282     ce.xclient.data.l[2] = 0l;
2283     ce.xclient.data.l[3] = 0l;
2284     ce.xclient.data.l[4] = 0l;
2285     XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
2286 }
2287
2288 void client_kill(ObClient *self)
2289 {
2290     XKillClient(ob_display, self->window);
2291 }
2292
2293 void client_set_desktop_recursive(ObClient *self,
2294                                   guint target, gboolean donthide)
2295 {
2296     guint old;
2297     GSList *it;
2298
2299     if (target != self->desktop) {
2300
2301         ob_debug("Setting desktop %u\n", target+1);
2302
2303         g_assert(target < screen_num_desktops || target == DESKTOP_ALL);
2304
2305         /* remove from the old desktop(s) */
2306         focus_order_remove(self);
2307
2308         old = self->desktop;
2309         self->desktop = target;
2310         PROP_SET32(self->window, net_wm_desktop, cardinal, target);
2311         /* the frame can display the current desktop state */
2312         frame_adjust_state(self->frame);
2313         /* 'move' the window to the new desktop */
2314         if (!donthide)
2315             client_showhide(self);
2316         /* raise if it was not already on the desktop */
2317         if (old != DESKTOP_ALL)
2318             client_raise(self);
2319         screen_update_areas();
2320
2321         /* add to the new desktop(s) */
2322         if (config_focus_new)
2323             focus_order_to_top(self);
2324         else
2325             focus_order_to_bottom(self);
2326     }
2327
2328     /* move all transients */
2329     for (it = self->transients; it != NULL; it = it->next)
2330         if (it->data != self) client_set_desktop_recursive(it->data,
2331                                                            target, donthide);
2332 }
2333
2334 void client_set_desktop(ObClient *self, guint target, gboolean donthide)
2335 {
2336     client_set_desktop_recursive(client_search_top_transient(self),
2337                                  target, donthide);
2338 }
2339
2340 ObClient *client_search_modal_child(ObClient *self)
2341 {
2342     GSList *it;
2343     ObClient *ret;
2344   
2345     for (it = self->transients; it != NULL; it = it->next) {
2346         ObClient *c = it->data;
2347         if ((ret = client_search_modal_child(c))) return ret;
2348         if (c->modal) return c;
2349     }
2350     return NULL;
2351 }
2352
2353 gboolean client_validate(ObClient *self)
2354 {
2355     XEvent e; 
2356
2357     XSync(ob_display, FALSE); /* get all events on the server */
2358
2359     if (XCheckTypedWindowEvent(ob_display, self->window, DestroyNotify, &e) ||
2360         XCheckTypedWindowEvent(ob_display, self->window, UnmapNotify, &e)) {
2361         XPutBackEvent(ob_display, &e);
2362         return FALSE;
2363     }
2364
2365     return TRUE;
2366 }
2367
2368 void client_set_wm_state(ObClient *self, long state)
2369 {
2370     if (state == self->wmstate) return; /* no change */
2371   
2372     switch (state) {
2373     case IconicState:
2374         client_iconify(self, TRUE, TRUE);
2375         break;
2376     case NormalState:
2377         client_iconify(self, FALSE, TRUE);
2378         break;
2379     }
2380 }
2381
2382 void client_set_state(ObClient *self, Atom action, long data1, long data2)
2383 {
2384     gboolean shaded = self->shaded;
2385     gboolean fullscreen = self->fullscreen;
2386     gboolean undecorated = self->undecorated;
2387     gboolean max_horz = self->max_horz;
2388     gboolean max_vert = self->max_vert;
2389     int i;
2390
2391     if (!(action == prop_atoms.net_wm_state_add ||
2392           action == prop_atoms.net_wm_state_remove ||
2393           action == prop_atoms.net_wm_state_toggle))
2394         /* an invalid action was passed to the client message, ignore it */
2395         return; 
2396
2397     for (i = 0; i < 2; ++i) {
2398         Atom state = i == 0 ? data1 : data2;
2399     
2400         if (!state) continue;
2401
2402         /* if toggling, then pick whether we're adding or removing */
2403         if (action == prop_atoms.net_wm_state_toggle) {
2404             if (state == prop_atoms.net_wm_state_modal)
2405                 action = self->modal ? prop_atoms.net_wm_state_remove :
2406                     prop_atoms.net_wm_state_add;
2407             else if (state == prop_atoms.net_wm_state_maximized_vert)
2408                 action = self->max_vert ? prop_atoms.net_wm_state_remove :
2409                     prop_atoms.net_wm_state_add;
2410             else if (state == prop_atoms.net_wm_state_maximized_horz)
2411                 action = self->max_horz ? prop_atoms.net_wm_state_remove :
2412                     prop_atoms.net_wm_state_add;
2413             else if (state == prop_atoms.net_wm_state_shaded)
2414                 action = shaded ? prop_atoms.net_wm_state_remove :
2415                     prop_atoms.net_wm_state_add;
2416             else if (state == prop_atoms.net_wm_state_skip_taskbar)
2417                 action = self->skip_taskbar ?
2418                     prop_atoms.net_wm_state_remove :
2419                     prop_atoms.net_wm_state_add;
2420             else if (state == prop_atoms.net_wm_state_skip_pager)
2421                 action = self->skip_pager ?
2422                     prop_atoms.net_wm_state_remove :
2423                     prop_atoms.net_wm_state_add;
2424             else if (state == prop_atoms.net_wm_state_fullscreen)
2425                 action = fullscreen ?
2426                     prop_atoms.net_wm_state_remove :
2427                     prop_atoms.net_wm_state_add;
2428             else if (state == prop_atoms.net_wm_state_above)
2429                 action = self->above ? prop_atoms.net_wm_state_remove :
2430                     prop_atoms.net_wm_state_add;
2431             else if (state == prop_atoms.net_wm_state_below)
2432                 action = self->below ? prop_atoms.net_wm_state_remove :
2433                     prop_atoms.net_wm_state_add;
2434             else if (state == prop_atoms.ob_wm_state_undecorated)
2435                 action = undecorated ? prop_atoms.net_wm_state_remove :
2436                     prop_atoms.net_wm_state_add;
2437         }
2438     
2439         if (action == prop_atoms.net_wm_state_add) {
2440             if (state == prop_atoms.net_wm_state_modal) {
2441                 /* XXX raise here or something? */
2442                 self->modal = TRUE;
2443             } else if (state == prop_atoms.net_wm_state_maximized_vert) {
2444                 max_vert = TRUE;
2445             } else if (state == prop_atoms.net_wm_state_maximized_horz) {
2446                 max_horz = TRUE;
2447             } else if (state == prop_atoms.net_wm_state_shaded) {
2448                 shaded = TRUE;
2449             } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
2450                 self->skip_taskbar = TRUE;
2451             } else if (state == prop_atoms.net_wm_state_skip_pager) {
2452                 self->skip_pager = TRUE;
2453             } else if (state == prop_atoms.net_wm_state_fullscreen) {
2454                 fullscreen = TRUE;
2455             } else if (state == prop_atoms.net_wm_state_above) {
2456                 self->above = TRUE;
2457             } else if (state == prop_atoms.net_wm_state_below) {
2458                 self->below = TRUE;
2459             } else if (state == prop_atoms.ob_wm_state_undecorated) {
2460                 undecorated = TRUE;
2461             }
2462
2463         } else { /* action == prop_atoms.net_wm_state_remove */
2464             if (state == prop_atoms.net_wm_state_modal) {
2465                 self->modal = FALSE;
2466             } else if (state == prop_atoms.net_wm_state_maximized_vert) {
2467                 max_vert = FALSE;
2468             } else if (state == prop_atoms.net_wm_state_maximized_horz) {
2469                 max_horz = FALSE;
2470             } else if (state == prop_atoms.net_wm_state_shaded) {
2471                 shaded = FALSE;
2472             } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
2473                 self->skip_taskbar = FALSE;
2474             } else if (state == prop_atoms.net_wm_state_skip_pager) {
2475                 self->skip_pager = FALSE;
2476             } else if (state == prop_atoms.net_wm_state_fullscreen) {
2477                 fullscreen = FALSE;
2478             } else if (state == prop_atoms.net_wm_state_above) {
2479                 self->above = FALSE;
2480             } else if (state == prop_atoms.net_wm_state_below) {
2481                 self->below = FALSE;
2482             } else if (state == prop_atoms.ob_wm_state_undecorated) {
2483                 undecorated = FALSE;
2484             }
2485         }
2486     }
2487     if (max_horz != self->max_horz || max_vert != self->max_vert) {
2488         if (max_horz != self->max_horz && max_vert != self->max_vert) {
2489             /* toggling both */
2490             if (max_horz == max_vert) { /* both going the same way */
2491                 client_maximize(self, max_horz, 0, TRUE);
2492             } else {
2493                 client_maximize(self, max_horz, 1, TRUE);
2494                 client_maximize(self, max_vert, 2, TRUE);
2495             }
2496         } else {
2497             /* toggling one */
2498             if (max_horz != self->max_horz)
2499                 client_maximize(self, max_horz, 1, TRUE);
2500             else
2501                 client_maximize(self, max_vert, 2, TRUE);
2502         }
2503     }
2504     /* change fullscreen state before shading, as it will affect if the window
2505        can shade or not */
2506     if (fullscreen != self->fullscreen)
2507         client_fullscreen(self, fullscreen, TRUE);
2508     if (shaded != self->shaded)
2509         client_shade(self, shaded);
2510     if (undecorated != self->undecorated)
2511         client_set_undecorated(self, undecorated);
2512     client_calc_layer(self);
2513     client_change_state(self); /* change the hint to reflect these changes */
2514 }
2515
2516 ObClient *client_focus_target(ObClient *self)
2517 {
2518     ObClient *child;
2519      
2520     /* if we have a modal child, then focus it, not us */
2521     child = client_search_modal_child(client_search_top_transient(self));
2522     if (child) return child;
2523     return self;
2524 }
2525
2526 gboolean client_can_focus(ObClient *self)
2527 {
2528     XEvent ev;
2529
2530     /* choose the correct target */
2531     self = client_focus_target(self);
2532
2533     if (!self->frame->visible)
2534         return FALSE;
2535
2536     if (!((self->can_focus || self->focus_notify) &&
2537           (self->desktop == screen_desktop ||
2538            self->desktop == DESKTOP_ALL) &&
2539           !self->iconic))
2540         return FALSE;
2541
2542     /* do a check to see if the window has already been unmapped or destroyed
2543        do this intelligently while watching out for unmaps we've generated
2544        (ignore_unmaps > 0) */
2545     if (XCheckTypedWindowEvent(ob_display, self->window,
2546                                DestroyNotify, &ev)) {
2547         XPutBackEvent(ob_display, &ev);
2548         return FALSE;
2549     }
2550     while (XCheckTypedWindowEvent(ob_display, self->window,
2551                                   UnmapNotify, &ev)) {
2552         if (self->ignore_unmaps) {
2553             self->ignore_unmaps--;
2554         } else {
2555             XPutBackEvent(ob_display, &ev);
2556             return FALSE;
2557         }
2558     }
2559
2560     return TRUE;
2561 }
2562
2563 gboolean client_focus(ObClient *self)
2564 {
2565     /* choose the correct target */
2566     self = client_focus_target(self);
2567
2568     if (!client_can_focus(self)) {
2569         if (!self->frame->visible) {
2570             /* update the focus lists */
2571             focus_order_to_top(self);
2572         }
2573         return FALSE;
2574     }
2575
2576     if (self->can_focus) {
2577         /* RevertToPointerRoot causes much more headache than RevertToNone, so
2578            I choose to use it always, hopefully to find errors quicker, if any
2579            are left. (I hate X. I hate focus events.)
2580            
2581            Update: Changing this to RevertToNone fixed a bug with mozilla (bug
2582            #799. So now it is RevertToNone again.
2583         */
2584         XSetInputFocus(ob_display, self->window, RevertToNone,
2585                        event_lasttime);
2586     }
2587
2588     if (self->focus_notify) {
2589         XEvent ce;
2590         ce.xclient.type = ClientMessage;
2591         ce.xclient.message_type = prop_atoms.wm_protocols;
2592         ce.xclient.display = ob_display;
2593         ce.xclient.window = self->window;
2594         ce.xclient.format = 32;
2595         ce.xclient.data.l[0] = prop_atoms.wm_take_focus;
2596         ce.xclient.data.l[1] = event_lasttime;
2597         ce.xclient.data.l[2] = 0l;
2598         ce.xclient.data.l[3] = 0l;
2599         ce.xclient.data.l[4] = 0l;
2600         XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
2601     }
2602
2603 #ifdef DEBUG_FOCUS
2604     ob_debug("%sively focusing %lx at %d\n",
2605              (self->can_focus ? "act" : "pass"),
2606              self->window, (int) event_lasttime);
2607 #endif
2608
2609     /* Cause the FocusIn to come back to us. Important for desktop switches,
2610        since otherwise we'll have no FocusIn on the queue and send it off to
2611        the focus_backup. */
2612     XSync(ob_display, FALSE);
2613     return TRUE;
2614 }
2615
2616 void client_unfocus(ObClient *self)
2617 {
2618     if (focus_client == self) {
2619 #ifdef DEBUG_FOCUS
2620         ob_debug("client_unfocus for %lx\n", self->window);
2621 #endif
2622         focus_fallback(OB_FOCUS_FALLBACK_UNFOCUSING);
2623     }
2624 }
2625
2626 void client_activate(ObClient *self, gboolean here)
2627 {
2628     if (client_normal(self) && screen_showing_desktop)
2629         screen_show_desktop(FALSE);
2630     if (self->iconic)
2631         client_iconify(self, FALSE, here);
2632     if (self->desktop != DESKTOP_ALL &&
2633         self->desktop != screen_desktop) {
2634         if (here)
2635             client_set_desktop(self, screen_desktop, FALSE);
2636         else
2637             screen_set_desktop(self->desktop);
2638     } else if (!self->frame->visible)
2639         /* if its not visible for other reasons, then don't mess
2640            with it */
2641         return;
2642     if (self->shaded)
2643         client_shade(self, FALSE);
2644
2645     client_focus(self);
2646
2647     /* we do this an action here. this is rather important. this is because
2648        we want the results from the focus change to take place BEFORE we go
2649        about raising the window. when a fullscreen window loses focus, we need
2650        this or else the raise wont be able to raise above the to-lose-focus
2651        fullscreen window. */
2652     client_raise(self);
2653 }
2654
2655 void client_raise(ObClient *self)
2656 {
2657     action_run_string("Raise", self);
2658 }
2659
2660 void client_lower(ObClient *self)
2661 {
2662     action_run_string("Raise", self);
2663 }
2664
2665 gboolean client_focused(ObClient *self)
2666 {
2667     return self == focus_client;
2668 }
2669
2670 static ObClientIcon* client_icon_recursive(ObClient *self, int w, int h)
2671 {
2672     guint i;
2673     /* si is the smallest image >= req */
2674     /* li is the largest image < req */
2675     unsigned long size, smallest = 0xffffffff, largest = 0, si = 0, li = 0;
2676
2677     g_message("icons %d", self->nicons);
2678
2679     if (!self->nicons) {
2680         ObClientIcon *parent = NULL;
2681
2682         if (self->transient_for) {
2683             if (self->transient_for != OB_TRAN_GROUP)
2684                 parent = client_icon_recursive(self->transient_for, w, h);
2685             else {
2686                 GSList *it;
2687                 for (it = self->group->members; it; it = g_slist_next(it)) {
2688                     ObClient *c = it->data;
2689                     if (c != self && !c->transient_for) {
2690                         if ((parent = client_icon_recursive(c, w, h)))
2691                             break;
2692                     }
2693                 }
2694             }
2695         }
2696         
2697         return parent;
2698     }
2699
2700     for (i = 0; i < self->nicons; ++i) {
2701         size = self->icons[i].width * self->icons[i].height;
2702         if (size < smallest && size >= (unsigned)(w * h)) {
2703             smallest = size;
2704             si = i;
2705         }
2706         if (size > largest && size <= (unsigned)(w * h)) {
2707             largest = size;
2708             li = i;
2709         }
2710     }
2711     if (largest == 0) /* didnt find one smaller than the requested size */
2712         return &self->icons[si];
2713     return &self->icons[li];
2714 }
2715
2716 const ObClientIcon* client_icon(ObClient *self, int w, int h)
2717 {
2718     ObClientIcon *ret;
2719     static ObClientIcon deficon;
2720
2721     g_message("going for broke");
2722     if (!(ret = client_icon_recursive(self, w, h))) {
2723         g_message("using default");
2724         deficon.width = deficon.height = 48;
2725         deficon.data = ob_rr_theme->def_win_icon;
2726         ret = &deficon;
2727     }
2728     return ret;
2729 }
2730
2731 /* this be mostly ripped from fvwm */
2732 ObClient *client_find_directional(ObClient *c, ObDirection dir) 
2733 {
2734     int my_cx, my_cy, his_cx, his_cy;
2735     int offset = 0;
2736     int distance = 0;
2737     int score, best_score;
2738     ObClient *best_client, *cur;
2739     GList *it;
2740
2741     if(!client_list)
2742         return NULL;
2743
2744     /* first, find the centre coords of the currently focused window */
2745     my_cx = c->frame->area.x + c->frame->area.width / 2;
2746     my_cy = c->frame->area.y + c->frame->area.height / 2;
2747
2748     best_score = -1;
2749     best_client = NULL;
2750
2751     for(it = g_list_first(client_list); it; it = it->next) {
2752         cur = it->data;
2753
2754         /* the currently selected window isn't interesting */
2755         if(cur == c)
2756             continue;
2757         if (!client_normal(cur))
2758             continue;
2759         if(c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
2760             continue;
2761         if(cur->iconic)
2762             continue;
2763         if(client_focus_target(cur) == cur &&
2764            !(cur->can_focus || cur->focus_notify))
2765             continue;
2766
2767         /* find the centre coords of this window, from the
2768          * currently focused window's point of view */
2769         his_cx = (cur->frame->area.x - my_cx)
2770             + cur->frame->area.width / 2;
2771         his_cy = (cur->frame->area.y - my_cy)
2772             + cur->frame->area.height / 2;
2773
2774         if(dir == OB_DIRECTION_NORTHEAST || dir == OB_DIRECTION_SOUTHEAST ||
2775            dir == OB_DIRECTION_SOUTHWEST || dir == OB_DIRECTION_NORTHWEST) {
2776             int tx;
2777             /* Rotate the diagonals 45 degrees counterclockwise.
2778              * To do this, multiply the matrix /+h +h\ with the
2779              * vector (x y).                   \-h +h/
2780              * h = sqrt(0.5). We can set h := 1 since absolute
2781              * distance doesn't matter here. */
2782             tx = his_cx + his_cy;
2783             his_cy = -his_cx + his_cy;
2784             his_cx = tx;
2785         }
2786
2787         switch(dir) {
2788         case OB_DIRECTION_NORTH:
2789         case OB_DIRECTION_SOUTH:
2790         case OB_DIRECTION_NORTHEAST:
2791         case OB_DIRECTION_SOUTHWEST:
2792             offset = (his_cx < 0) ? -his_cx : his_cx;
2793             distance = ((dir == OB_DIRECTION_NORTH ||
2794                         dir == OB_DIRECTION_NORTHEAST) ?
2795                         -his_cy : his_cy);
2796             break;
2797         case OB_DIRECTION_EAST:
2798         case OB_DIRECTION_WEST:
2799         case OB_DIRECTION_SOUTHEAST:
2800         case OB_DIRECTION_NORTHWEST:
2801             offset = (his_cy < 0) ? -his_cy : his_cy;
2802             distance = ((dir == OB_DIRECTION_WEST ||
2803                         dir == OB_DIRECTION_NORTHWEST) ?
2804                         -his_cx : his_cx);
2805             break;
2806         }
2807
2808         /* the target must be in the requested direction */
2809         if(distance <= 0)
2810             continue;
2811
2812         /* Calculate score for this window.  The smaller the better. */
2813         score = distance + offset;
2814
2815         /* windows more than 45 degrees off the direction are
2816          * heavily penalized and will only be chosen if nothing
2817          * else within a million pixels */
2818         if(offset > distance)
2819             score += 1000000;
2820
2821         if(best_score == -1 || score < best_score)
2822             best_client = cur,
2823                 best_score = score;
2824     }
2825
2826     return best_client;
2827 }
2828
2829 void client_set_layer(ObClient *self, int layer)
2830 {
2831     if (layer < 0) {
2832         self->below = TRUE;
2833         self->above = FALSE;
2834     } else if (layer == 0) {
2835         self->below = self->above = FALSE;
2836     } else {
2837         self->below = FALSE;
2838         self->above = TRUE;
2839     }
2840     client_calc_layer(self);
2841     client_change_state(self); /* reflect this in the state hints */
2842 }
2843
2844 void client_set_undecorated(ObClient *self, gboolean undecorated)
2845 {
2846     if (self->undecorated != undecorated) {
2847         self->undecorated = undecorated;
2848         client_setup_decor_and_functions(self);
2849         client_change_state(self); /* reflect this in the state hints */
2850     }
2851 }
2852
2853 guint client_monitor(ObClient *self)
2854 {
2855     guint i;
2856
2857     for (i = 0; i < screen_num_monitors; ++i) {
2858         Rect *area = screen_physical_area_monitor(i);
2859         if (RECT_INTERSECTS_RECT(*area, self->frame->area))
2860             break;
2861     }
2862     if (i == screen_num_monitors) i = 0;
2863     g_assert(i < screen_num_monitors);
2864     return i;
2865 }
2866
2867 ObClient *client_search_top_transient(ObClient *self)
2868 {
2869     /* move up the transient chain as far as possible */
2870     if (self->transient_for) {
2871         if (self->transient_for != OB_TRAN_GROUP) {
2872             return client_search_top_transient(self->transient_for);
2873         } else {
2874             GSList *it;
2875
2876             g_assert(self->group);
2877
2878             for (it = self->group->members; it; it = it->next) {
2879                 ObClient *c = it->data;
2880
2881                 /* checking transient_for prevents infinate loops! */
2882                 if (c != self && !c->transient_for)
2883                     break;
2884             }
2885             if (it)
2886                 return it->data;
2887         }
2888     }
2889
2890     return self;
2891 }
2892
2893 ObClient *client_search_focus_parent(ObClient *self)
2894 {
2895     if (self->transient_for) {
2896         if (self->transient_for != OB_TRAN_GROUP) {
2897             if (client_focused(self->transient_for))
2898                 return self->transient_for;
2899         } else {
2900             GSList *it;
2901
2902             for (it = self->group->members; it; it = it->next) {
2903                 ObClient *c = it->data;
2904
2905                 /* checking transient_for prevents infinate loops! */
2906                 if (c != self && !c->transient_for)
2907                     if (client_focused(c))
2908                         return c;
2909             }
2910         }
2911     }
2912
2913     return NULL;
2914 }
2915
2916 ObClient *client_search_parent(ObClient *self, ObClient *search)
2917 {
2918     if (self->transient_for) {
2919         if (self->transient_for != OB_TRAN_GROUP) {
2920             if (self->transient_for == search)
2921                 return search;
2922         } else {
2923             GSList *it;
2924
2925             for (it = self->group->members; it; it = it->next) {
2926                 ObClient *c = it->data;
2927
2928                 /* checking transient_for prevents infinate loops! */
2929                 if (c != self && !c->transient_for)
2930                     if (c == search)
2931                         return search;
2932             }
2933         }
2934     }
2935
2936     return NULL;
2937 }
2938
2939 ObClient *client_search_transient(ObClient *self, ObClient *search)
2940 {
2941     GSList *sit;
2942
2943     for (sit = self->transients; sit; sit = g_slist_next(sit)) {
2944         if (sit->data == search)
2945             return search;
2946         if (client_search_transient(sit->data, search))
2947             return search;
2948     }
2949     return NULL;
2950 }
2951
2952 void client_update_sm_client_id(ObClient *self)
2953 {
2954     g_free(self->sm_client_id);
2955     self->sm_client_id = NULL;
2956
2957     if (!PROP_GETS(self->window, sm_client_id, locale, &self->sm_client_id) &&
2958         self->group)
2959         PROP_GETS(self->group->leader, sm_client_id, locale,
2960                   &self->sm_client_id);
2961 }
2962
2963 /* finds the nearest edge in the given direction from the current client
2964  * note to self: the edge is the -frame- edge (the actual one), not the
2965  * client edge.
2966  */
2967 int client_directional_edge_search(ObClient *c, ObDirection dir)
2968 {
2969     int dest;
2970     int my_edge_start, my_edge_end, my_offset;
2971     GList *it;
2972     Rect *a;
2973     
2974     if(!client_list)
2975         return -1;
2976
2977     a = screen_area(c->desktop);
2978
2979     switch(dir) {
2980     case OB_DIRECTION_NORTH:
2981         my_edge_start = c->frame->area.x;
2982         my_edge_end = c->frame->area.x + c->frame->area.width;
2983         my_offset = c->frame->area.y;
2984         
2985         dest = a->y; /* default: top of screen */
2986
2987         for(it = g_list_first(client_list); it; it = it->next) {
2988             int his_edge_start, his_edge_end, his_offset;
2989             ObClient *cur = it->data;
2990
2991             if(cur == c)
2992                 continue;
2993             if(!client_normal(cur))
2994                 continue;
2995             if(c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
2996                 continue;
2997             if(cur->iconic)
2998                 continue;
2999
3000             his_edge_start = cur->frame->area.x;
3001             his_edge_end = cur->frame->area.x + cur->frame->area.width;
3002             his_offset = cur->frame->area.y + cur->frame->area.height;
3003
3004             if(his_offset + 1 > my_offset)
3005                 continue;
3006
3007             if(his_offset < dest)
3008                 continue;
3009             
3010             if(his_edge_start >= my_edge_start &&
3011                his_edge_start <= my_edge_end)
3012                 dest = his_offset;
3013
3014             if(my_edge_start >= his_edge_start &&
3015                my_edge_start <= his_edge_end)
3016                 dest = his_offset;
3017
3018         }
3019         break;
3020     case OB_DIRECTION_SOUTH:
3021         my_edge_start = c->frame->area.x;
3022         my_edge_end = c->frame->area.x + c->frame->area.width;
3023         my_offset = c->frame->area.y + c->frame->area.height;
3024         
3025         dest = a->y + a->height; /* default: bottom of screen */
3026
3027         for(it = g_list_first(client_list); it; it = it->next) {
3028             int his_edge_start, his_edge_end, his_offset;
3029             ObClient *cur = it->data;
3030
3031             if(cur == c)
3032                 continue;
3033             if(!client_normal(cur))
3034                 continue;
3035             if(c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
3036                 continue;
3037             if(cur->iconic)
3038                 continue;
3039
3040             his_edge_start = cur->frame->area.x;
3041             his_edge_end = cur->frame->area.x + cur->frame->area.width;
3042             his_offset = cur->frame->area.y;
3043
3044
3045             if(his_offset - 1 < my_offset)
3046                 continue;
3047             
3048             if(his_offset > dest)
3049                 continue;
3050             
3051             if(his_edge_start >= my_edge_start &&
3052                his_edge_start <= my_edge_end)
3053                 dest = his_offset;
3054
3055             if(my_edge_start >= his_edge_start &&
3056                my_edge_start <= his_edge_end)
3057                 dest = his_offset;
3058
3059         }
3060         break;
3061     case OB_DIRECTION_WEST:
3062         my_edge_start = c->frame->area.y;
3063         my_edge_end = c->frame->area.y + c->frame->area.height;
3064         my_offset = c->frame->area.x;
3065
3066         dest = a->x; /* default: leftmost egde of screen */
3067
3068         for(it = g_list_first(client_list); it; it = it->next) {
3069             int his_edge_start, his_edge_end, his_offset;
3070             ObClient *cur = it->data;
3071
3072             if(cur == c)
3073                 continue;
3074             if(!client_normal(cur))
3075                 continue;
3076             if(c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
3077                 continue;
3078             if(cur->iconic)
3079                 continue;
3080
3081             his_edge_start = cur->frame->area.y;
3082             his_edge_end = cur->frame->area.y + cur->frame->area.height;
3083             his_offset = cur->frame->area.x + cur->frame->area.width;
3084
3085             if(his_offset + 1 > my_offset)
3086                 continue;
3087             
3088             if(his_offset < dest)
3089                 continue;
3090             
3091             if(his_edge_start >= my_edge_start &&
3092                his_edge_start <= my_edge_end)
3093                 dest = his_offset;
3094
3095             if(my_edge_start >= his_edge_start &&
3096                my_edge_start <= his_edge_end)
3097                 dest = his_offset;
3098                 
3099
3100         }
3101         break;
3102     case OB_DIRECTION_EAST:
3103         my_edge_start = c->frame->area.y;
3104         my_edge_end = c->frame->area.y + c->frame->area.height;
3105         my_offset = c->frame->area.x + c->frame->area.width;
3106         
3107         dest = a->x + a->width; /* default: rightmost edge of screen */
3108
3109         for(it = g_list_first(client_list); it; it = it->next) {
3110             int his_edge_start, his_edge_end, his_offset;
3111             ObClient *cur = it->data;
3112
3113             if(cur == c)
3114                 continue;
3115             if(!client_normal(cur))
3116                 continue;
3117             if(c->desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
3118                 continue;
3119             if(cur->iconic)
3120                 continue;
3121
3122             his_edge_start = cur->frame->area.y;
3123             his_edge_end = cur->frame->area.y + cur->frame->area.height;
3124             his_offset = cur->frame->area.x;
3125
3126             if(his_offset - 1 < my_offset)
3127                 continue;
3128             
3129             if(his_offset > dest)
3130                 continue;
3131             
3132             if(his_edge_start >= my_edge_start &&
3133                his_edge_start <= my_edge_end)
3134                 dest = his_offset;
3135
3136             if(my_edge_start >= his_edge_start &&
3137                my_edge_start <= his_edge_end)
3138                 dest = his_offset;
3139
3140         }
3141         break;
3142     case OB_DIRECTION_NORTHEAST:
3143     case OB_DIRECTION_SOUTHEAST:
3144     case OB_DIRECTION_NORTHWEST:
3145     case OB_DIRECTION_SOUTHWEST:
3146         /* not implemented */
3147     default:
3148         g_assert_not_reached();
3149     }
3150     return dest;
3151 }
3152
3153 ObClient* client_under_pointer()
3154 {
3155     int x, y;
3156     GList *it;
3157     ObClient *ret = NULL;
3158
3159     if (screen_pointer_pos(&x, &y)) {
3160         for (it = stacking_list; it != NULL; it = it->next) {
3161             if (WINDOW_IS_CLIENT(it->data)) {
3162                 ObClient *c = WINDOW_AS_CLIENT(it->data);
3163                 if (c->desktop == screen_desktop &&
3164                     RECT_CONTAINS(c->frame->area, x, y)) {
3165                     ret = c;
3166                     break;
3167                 }
3168             }
3169         }
3170     }
3171     return ret;
3172 }