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