4 #include "extensions.h"
14 #include <X11/Xutil.h>
16 /*! The event mask to grab on client windows */
17 #define CLIENT_EVENTMASK (PropertyChangeMask | FocusChangeMask | \
20 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
23 GSList *client_list = NULL;
24 GHashTable *client_map = NULL;
26 static Window *client_startup_stack_order = NULL;
27 static gulong client_startup_stack_size = 0;
29 static void client_get_all(Client *self);
30 static void client_toggle_border(Client *self, gboolean show);
31 static void client_get_area(Client *self);
32 static void client_get_desktop(Client *self);
33 static void client_get_state(Client *self);
34 static void client_get_shaped(Client *self);
35 static void client_get_mwm_hints(Client *self);
36 static void client_get_gravity(Client *self);
37 static void client_showhide(Client *self);
38 static void client_change_allowed_actions(Client *self);
39 static void client_change_state(Client *self);
40 static Client *search_focus_tree(Client *node, Client *skip);
41 static void client_apply_startup_state(Client *self);
42 static Client *search_modal_tree(Client *node, Client *skip);
44 static guint map_hash(Window *w) { return *w; }
45 static gboolean map_key_comp(Window *w1, Window *w2) { return *w1 == *w2; }
49 client_map = g_hash_table_new((GHashFunc)map_hash,
50 (GEqualFunc)map_key_comp);
52 /* save the stacking order on startup! */
53 PROP_GET32U(ob_root, net_client_list_stacking, window,
54 client_startup_stack_order, client_startup_stack_size);
59 void client_shutdown()
61 g_hash_table_destroy(client_map);
64 void client_set_list()
66 Window *windows, *win_it;
68 guint size = g_slist_length(client_list);
70 /* create an array of the window ids */
72 windows = g_new(Window, size);
74 for (it = client_list; it != NULL; it = it->next, ++win_it)
75 *win_it = ((Client*)it->data)->window;
79 PROP_SET32A(ob_root, net_client_list, window, windows, size);
87 void client_manage_all()
89 unsigned int i, j, nchild;
92 XWindowAttributes attrib;
94 XQueryTree(ob_display, ob_root, &w, &w, &children, &nchild);
96 /* remove all icon windows from the list */
97 for (i = 0; i < nchild; i++) {
98 if (children[i] == None) continue;
99 wmhints = XGetWMHints(ob_display, children[i]);
101 if ((wmhints->flags & IconWindowHint) &&
102 (wmhints->icon_window != children[i]))
103 for (j = 0; j < nchild; j++)
104 if (children[j] == wmhints->icon_window) {
112 for (i = 0; i < nchild; ++i) {
113 if (children[i] == None)
115 if (XGetWindowAttributes(ob_display, children[i], &attrib)) {
116 if (attrib.override_redirect) continue;
118 if (attrib.map_state != IsUnmapped)
119 client_manage(children[i]);
124 /* stack them as they were on startup!
125 why with stacking_lower? Why, because then windows who aren't in the
126 stacking list are on the top where you can see them instead of buried
128 for (i = client_startup_stack_size; i > 0; --i) {
131 w = client_startup_stack_order[i-1];
132 c = g_hash_table_lookup(client_map, &w);
133 if (c) stacking_lower(c);
135 g_free(client_startup_stack_order);
136 client_startup_stack_order = NULL;
137 client_startup_stack_size = 0;
140 void client_manage(Window window)
144 XWindowAttributes attrib;
145 XSetWindowAttributes attrib_set;
146 /* XWMHints *wmhint; */
151 /* check if it has already been unmapped by the time we started mapping
152 the grab does a sync so we don't have to here */
153 if (XCheckTypedWindowEvent(ob_display, window, DestroyNotify, &e) ||
154 XCheckTypedWindowEvent(ob_display, window, UnmapNotify, &e)) {
155 XPutBackEvent(ob_display, &e);
158 return; /* don't manage it */
161 /* make sure it isn't an override-redirect window */
162 if (!XGetWindowAttributes(ob_display, window, &attrib) ||
163 attrib.override_redirect) {
165 return; /* don't manage it */
168 /* /\* is the window a docking app *\/
169 if ((wmhint = XGetWMHints(ob_display, window))) {
170 if ((wmhint->flags & StateHint) &&
171 wmhint->initial_state == WithdrawnState) {
172 /\* XXX: make dock apps work! *\/
181 /* choose the events we want to receive on the CLIENT window */
182 attrib_set.event_mask = CLIENT_EVENTMASK;
183 attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK;
184 XChangeWindowAttributes(ob_display, window,
185 CWEventMask|CWDontPropagate, &attrib_set);
188 /* create the Client struct, and populate it from the hints on the
190 client = g_new(Client, 1);
191 client->window = window;
192 client_get_all(client);
194 /* remove the client's border (and adjust re gravity) */
195 client_toggle_border(client, FALSE);
197 /* specify that if we exit, the window should not be destroyed and should
198 be reparented back to root automatically */
199 XChangeSaveSet(ob_display, window, SetModeInsert);
201 /* create the decoration frame for the client window */
202 client->frame = engine_frame_new();
204 engine_frame_grab_client(client->frame, client);
206 client_apply_startup_state(client);
210 client_list = g_slist_append(client_list, client);
211 stacking_list = g_list_append(stacking_list, client);
212 g_assert(!g_hash_table_lookup(client_map, &client->window));
213 g_hash_table_insert(client_map, &client->window, client);
215 /* update the focus lists */
216 if (client->desktop == DESKTOP_ALL) {
217 for (i = 0; i < screen_num_desktops; ++i)
218 focus_order[i] = g_list_append(focus_order[i], client);
221 focus_order[i] = g_list_append(focus_order[i], client);
224 stacking_raise(client);
226 screen_update_struts();
228 dispatch_client(Event_Client_New, client, 0, 0);
230 client_showhide(client);
232 dispatch_client(Event_Client_Mapped, client, 0, 0);
234 /* update the list hints */
237 g_message("Managed window 0x%lx", window);
240 void client_unmanage_all()
242 while (client_list != NULL)
243 client_unmanage(client_list->data);
246 void client_unmanage(Client *client)
252 g_message("Unmanaging window: %lx", client->window);
254 dispatch_client(Event_Client_Destroy, client, 0, 0);
255 g_assert(client != NULL);
257 /* remove the window from our save set */
258 XChangeSaveSet(ob_display, client->window, SetModeDelete);
260 /* we dont want events no more */
261 XSelectInput(ob_display, client->window, NoEventMask);
263 engine_frame_hide(client->frame);
265 client_list = g_slist_remove(client_list, client);
266 stacking_list = g_list_remove(stacking_list, client);
267 g_hash_table_remove(client_map, &client->window);
269 /* update the focus lists */
270 if (client->desktop == DESKTOP_ALL) {
271 for (i = 0; i < screen_num_desktops; ++i)
272 focus_order[i] = g_list_remove(focus_order[i], client);
275 focus_order[i] = g_list_remove(focus_order[i], client);
278 /* once the client is out of the list, update the struts to remove it's
280 screen_update_struts();
282 /* tell our parent that we're gone */
283 if (client->transient_for != NULL)
284 client->transient_for->transients =
285 g_slist_remove(client->transient_for->transients, client);
287 /* tell our transients that we're gone */
288 for (it = client->transients; it != NULL; it = it->next) {
289 ((Client*)it->data)->transient_for = NULL;
290 client_calc_layer(it->data);
293 /* dispatch the unmapped event */
294 dispatch_client(Event_Client_Unmapped, client, 0, 0);
295 g_assert(client != NULL);
297 /* unfocus the client (dispatchs the focus event) (we're out of the
298 transient lists already, so being modal doesn't matter) */
299 if (client_focused(client))
300 client_unfocus(client);
302 /* give the client its border back */
303 client_toggle_border(client, TRUE);
305 /* reparent the window out of the frame, and free the frame */
306 engine_frame_release_client(client->frame, client);
307 client->frame = NULL;
309 if (ob_state != State_Exiting) {
310 /* these values should not be persisted across a window
312 prop_erase(client->window, prop_atoms.net_wm_desktop);
313 prop_erase(client->window, prop_atoms.net_wm_state);
315 /* if we're left in an iconic state, the client wont be mapped. this is
316 bad, since we will no longer be managing the window on restart */
318 XMapWindow(ob_display, client->window);
321 /* free all data allocated in the client struct */
322 g_slist_free(client->transients);
323 for (j = 0; j < client->nicons; ++j)
324 g_free(client->icons[j].data);
325 if (client->nicons > 0)
326 g_free(client->icons);
327 g_free(client->title);
328 g_free(client->icon_title);
329 g_free(client->name);
330 g_free(client->class);
331 g_free(client->role);
334 /* update the list hints */
338 static void client_toggle_border(Client *self, gboolean show)
340 /* adjust our idea of where the client is, based on its border. When the
341 border is removed, the client should now be considered to be in a
343 when re-adding the border to the client, the same operation needs to be
345 int oldx = self->area.x, oldy = self->area.y;
346 int x = oldx, y = oldy;
347 switch(self->gravity) {
349 case NorthWestGravity:
351 case SouthWestGravity:
353 case NorthEastGravity:
355 case SouthEastGravity:
356 if (show) x -= self->border_width * 2;
357 else x += self->border_width * 2;
364 if (show) x -= self->border_width;
365 else x += self->border_width;
368 switch(self->gravity) {
370 case NorthWestGravity:
372 case NorthEastGravity:
374 case SouthWestGravity:
376 case SouthEastGravity:
377 if (show) y -= self->border_width * 2;
378 else y += self->border_width * 2;
385 if (show) y -= self->border_width;
386 else y += self->border_width;
393 XSetWindowBorderWidth(ob_display, self->window, self->border_width);
395 /* move the client so it is back it the right spot _with_ its
397 if (x != oldx || y != oldy)
398 XMoveWindow(ob_display, self->window, x, y);
400 XSetWindowBorderWidth(ob_display, self->window, 0);
404 static void client_get_all(Client *self)
406 /* update EVERYTHING!! */
408 self->ignore_unmaps = 0;
412 self->title = self->icon_title = NULL;
413 self->name = self->class = self->role = NULL;
414 self->wmstate = NormalState;
415 self->transient = FALSE;
416 self->transients = NULL;
417 self->transient_for = NULL;
419 self->urgent = FALSE;
420 self->positioned = FALSE;
421 self->disabled_decorations = 0;
425 client_get_area(self);
426 client_get_desktop(self);
427 client_get_state(self);
428 client_get_shaped(self);
430 client_update_transient_for(self);
431 client_get_mwm_hints(self);
432 client_get_type(self);/* this can change the mwmhints for special cases */
434 client_update_protocols(self);
436 client_get_gravity(self); /* get the attribute gravity */
437 client_update_normal_hints(self); /* this may override the attribute
440 /* got the type, the mwmhints, the protocols, and the normal hints
441 (min/max sizes), so we're ready to set up the decorations/functions */
442 client_setup_decor_and_functions(self);
444 client_update_wmhints(self);
445 client_update_title(self);
446 client_update_icon_title(self);
447 client_update_class(self);
448 client_update_strut(self);
449 client_update_icons(self);
450 client_update_kwm_icon(self);
452 /* this makes sure that these windows appear on all desktops */
453 if (self->type == Type_Desktop)
454 self->desktop = DESKTOP_ALL;
456 /* set the desktop hint, to make sure that it always exists, and to
457 reflect any changes we've made here */
458 PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop);
460 client_change_state(self);
463 static void client_get_area(Client *self)
465 XWindowAttributes wattrib;
468 ret = XGetWindowAttributes(ob_display, self->window, &wattrib);
469 g_assert(ret != BadWindow);
471 RECT_SET(self->area, wattrib.x, wattrib.y, wattrib.width, wattrib.height);
472 self->border_width = wattrib.border_width;
475 static void client_get_desktop(Client *self)
479 if (PROP_GET32(self->window, net_wm_desktop, cardinal, d)) {
480 if (d >= screen_num_desktops && d != DESKTOP_ALL)
481 d = screen_num_desktops - 1;
484 /* defaults to the current desktop */
485 self->desktop = screen_desktop;
489 static void client_get_state(Client *self)
494 self->modal = self->shaded = self->max_horz = self->max_vert =
495 self->fullscreen = self->above = self->below = self->iconic =
496 self->skip_taskbar = self->skip_pager = FALSE;
498 if (PROP_GET32U(self->window, net_wm_state, atom, state, num)) {
500 for (i = 0; i < num; ++i) {
501 if (state[i] == prop_atoms.net_wm_state_modal)
503 else if (state[i] == prop_atoms.net_wm_state_shaded)
505 else if (state[i] == prop_atoms.net_wm_state_hidden)
507 else if (state[i] == prop_atoms.net_wm_state_skip_taskbar)
508 self->skip_taskbar = TRUE;
509 else if (state[i] == prop_atoms.net_wm_state_skip_pager)
510 self->skip_pager = TRUE;
511 else if (state[i] == prop_atoms.net_wm_state_fullscreen)
512 self->fullscreen = TRUE;
513 else if (state[i] == prop_atoms.net_wm_state_maximized_vert)
514 self->max_vert = TRUE;
515 else if (state[i] == prop_atoms.net_wm_state_maximized_horz)
516 self->max_horz = TRUE;
517 else if (state[i] == prop_atoms.net_wm_state_above)
519 else if (state[i] == prop_atoms.net_wm_state_below)
527 static void client_get_shaped(Client *self)
529 self->shaped = FALSE;
531 if (extensions_shape) {
536 XShapeSelectInput(ob_display, self->window, ShapeNotifyMask);
538 XShapeQueryExtents(ob_display, self->window, &s, &foo,
539 &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo,
541 self->shaped = (s != 0);
546 void client_update_transient_for(Client *self)
551 if (XGetTransientForHint(ob_display, self->window, &t) &&
552 t != self->window) { /* cant be transient to itself! */
553 self->transient = TRUE;
554 c = g_hash_table_lookup(client_map, &t);
555 g_assert(c != self);/* if this happens then we need to check for it*/
557 if (!c /*XXX: && _group*/) {
558 /* not transient to a client, see if it is transient for a
560 if (/*t == _group->leader() || */
563 /* window is a transient for its group! */
564 /* XXX: for now this is treated as non-transient.
565 this needs to be fixed! */
569 self->transient = FALSE;
571 /* if anything has changed... */
572 if (c != self->transient_for) {
573 if (self->transient_for)
574 /* remove from old parent */
575 self->transient_for->transients =
576 g_slist_remove(self->transient_for->transients, self);
577 self->transient_for = c;
578 if (self->transient_for)
579 /* add to new parent */
580 self->transient_for->transients =
581 g_slist_append(self->transient_for->transients, self);
585 static void client_get_mwm_hints(Client *self)
588 unsigned long *hints;
590 self->mwmhints.flags = 0; /* default to none */
592 if (PROP_GET32U(self->window, motif_wm_hints, motif_wm_hints, hints, num)) {
593 if (num >= MWM_ELEMENTS) {
594 self->mwmhints.flags = hints[0];
595 self->mwmhints.functions = hints[1];
596 self->mwmhints.decorations = hints[2];
602 void client_get_type(Client *self)
608 if (PROP_GET32U(self->window, net_wm_window_type, atom, val, num)) {
609 /* use the first value that we know about in the array */
610 for (i = 0; i < num; ++i) {
611 if (val[i] == prop_atoms.net_wm_window_type_desktop)
612 self->type = Type_Desktop;
613 else if (val[i] == prop_atoms.net_wm_window_type_dock)
614 self->type = Type_Dock;
615 else if (val[i] == prop_atoms.net_wm_window_type_toolbar)
616 self->type = Type_Toolbar;
617 else if (val[i] == prop_atoms.net_wm_window_type_menu)
618 self->type = Type_Menu;
619 else if (val[i] == prop_atoms.net_wm_window_type_utility)
620 self->type = Type_Utility;
621 else if (val[i] == prop_atoms.net_wm_window_type_splash)
622 self->type = Type_Splash;
623 else if (val[i] == prop_atoms.net_wm_window_type_dialog)
624 self->type = Type_Dialog;
625 else if (val[i] == prop_atoms.net_wm_window_type_normal)
626 self->type = Type_Normal;
627 else if (val[i] == prop_atoms.kde_net_wm_window_type_override) {
628 /* prevent this window from getting any decor or
630 self->mwmhints.flags &= (MwmFlag_Functions |
631 MwmFlag_Decorations);
632 self->mwmhints.decorations = 0;
633 self->mwmhints.functions = 0;
635 if (self->type != (WindowType) -1)
636 break; /* grab the first legit type */
641 if (self->type == (WindowType) -1) {
642 /*the window type hint was not set, which means we either classify
643 ourself as a normal window or a dialog, depending on if we are a
646 self->type = Type_Dialog;
648 self->type = Type_Normal;
652 void client_update_protocols(Client *self)
655 gulong num_return, i;
657 self->focus_notify = FALSE;
658 self->delete_window = FALSE;
660 if (PROP_GET32U(self->window, wm_protocols, atom, proto, num_return)) {
661 for (i = 0; i < num_return; ++i) {
662 if (proto[i] == prop_atoms.wm_delete_window) {
663 /* this means we can request the window to close */
664 self->delete_window = TRUE;
665 } else if (proto[i] == prop_atoms.wm_take_focus)
666 /* if this protocol is requested, then the window will be
667 notified whenever we want it to receive focus */
668 self->focus_notify = TRUE;
674 static void client_get_gravity(Client *self)
676 XWindowAttributes wattrib;
679 ret = XGetWindowAttributes(ob_display, self->window, &wattrib);
680 g_assert(ret != BadWindow);
681 self->gravity = wattrib.win_gravity;
684 void client_update_normal_hints(Client *self)
688 int oldgravity = self->gravity;
691 self->min_ratio = 0.0f;
692 self->max_ratio = 0.0f;
693 SIZE_SET(self->size_inc, 1, 1);
694 SIZE_SET(self->base_size, 0, 0);
695 SIZE_SET(self->min_size, 0, 0);
696 SIZE_SET(self->max_size, G_MAXINT, G_MAXINT);
698 /* get the hints from the window */
699 if (XGetWMNormalHints(ob_display, self->window, &size, &ret)) {
700 self->positioned = (size.flags & (PPosition|USPosition));
702 if (size.flags & PWinGravity) {
703 self->gravity = size.win_gravity;
705 /* if the client has a frame, i.e. has already been mapped and
706 is changing its gravity */
707 if (self->frame && self->gravity != oldgravity) {
708 /* move our idea of the client's position based on its new
710 self->area.x = self->frame->area.x;
711 self->area.y = self->frame->area.y;
712 frame_frame_gravity(self->frame, &self->area.x, &self->area.y);
716 if (size.flags & PAspect) {
717 if (size.min_aspect.y)
718 self->min_ratio = (float)size.min_aspect.x / size.min_aspect.y;
719 if (size.max_aspect.y)
720 self->max_ratio = (float)size.max_aspect.x / size.max_aspect.y;
723 if (size.flags & PMinSize)
724 SIZE_SET(self->min_size, size.min_width, size.min_height);
726 if (size.flags & PMaxSize)
727 SIZE_SET(self->max_size, size.max_width, size.max_height);
729 if (size.flags & PBaseSize)
730 SIZE_SET(self->base_size, size.base_width, size.base_height);
732 if (size.flags & PResizeInc)
733 SIZE_SET(self->size_inc, size.width_inc, size.height_inc);
737 void client_setup_decor_and_functions(Client *self)
739 /* start with everything (cept fullscreen) */
740 self->decorations = Decor_Titlebar | Decor_Handle | Decor_Border |
741 Decor_Icon | Decor_AllDesktops | Decor_Iconify | Decor_Maximize;
742 self->functions = Func_Resize | Func_Move | Func_Iconify | Func_Maximize |
744 if (self->delete_window) {
745 self->decorations |= Decor_Close;
746 self->functions |= Func_Close;
749 if (!(self->min_size.width < self->max_size.width ||
750 self->min_size.height < self->max_size.height)) {
751 self->decorations &= ~(Decor_Maximize | Decor_Handle);
752 self->functions &= ~(Func_Resize | Func_Maximize);
755 switch (self->type) {
757 /* normal windows retain all of the possible decorations and
758 functionality, and are the only windows that you can fullscreen */
759 self->functions |= Func_Fullscreen;
763 /* dialogs cannot be maximized */
764 self->decorations &= ~Decor_Maximize;
765 self->functions &= ~Func_Maximize;
771 /* these windows get less functionality */
772 self->decorations &= ~(Decor_Iconify | Decor_Handle);
773 self->functions &= ~(Func_Iconify | Func_Resize);
779 /* none of these windows are manipulated by the window manager */
780 self->decorations = 0;
785 /* Mwm Hints are applied subtractively to what has already been chosen for
786 decor and functionality */
787 if (self->mwmhints.flags & MwmFlag_Decorations) {
788 if (! (self->mwmhints.decorations & MwmDecor_All)) {
789 if (! (self->mwmhints.decorations & MwmDecor_Border))
790 self->decorations &= ~Decor_Border;
791 if (! (self->mwmhints.decorations & MwmDecor_Handle))
792 self->decorations &= ~Decor_Handle;
793 if (! (self->mwmhints.decorations & MwmDecor_Title))
794 self->decorations &= ~Decor_Titlebar;
795 if (! (self->mwmhints.decorations & MwmDecor_Iconify))
796 self->decorations &= ~Decor_Iconify;
797 if (! (self->mwmhints.decorations & MwmDecor_Maximize))
798 self->decorations &= ~Decor_Maximize;
802 if (self->mwmhints.flags & MwmFlag_Functions) {
803 if (! (self->mwmhints.functions & MwmFunc_All)) {
804 if (! (self->mwmhints.functions & MwmFunc_Resize))
805 self->functions &= ~Func_Resize;
806 if (! (self->mwmhints.functions & MwmFunc_Move))
807 self->functions &= ~Func_Move;
808 if (! (self->mwmhints.functions & MwmFunc_Iconify))
809 self->functions &= ~Func_Iconify;
810 if (! (self->mwmhints.functions & MwmFunc_Maximize))
811 self->functions &= ~Func_Maximize;
812 /* dont let mwm hints kill the close button
813 if (! (self->mwmhints.functions & MwmFunc_Close))
814 self->functions &= ~Func_Close; */
818 /* can't maximize without moving/resizing */
819 if (!((self->functions & Func_Move) && (self->functions & Func_Resize)))
820 self->functions &= ~(Func_Maximize | Func_Fullscreen);
822 /* finally, user specified disabled decorations are applied to subtract
824 if (self->disabled_decorations & Decor_Titlebar)
825 self->decorations &= ~Decor_Titlebar;
826 if (self->disabled_decorations & Decor_Handle)
827 self->decorations &= ~Decor_Handle;
828 if (self->disabled_decorations & Decor_Border)
829 self->decorations &= ~Decor_Border;
830 if (self->disabled_decorations & Decor_Iconify)
831 self->decorations &= ~Decor_Iconify;
832 if (self->disabled_decorations & Decor_Maximize)
833 self->decorations &= ~Decor_Maximize;
834 if (self->disabled_decorations & Decor_AllDesktops)
835 self->decorations &= ~Decor_AllDesktops;
836 if (self->disabled_decorations & Decor_Close)
837 self->decorations &= ~Decor_Close;
839 /* if we don't have a titlebar, then we cannot shade! */
840 if (!(self->decorations & Decor_Titlebar))
841 self->functions &= ~Func_Shade;
843 client_change_allowed_actions(self);
846 /* change the decors on the frame, and with more/less decorations,
847 we may also need to be repositioned */
848 engine_frame_adjust_area(self->frame, TRUE, TRUE);
849 /* with new decor, the window's maximized size may change */
850 client_remaximize(self);
854 static void client_change_allowed_actions(Client *self)
859 actions[num++] = prop_atoms.net_wm_action_change_desktop;
861 if (self->functions & Func_Shade)
862 actions[num++] = prop_atoms.net_wm_action_shade;
863 if (self->functions & Func_Close)
864 actions[num++] = prop_atoms.net_wm_action_close;
865 if (self->functions & Func_Move)
866 actions[num++] = prop_atoms.net_wm_action_move;
867 if (self->functions & Func_Iconify)
868 actions[num++] = prop_atoms.net_wm_action_minimize;
869 if (self->functions & Func_Resize)
870 actions[num++] = prop_atoms.net_wm_action_resize;
871 if (self->functions & Func_Fullscreen)
872 actions[num++] = prop_atoms.net_wm_action_fullscreen;
873 if (self->functions & Func_Maximize) {
874 actions[num++] = prop_atoms.net_wm_action_maximize_horz;
875 actions[num++] = prop_atoms.net_wm_action_maximize_vert;
878 PROP_SET32A(self->window, net_wm_allowed_actions, atom, actions, num);
880 /* make sure the window isn't breaking any rules now */
882 if (!(self->functions & Func_Shade) && self->shaded) {
883 if (self->frame) client_shade(self, FALSE);
884 else self->shaded = FALSE;
886 if (!(self->functions & Func_Iconify) && self->iconic) {
887 if (self->frame) client_iconify(self, FALSE, TRUE);
888 else self->iconic = FALSE;
890 if (!(self->functions & Func_Fullscreen) && self->fullscreen) {
891 if (self->frame) client_fullscreen(self, FALSE, TRUE);
892 else self->fullscreen = FALSE;
894 if (!(self->functions & Func_Maximize) && (self->max_horz ||
896 if (self->frame) client_maximize(self, FALSE, 0, TRUE);
897 else self->max_vert = self->max_horz = FALSE;
901 void client_remaximize(Client *self)
904 if (self->max_horz && self->max_vert)
906 else if (self->max_horz)
908 else if (self->max_vert)
911 return; /* not maximized */
912 self->max_horz = self->max_vert = FALSE;
913 client_maximize(self, TRUE, dir, FALSE);
916 void client_update_wmhints(Client *self)
921 /* assume a window takes input if it doesnt specify */
922 self->can_focus = TRUE;
924 if ((hints = XGetWMHints(ob_display, self->window)) != NULL) {
925 if (hints->flags & InputHint)
926 self->can_focus = hints->input;
928 /* only do this when starting! */
929 if (ob_state == State_Starting && (hints->flags & StateHint))
930 self->iconic = hints->initial_state == IconicState;
932 if (hints->flags & XUrgencyHint)
935 if (hints->flags & WindowGroupHint) {
936 if (hints->window_group != self->group) {
937 /* XXX: remove from the old group if there was one */
938 self->group = hints->window_group;
939 /* XXX: do stuff with the group */
941 } else /* no group! */
944 if (hints->flags & IconPixmapHint) {
945 client_update_kwm_icon(self);
946 /* try get the kwm icon first, this is a fallback only */
947 if (self->pixmap_icon == None) {
948 self->pixmap_icon = hints->icon_pixmap;
949 if (hints->flags & IconMaskHint)
950 self->pixmap_icon_mask = hints->icon_mask;
952 self->pixmap_icon_mask = None;
955 engine_frame_adjust_icon(self->frame);
962 if (ur != self->urgent) {
964 g_message("Urgent Hint for 0x%lx: %s", self->window,
966 /* fire the urgent callback if we're mapped, otherwise, wait until
967 after we're mapped */
969 dispatch_client(Event_Client_Urgent, self, self->urgent, 0);
973 void client_update_title(Client *self)
980 if (!PROP_GETS(self->window, net_wm_name, utf8, data)) {
981 /* try old x stuff */
982 if (PROP_GETS(self->window, wm_name, string, data)) {
983 /* convert it to UTF-8 */
987 u = g_locale_to_utf8(data, -1, &r, &w, NULL);
989 g_warning("Unable to convert string to UTF-8");
996 data = g_strdup("Unnamed Window");
998 PROP_SETS(self->window, net_wm_visible_name, utf8, data);
1004 engine_frame_adjust_title(self->frame);
1007 void client_update_icon_title(Client *self)
1011 g_free(self->icon_title);
1014 if (!PROP_GETS(self->window, net_wm_icon_name, utf8, data)) {
1015 /* try old x stuff */
1016 if (PROP_GETS(self->window, wm_icon_name, string, data)) {
1017 /* convert it to UTF-8 */
1021 u = g_locale_to_utf8(data, -1, &r, &w, NULL);
1023 g_warning("Unable to convert string to UTF-8");
1030 data = g_strdup("Unnamed Window");
1032 PROP_SETS(self->window, net_wm_visible_icon_name, utf8, data);
1035 self->icon_title = data;
1038 void client_update_class(Client *self)
1044 if (self->name) g_free(self->name);
1045 if (self->class) g_free(self->class);
1046 if (self->role) g_free(self->role);
1048 self->name = self->class = self->role = NULL;
1050 data = g_ptr_array_new();
1052 if (PROP_GETSA(self->window, wm_class, string, data)) {
1054 self->name = g_strdup(g_ptr_array_index(data, 0));
1056 self->class = g_strdup(g_ptr_array_index(data, 1));
1059 for (i = 0; i < data->len; ++i)
1060 g_free(g_ptr_array_index(data, i));
1061 g_ptr_array_free(data, TRUE);
1063 if (PROP_GETS(self->window, wm_window_role, string, s))
1064 self->role = g_strdup(s);
1066 if (self->name == NULL) self->name = g_strdup("");
1067 if (self->class == NULL) self->class = g_strdup("");
1068 if (self->role == NULL) self->role = g_strdup("");
1071 void client_update_strut(Client *self)
1075 if (PROP_GET32A(self->window, net_wm_strut, cardinal, data, 4)) {
1076 STRUT_SET(self->strut, data[0], data[1], data[2], data[3]);
1079 STRUT_SET(self->strut, 0, 0, 0, 0);
1081 /* updating here is pointless while we're being mapped cuz we're not in
1082 the client list yet */
1084 screen_update_struts();
1087 void client_update_icons(Client *self)
1090 unsigned long *data;
1091 unsigned long w, h, i;
1094 for (j = 0; j < self->nicons; ++j)
1095 g_free(self->icons[j].data);
1096 if (self->nicons > 0)
1097 g_free(self->icons);
1100 if (PROP_GET32U(self->window, net_wm_icon, cardinal, data, num)) {
1101 /* figure out how many valid icons are in here */
1103 while (num - i > 2) {
1111 self->icons = g_new(Icon, self->nicons);
1113 /* store the icons */
1115 for (j = 0; j < self->nicons; ++j) {
1116 w = self->icons[j].width = data[i++];
1117 h = self->icons[j].height = data[i++];
1118 self->icons[j].data =
1119 g_memdup(&data[i], w * h * sizeof(gulong));
1128 engine_frame_adjust_icon(self->frame);
1131 void client_update_kwm_icon(Client *self)
1135 if (PROP_GET32A(self->window, kwm_win_icon, kwm_win_icon, data, 2)) {
1136 self->pixmap_icon = data[0];
1137 self->pixmap_icon_mask = data[1];
1140 self->pixmap_icon = self->pixmap_icon_mask = None;
1143 engine_frame_adjust_icon(self->frame);
1146 static void client_change_state(Client *self)
1148 unsigned long state[2];
1152 state[0] = self->wmstate;
1154 PROP_SET32A(self->window, wm_state, wm_state, state, 2);
1158 netstate[num++] = prop_atoms.net_wm_state_modal;
1160 netstate[num++] = prop_atoms.net_wm_state_shaded;
1162 netstate[num++] = prop_atoms.net_wm_state_hidden;
1163 if (self->skip_taskbar)
1164 netstate[num++] = prop_atoms.net_wm_state_skip_taskbar;
1165 if (self->skip_pager)
1166 netstate[num++] = prop_atoms.net_wm_state_skip_pager;
1167 if (self->fullscreen)
1168 netstate[num++] = prop_atoms.net_wm_state_fullscreen;
1170 netstate[num++] = prop_atoms.net_wm_state_maximized_vert;
1172 netstate[num++] = prop_atoms.net_wm_state_maximized_horz;
1174 netstate[num++] = prop_atoms.net_wm_state_above;
1176 netstate[num++] = prop_atoms.net_wm_state_below;
1177 PROP_SET32A(self->window, net_wm_state, atom, netstate, num);
1179 client_calc_layer(self);
1182 engine_frame_adjust_state(self->frame);
1185 static Client *search_focus_tree(Client *node, Client *skip)
1190 for (it = node->transients; it != NULL; it = it->next) {
1191 Client *c = it->data;
1192 if (c == skip) continue; /* circular? */
1193 if ((ret = search_focus_tree(c, skip))) return ret;
1194 if (client_focused(c)) return c;
1199 void client_calc_layer(Client *self)
1205 /* are we fullscreen, or do we have a fullscreen transient parent? */
1209 if (c->fullscreen) {
1213 c = c->transient_for;
1215 if (!fs && self->fullscreen) {
1216 /* is one of our transients focused? */
1217 c = search_focus_tree(self, self);
1218 if (c != NULL) fs = TRUE;
1221 if (self->iconic) l = Layer_Icon;
1222 else if (fs) l = Layer_Fullscreen;
1223 else if (self->type == Type_Desktop) l = Layer_Desktop;
1224 else if (self->type == Type_Dock) {
1225 if (!self->below) l = Layer_Top;
1226 else l = Layer_Normal;
1228 else if (self->above) l = Layer_Above;
1229 else if (self->below) l = Layer_Below;
1230 else l = Layer_Normal;
1232 if (l != self->layer) {
1235 stacking_raise(self);
1239 gboolean client_should_show(Client *self)
1241 if (self->iconic) return FALSE;
1242 else if (!(self->desktop == screen_desktop ||
1243 self->desktop == DESKTOP_ALL)) return FALSE;
1244 else if (client_normal(self) && screen_showing_desktop) return FALSE;
1249 static void client_showhide(Client *self)
1252 if (client_should_show(self))
1253 engine_frame_show(self->frame);
1255 engine_frame_hide(self->frame);
1258 gboolean client_normal(Client *self) {
1259 return ! (self->type == Type_Desktop || self->type == Type_Dock ||
1260 self->type == Type_Splash);
1263 static void client_apply_startup_state(Client *self)
1265 /* these are in a carefully crafted order.. */
1268 self->iconic = FALSE;
1269 client_iconify(self, TRUE, FALSE);
1271 if (self->fullscreen) {
1272 self->fullscreen = FALSE;
1273 client_fullscreen(self, TRUE, FALSE);
1276 self->shaded = FALSE;
1277 client_shade(self, TRUE);
1280 dispatch_client(Event_Client_Urgent, self, self->urgent, 0);
1282 if (self->max_vert && self->max_horz) {
1283 self->max_vert = self->max_horz = FALSE;
1284 client_maximize(self, TRUE, 0, FALSE);
1285 } else if (self->max_vert) {
1286 self->max_vert = FALSE;
1287 client_maximize(self, TRUE, 2, FALSE);
1288 } else if (self->max_horz) {
1289 self->max_horz = FALSE;
1290 client_maximize(self, TRUE, 1, FALSE);
1293 /* nothing to do for the other states:
1302 void client_configure(Client *self, Corner anchor, int x, int y, int w, int h,
1303 gboolean user, gboolean final)
1305 gboolean moved = FALSE, resized = FALSE;
1307 /* set the size and position if fullscreen */
1308 if (self->fullscreen) {
1311 w = screen_physical_size.width;
1312 h = screen_physical_size.height;
1314 /* set the size and position if maximized */
1315 if (self->max_horz) {
1316 x = screen_area(self->desktop)->x - self->frame->size.left;
1317 w = screen_area(self->desktop)->x +
1318 screen_area(self->desktop)->width;
1320 if (self->max_vert) {
1321 y = screen_area(self->desktop)->y;
1322 h = screen_area(self->desktop)->y +
1323 screen_area(self->desktop)->height -
1324 self->frame->size.top - self->frame->size.bottom;
1328 /* these override the above states! if you cant move you can't move! */
1330 if (!(self->functions & Func_Move)) {
1334 if (!(self->functions & Func_Resize)) {
1335 w = self->area.width;
1336 h = self->area.height;
1340 if (!(w == self->area.width && h == self->area.height)) {
1341 w -= self->base_size.width;
1342 h -= self->base_size.height;
1345 /* for interactive resizing. have to move half an increment in each
1348 /* how far we are towards the next size inc */
1349 int mw = w % self->size_inc.width;
1350 int mh = h % self->size_inc.height;
1352 int aw = self->size_inc.width / 2;
1353 int ah = self->size_inc.height / 2;
1354 /* don't let us move into a new size increment */
1355 if (mw + aw >= self->size_inc.width)
1356 aw = self->size_inc.width - mw - 1;
1357 if (mh + ah >= self->size_inc.height)
1358 ah = self->size_inc.height - mh - 1;
1362 /* if this is a user-requested resize, then check against min/max
1363 sizes and aspect ratios */
1365 /* smaller than min size or bigger than max size? */
1366 if (w > self->max_size.width) w = self->max_size.width;
1367 if (w < self->min_size.width) w = self->min_size.width;
1368 if (h > self->max_size.height) h = self->max_size.height;
1369 if (h < self->min_size.height) h = self->min_size.height;
1371 /* adjust the height ot match the width for the aspect ratios */
1372 if (self->min_ratio)
1373 if (h * self->min_ratio > w) h = (int)(w / self->min_ratio);
1374 if (self->max_ratio)
1375 if (h * self->max_ratio < w) h = (int)(w / self->max_ratio);
1378 /* keep to the increments */
1379 w /= self->size_inc.width;
1380 h /= self->size_inc.height;
1382 /* you cannot resize to nothing */
1386 /* store the logical size */
1387 SIZE_SET(self->logical_size, w, h);
1389 w *= self->size_inc.width;
1390 h *= self->size_inc.height;
1392 w += self->base_size.width;
1393 h += self->base_size.height;
1397 case Corner_TopLeft:
1399 case Corner_TopRight:
1400 x -= w - self->area.width;
1402 case Corner_BottomLeft:
1403 y -= h - self->area.height;
1405 case Corner_BottomRight:
1406 x -= w - self->area.width;
1407 y -= h - self->area.height;
1411 moved = x != self->area.x || y != self->area.y;
1412 resized = w != self->area.width || h != self->area.height;
1414 RECT_SET(self->area, x, y, w, h);
1417 XResizeWindow(ob_display, self->window, w, h);
1419 /* move/resize the frame to match the request */
1421 if (moved || resized)
1422 engine_frame_adjust_area(self->frame, moved, resized);
1424 if (!user || final) {
1426 event.type = ConfigureNotify;
1427 event.xconfigure.display = ob_display;
1428 event.xconfigure.event = self->window;
1429 event.xconfigure.window = self->window;
1431 /* root window coords with border in mind */
1432 event.xconfigure.x = x - self->border_width +
1433 self->frame->size.left;
1434 event.xconfigure.y = y - self->border_width +
1435 self->frame->size.top;
1437 event.xconfigure.width = self->area.width;
1438 event.xconfigure.height = self->area.height;
1439 event.xconfigure.border_width = self->border_width;
1440 event.xconfigure.above = self->frame->plate;
1441 event.xconfigure.override_redirect = FALSE;
1442 XSendEvent(event.xconfigure.display, event.xconfigure.window,
1443 FALSE, StructureNotifyMask, &event);
1448 void client_fullscreen(Client *self, gboolean fs, gboolean savearea)
1452 if (!(self->functions & Func_Fullscreen) || /* can't */
1453 self->fullscreen == fs) return; /* already done */
1455 self->fullscreen = fs;
1456 client_change_state(self); /* change the state hints on the client */
1459 /* save the functions and remove them */
1460 self->pre_fs_func = self->functions;
1461 self->functions &= (Func_Close | Func_Fullscreen |
1463 /* save the decorations and remove them */
1464 self->pre_fs_decor = self->decorations;
1465 self->decorations = 0;
1468 dimensions[0] = self->area.x;
1469 dimensions[1] = self->area.y;
1470 dimensions[2] = self->area.width;
1471 dimensions[3] = self->area.height;
1473 PROP_SET32A(self->window, openbox_premax, cardinal,
1477 /* these are not actually used cuz client_configure will set them
1478 as appropriate when the window is fullscreened */
1483 self->functions = self->pre_fs_func;
1484 self->decorations = self->pre_fs_decor;
1486 if (PROP_GET32A(self->window, openbox_premax, cardinal,
1494 /* pick some fallbacks... */
1495 x = screen_area(self->desktop)->x +
1496 screen_area(self->desktop)->width / 4;
1497 y = screen_area(self->desktop)->y +
1498 screen_area(self->desktop)->height / 4;
1499 w = screen_area(self->desktop)->width / 2;
1500 h = screen_area(self->desktop)->height / 2;
1504 client_change_allowed_actions(self); /* based on the new _functions */
1506 /* when fullscreening, don't obey things like increments, fill the
1508 client_configure(self, Corner_TopLeft, x, y, w, h, !fs, TRUE);
1510 /* raise (back) into our stacking layer */
1511 stacking_raise(self);
1513 /* try focus us when we go into fullscreen mode */
1517 void client_iconify(Client *self, gboolean iconic, gboolean curdesk)
1519 if (self->iconic == iconic) return; /* nothing to do */
1521 g_message("%sconifying window: 0x%lx", (iconic ? "I" : "Uni"),
1524 self->iconic = iconic;
1527 self->wmstate = IconicState;
1528 self->ignore_unmaps++;
1529 /* we unmap the client itself so that we can get MapRequest events,
1530 and because the ICCCM tells us to! */
1531 XUnmapWindow(ob_display, self->window);
1534 client_set_desktop(self, screen_desktop);
1535 self->wmstate = self->shaded ? IconicState : NormalState;
1536 XMapWindow(ob_display, self->window);
1538 client_change_state(self);
1539 client_showhide(self);
1540 screen_update_struts();
1542 dispatch_client(iconic ? Event_Client_Unmapped : Event_Client_Mapped,
1546 void client_maximize(Client *self, gboolean max, int dir, gboolean savearea)
1550 g_assert(dir == 0 || dir == 1 || dir == 2);
1551 if (!(self->functions & Func_Maximize)) return; /* can't */
1553 /* check if already done */
1555 if (dir == 0 && self->max_horz && self->max_vert) return;
1556 if (dir == 1 && self->max_horz) return;
1557 if (dir == 2 && self->max_vert) return;
1559 if (dir == 0 && !self->max_horz && !self->max_vert) return;
1560 if (dir == 1 && !self->max_horz) return;
1561 if (dir == 2 && !self->max_vert) return;
1564 /* work with the frame's coords */
1565 x = self->frame->area.x;
1566 y = self->frame->area.y;
1567 w = self->area.width;
1568 h = self->area.height;
1580 /* get the property off the window and use it for the dimensions
1581 we are already maxed on */
1582 if (PROP_GET32A(self->window, openbox_premax, cardinal,
1584 if (self->max_horz) {
1585 dimensions[0] = readdim[0];
1586 dimensions[2] = readdim[2];
1588 if (self->max_vert) {
1589 dimensions[1] = readdim[1];
1590 dimensions[3] = readdim[3];
1595 PROP_SET32A(self->window, openbox_premax, cardinal,
1599 /* pass the client's current position info. the client_configure
1600 will move/size stuff as appropriate for a maximized window */
1603 w = self->area.width;
1604 h = self->area.height;
1608 if (PROP_GET32A(self->window, openbox_premax, cardinal,
1610 if (dir == 0 || dir == 1) { /* horz */
1614 if (dir == 0 || dir == 2) { /* vert */
1620 /* pick some fallbacks... */
1621 if (dir == 0 || dir == 1) { /* horz */
1622 x = screen_area(self->desktop)->x +
1623 screen_area(self->desktop)->width / 4;
1624 w = screen_area(self->desktop)->width / 2;
1626 if (dir == 0 || dir == 2) { /* vert */
1627 y = screen_area(self->desktop)->y +
1628 screen_area(self->desktop)->height / 4;
1629 h = screen_area(self->desktop)->height / 2;
1634 if (dir == 0 || dir == 1) /* horz */
1635 self->max_horz = max;
1636 if (dir == 0 || dir == 2) /* vert */
1637 self->max_vert = max;
1639 if (!self->max_horz && !self->max_vert)
1640 PROP_ERASE(self->window, openbox_premax);
1642 client_change_state(self); /* change the state hints on the client */
1644 /* figure out where the client should be going */
1645 frame_frame_gravity(self->frame, &x, &y);
1646 client_configure(self, Corner_TopLeft, x, y, w, h, TRUE, TRUE);
1649 void client_shade(Client *self, gboolean shade)
1651 if (!(self->functions & Func_Shade) || /* can't */
1652 self->shaded == shade) return; /* already done */
1654 /* when we're iconic, don't change the wmstate */
1656 self->wmstate = shade ? IconicState : NormalState;
1657 self->shaded = shade;
1658 client_change_state(self);
1659 /* resize the frame to just the titlebar */
1660 engine_frame_adjust_area(self->frame, FALSE, FALSE);
1663 void client_close(Client *self)
1667 if (!(self->functions & Func_Close)) return;
1670 XXX: itd be cool to do timeouts and shit here for killing the client's
1672 like... if the window is around after 5 seconds, then the close button
1673 turns a nice red, and if this function is called again, the client is
1677 ce.xclient.type = ClientMessage;
1678 ce.xclient.message_type = prop_atoms.wm_protocols;
1679 ce.xclient.display = ob_display;
1680 ce.xclient.window = self->window;
1681 ce.xclient.format = 32;
1682 ce.xclient.data.l[0] = prop_atoms.wm_delete_window;
1683 ce.xclient.data.l[1] = event_lasttime;
1684 ce.xclient.data.l[2] = 0l;
1685 ce.xclient.data.l[3] = 0l;
1686 ce.xclient.data.l[4] = 0l;
1687 XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
1690 void client_kill(Client *self)
1692 XKillClient(ob_display, self->window);
1695 void client_set_desktop(Client *self, guint target)
1699 if (target == self->desktop) return;
1701 g_message("Setting desktop %u\n", target);
1703 g_assert(target < screen_num_desktops || target == DESKTOP_ALL);
1705 old = self->desktop;
1706 self->desktop = target;
1707 PROP_SET32(self->window, net_wm_desktop, cardinal, target);
1708 /* the frame can display the current desktop state */
1709 engine_frame_adjust_state(self->frame);
1710 /* 'move' the window to the new desktop */
1711 client_showhide(self);
1712 screen_update_struts();
1714 /* update the focus lists */
1715 if (old == DESKTOP_ALL) {
1716 for (i = 0; i < screen_num_desktops; ++i)
1717 focus_order[i] = g_list_remove(focus_order[i], self);
1718 focus_order[target] = g_list_prepend(focus_order[target], self);
1720 focus_order[old] = g_list_remove(focus_order[old], self);
1721 if (target == DESKTOP_ALL)
1722 for (i = 0; i < screen_num_desktops; ++i)
1723 focus_order[i] = g_list_prepend(focus_order[i], self);
1726 dispatch_client(Event_Client_Desktop, self, target, old);
1729 static Client *search_modal_tree(Client *node, Client *skip)
1734 for (it = node->transients; it != NULL; it = it->next) {
1735 Client *c = it->data;
1736 if (c == skip) continue; /* circular? */
1737 if ((ret = search_modal_tree(c, skip))) return ret;
1738 if (c->modal) return c;
1743 Client *client_find_modal_child(Client *self)
1745 return search_modal_tree(self, self);
1748 gboolean client_validate(Client *self)
1752 XSync(ob_display, FALSE); /* get all events on the server */
1754 if (XCheckTypedWindowEvent(ob_display, self->window, DestroyNotify, &e) ||
1755 XCheckTypedWindowEvent(ob_display, self->window, UnmapNotify, &e)) {
1756 XPutBackEvent(ob_display, &e);
1763 void client_set_wm_state(Client *self, long state)
1765 if (state == self->wmstate) return; /* no change */
1769 client_iconify(self, TRUE, TRUE);
1772 client_iconify(self, FALSE, TRUE);
1777 void client_set_state(Client *self, Atom action, long data1, long data2)
1779 gboolean shaded = self->shaded;
1780 gboolean fullscreen = self->fullscreen;
1781 gboolean max_horz = self->max_horz;
1782 gboolean max_vert = self->max_vert;
1785 if (!(action == prop_atoms.net_wm_state_add ||
1786 action == prop_atoms.net_wm_state_remove ||
1787 action == prop_atoms.net_wm_state_toggle))
1788 /* an invalid action was passed to the client message, ignore it */
1791 for (i = 0; i < 2; ++i) {
1792 Atom state = i == 0 ? data1 : data2;
1794 if (!state) continue;
1796 /* if toggling, then pick whether we're adding or removing */
1797 if (action == prop_atoms.net_wm_state_toggle) {
1798 if (state == prop_atoms.net_wm_state_modal)
1799 action = self->modal ? prop_atoms.net_wm_state_remove :
1800 prop_atoms.net_wm_state_add;
1801 else if (state == prop_atoms.net_wm_state_maximized_vert)
1802 action = self->max_vert ? prop_atoms.net_wm_state_remove :
1803 prop_atoms.net_wm_state_add;
1804 else if (state == prop_atoms.net_wm_state_maximized_horz)
1805 action = self->max_horz ? prop_atoms.net_wm_state_remove :
1806 prop_atoms.net_wm_state_add;
1807 else if (state == prop_atoms.net_wm_state_shaded)
1808 action = self->shaded ? prop_atoms.net_wm_state_remove :
1809 prop_atoms.net_wm_state_add;
1810 else if (state == prop_atoms.net_wm_state_skip_taskbar)
1811 action = self->skip_taskbar ?
1812 prop_atoms.net_wm_state_remove :
1813 prop_atoms.net_wm_state_add;
1814 else if (state == prop_atoms.net_wm_state_skip_pager)
1815 action = self->skip_pager ?
1816 prop_atoms.net_wm_state_remove :
1817 prop_atoms.net_wm_state_add;
1818 else if (state == prop_atoms.net_wm_state_fullscreen)
1819 action = self->fullscreen ?
1820 prop_atoms.net_wm_state_remove :
1821 prop_atoms.net_wm_state_add;
1822 else if (state == prop_atoms.net_wm_state_above)
1823 action = self->above ? prop_atoms.net_wm_state_remove :
1824 prop_atoms.net_wm_state_add;
1825 else if (state == prop_atoms.net_wm_state_below)
1826 action = self->below ? prop_atoms.net_wm_state_remove :
1827 prop_atoms.net_wm_state_add;
1830 if (action == prop_atoms.net_wm_state_add) {
1831 if (state == prop_atoms.net_wm_state_modal) {
1832 /* XXX raise here or something? */
1834 } else if (state == prop_atoms.net_wm_state_maximized_vert) {
1836 } else if (state == prop_atoms.net_wm_state_maximized_horz) {
1838 } else if (state == prop_atoms.net_wm_state_shaded) {
1840 } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
1841 self->skip_taskbar = TRUE;
1842 } else if (state == prop_atoms.net_wm_state_skip_pager) {
1843 self->skip_pager = TRUE;
1844 } else if (state == prop_atoms.net_wm_state_fullscreen) {
1846 } else if (state == prop_atoms.net_wm_state_above) {
1848 } else if (state == prop_atoms.net_wm_state_below) {
1852 } else { /* action == prop_atoms.net_wm_state_remove */
1853 if (state == prop_atoms.net_wm_state_modal) {
1854 self->modal = FALSE;
1855 } else if (state == prop_atoms.net_wm_state_maximized_vert) {
1857 } else if (state == prop_atoms.net_wm_state_maximized_horz) {
1859 } else if (state == prop_atoms.net_wm_state_shaded) {
1861 } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
1862 self->skip_taskbar = FALSE;
1863 } else if (state == prop_atoms.net_wm_state_skip_pager) {
1864 self->skip_pager = FALSE;
1865 } else if (state == prop_atoms.net_wm_state_fullscreen) {
1867 } else if (state == prop_atoms.net_wm_state_above) {
1868 self->above = FALSE;
1869 } else if (state == prop_atoms.net_wm_state_below) {
1870 self->below = FALSE;
1874 if (max_horz != self->max_horz || max_vert != self->max_vert) {
1875 if (max_horz != self->max_horz && max_vert != self->max_vert) {
1877 if (max_horz == max_vert) { /* both going the same way */
1878 client_maximize(self, max_horz, 0, TRUE);
1880 client_maximize(self, max_horz, 1, TRUE);
1881 client_maximize(self, max_vert, 2, TRUE);
1885 if (max_horz != self->max_horz)
1886 client_maximize(self, max_horz, 1, TRUE);
1888 client_maximize(self, max_vert, 2, TRUE);
1891 /* change fullscreen state before shading, as it will affect if the window
1893 if (fullscreen != self->fullscreen)
1894 client_fullscreen(self, fullscreen, TRUE);
1895 if (shaded != self->shaded)
1896 client_shade(self, shaded);
1897 client_calc_layer(self);
1898 client_change_state(self); /* change the hint to relect these changes */
1901 gboolean client_focus(Client *self)
1906 /* if we have a modal child, then focus it, not us */
1907 child = client_find_modal_child(self);
1909 return client_focus(child);
1911 /* won't try focus if the client doesn't want it, or if the window isn't
1912 visible on the screen */
1913 if (!(self->frame->visible &&
1914 (self->can_focus || self->focus_notify)))
1917 /* do a check to see if the window has already been unmapped or destroyed
1918 do this intelligently while watching out for unmaps we've generated
1919 (ignore_unmaps > 0) */
1920 if (XCheckTypedWindowEvent(ob_display, self->window,
1921 DestroyNotify, &ev)) {
1922 XPutBackEvent(ob_display, &ev);
1925 while (XCheckTypedWindowEvent(ob_display, self->window,
1926 UnmapNotify, &ev)) {
1927 if (self->ignore_unmaps) {
1928 self->ignore_unmaps--;
1930 XPutBackEvent(ob_display, &ev);
1935 if (self->can_focus)
1936 XSetInputFocus(ob_display, self->window, RevertToNone,
1939 if (self->focus_notify) {
1941 ce.xclient.type = ClientMessage;
1942 ce.xclient.message_type = prop_atoms.wm_protocols;
1943 ce.xclient.display = ob_display;
1944 ce.xclient.window = self->window;
1945 ce.xclient.format = 32;
1946 ce.xclient.data.l[0] = prop_atoms.wm_take_focus;
1947 ce.xclient.data.l[1] = CurrentTime;
1948 ce.xclient.data.l[2] = 0l;
1949 ce.xclient.data.l[3] = 0l;
1950 ce.xclient.data.l[4] = 0l;
1951 XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
1954 /* XSync(ob_display, FALSE); XXX Why sync? */
1958 void client_unfocus(Client *self)
1960 g_assert(focus_client == self);
1961 client_set_focused(self, FALSE);
1964 gboolean client_focused(Client *self)
1966 return self == focus_client;
1969 void client_set_focused(Client *self, gboolean focused)
1972 if (focus_client != self)
1973 focus_set_client(self);
1975 if (focus_client == self)
1976 focus_set_client(NULL);
1979 /* focus state can affect the stacking layer */
1980 client_calc_layer(self);
1982 engine_frame_adjust_focus(self->frame);
1985 Icon *client_icon(Client *self, int w, int h)
1988 /* si is the smallest image >= req */
1989 /* li is the largest image < req */
1990 unsigned long size, smallest = 0xffffffff, largest = 0, si = 0, li = 0;
1992 if (!self->nicons) return NULL;
1994 for (i = 0; i < self->nicons; ++i) {
1995 size = self->icons[i].width * self->icons[i].height;
1996 if (size < smallest && size >= (unsigned)(w * h)) {
2000 if (size > largest && size <= (unsigned)(w * h)) {
2005 if (largest == 0) /* didnt find one smaller than the requested size */
2006 return &self->icons[si];
2007 return &self->icons[li];