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