1 /* -*- indent-tabs-mode: nil; tab-width: 4; c-basic-offset: 4; -*-
3 client.c for the Openbox window manager
4 Copyright (c) 2004 Mikael Magnusson
5 Copyright (c) 2003 Ben Jansens
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 See the COPYING file for a copy of the GNU General Public License.
22 #include "startupnotify.h"
26 #include "moveresize.h"
29 #include "extensions.h"
39 #include "menuframe.h"
42 #include "render/render.h"
45 #include <X11/Xutil.h>
47 /*! The event mask to grab on client windows */
48 #define CLIENT_EVENTMASK (PropertyChangeMask | FocusChangeMask | \
51 #define CLIENT_NOPROPAGATEMASK (ButtonPressMask | ButtonReleaseMask | \
56 ObClientDestructor func;
60 GList *client_list = NULL;
61 GSList *client_destructors = NULL;
63 static void client_get_all(ObClient *self);
64 static void client_toggle_border(ObClient *self, gboolean show);
65 static void client_get_startup_id(ObClient *self);
66 static void client_get_area(ObClient *self);
67 static void client_get_desktop(ObClient *self);
68 static void client_get_state(ObClient *self);
69 static void client_get_shaped(ObClient *self);
70 static void client_get_mwm_hints(ObClient *self);
71 static void client_get_gravity(ObClient *self);
72 static void client_showhide(ObClient *self);
73 static void client_change_allowed_actions(ObClient *self);
74 static void client_change_state(ObClient *self);
75 static void client_apply_startup_state(ObClient *self);
76 static void client_restore_session_state(ObClient *self);
77 static void client_restore_session_stacking(ObClient *self);
78 static void client_urgent_notify(ObClient *self);
80 void client_startup(gboolean reconfig)
87 void client_shutdown(gboolean reconfig)
91 void client_add_destructor(ObClientDestructor func, gpointer data)
93 Destructor *d = g_new(Destructor, 1);
96 client_destructors = g_slist_prepend(client_destructors, d);
99 void client_remove_destructor(ObClientDestructor func)
103 for (it = client_destructors; it; it = g_slist_next(it)) {
104 Destructor *d = it->data;
105 if (d->func == func) {
107 client_destructors = g_slist_delete_link(client_destructors, it);
113 void client_set_list()
115 Window *windows, *win_it;
117 guint size = g_list_length(client_list);
119 /* create an array of the window ids */
121 windows = g_new(Window, size);
123 for (it = client_list; it; it = g_list_next(it), ++win_it)
124 *win_it = ((ObClient*)it->data)->window;
128 PROP_SETA32(RootWindow(ob_display, ob_screen),
129 net_client_list, window, (guint32*)windows, size);
138 void client_foreach_transient(ObClient *self, ObClientForeachFunc func, gpointer data)
142 for (it = self->transients; it; it = g_slist_next(it)) {
143 if (!func(it->data, data)) return;
144 client_foreach_transient(it->data, func, data);
148 void client_foreach_ancestor(ObClient *self, ObClientForeachFunc func, gpointer data)
150 if (self->transient_for) {
151 if (self->transient_for != OB_TRAN_GROUP) {
152 if (!func(self->transient_for, data)) return;
153 client_foreach_ancestor(self->transient_for, func, data);
157 for (it = self->group->members; it; it = g_slist_next(it))
158 if (it->data != self &&
159 !((ObClient*)it->data)->transient_for) {
160 if (!func(it->data, data)) return;
161 client_foreach_ancestor(it->data, func, data);
168 void client_manage_all()
173 XWindowAttributes attrib;
175 XQueryTree(ob_display, RootWindow(ob_display, ob_screen),
176 &w, &w, &children, &nchild);
178 /* remove all icon windows from the list */
179 for (i = 0; i < nchild; i++) {
180 if (children[i] == None) continue;
181 wmhints = XGetWMHints(ob_display, children[i]);
183 if ((wmhints->flags & IconWindowHint) &&
184 (wmhints->icon_window != children[i]))
185 for (j = 0; j < nchild; j++)
186 if (children[j] == wmhints->icon_window) {
194 for (i = 0; i < nchild; ++i) {
195 if (children[i] == None)
197 if (XGetWindowAttributes(ob_display, children[i], &attrib)) {
198 if (attrib.override_redirect) continue;
200 if (attrib.map_state != IsUnmapped)
201 client_manage(children[i]);
207 void client_manage(Window window)
211 XWindowAttributes attrib;
212 XSetWindowAttributes attrib_set;
214 gboolean activate = FALSE;
218 /* check if it has already been unmapped by the time we started mapping
219 the grab does a sync so we don't have to here */
220 if (XCheckTypedWindowEvent(ob_display, window, DestroyNotify, &e) ||
221 XCheckTypedWindowEvent(ob_display, window, UnmapNotify, &e)) {
222 XPutBackEvent(ob_display, &e);
225 return; /* don't manage it */
228 /* make sure it isn't an override-redirect window */
229 if (!XGetWindowAttributes(ob_display, window, &attrib) ||
230 attrib.override_redirect) {
232 return; /* don't manage it */
235 /* is the window a docking app */
236 if ((wmhint = XGetWMHints(ob_display, window))) {
237 if ((wmhint->flags & StateHint) &&
238 wmhint->initial_state == WithdrawnState) {
239 dock_add(window, wmhint);
247 ob_debug("Managing window: %lx\n", window);
249 /* choose the events we want to receive on the CLIENT window */
250 attrib_set.event_mask = CLIENT_EVENTMASK;
251 attrib_set.do_not_propagate_mask = CLIENT_NOPROPAGATEMASK;
252 XChangeWindowAttributes(ob_display, window,
253 CWEventMask|CWDontPropagate, &attrib_set);
256 /* create the ObClient struct, and populate it from the hints on the
258 self = g_new0(ObClient, 1);
259 self->obwin.type = Window_Client;
260 self->window = window;
262 /* non-zero defaults */
263 self->title_count = 1;
264 self->wmstate = NormalState;
266 self->desktop = screen_num_desktops; /* always an invalid value */
268 client_get_all(self);
269 client_restore_session_state(self);
271 sn_app_started(self->class);
273 /* update the focus lists, do this before the call to change_state or
274 it can end up in the list twice! */
275 focus_order_add_new(self);
277 client_change_state(self);
279 /* remove the client's border (and adjust re gravity) */
280 client_toggle_border(self, FALSE);
282 /* specify that if we exit, the window should not be destroyed and should
283 be reparented back to root automatically */
284 XChangeSaveSet(ob_display, window, SetModeInsert);
286 /* create the decoration frame for the client window */
287 self->frame = frame_new();
289 frame_grab_client(self->frame, self);
293 client_apply_startup_state(self);
295 stacking_add(CLIENT_AS_WINDOW(self));
296 client_restore_session_stacking(self);
298 /* focus the new window? */
299 if (ob_state() != OB_STATE_STARTING &&
300 (config_focus_new || client_search_focus_parent(self)) &&
301 /* note the check against Type_Normal/Dialog, not client_normal(self),
302 which would also include other types. in this case we want more
303 strict rules for focus */
304 (self->type == OB_CLIENT_TYPE_NORMAL ||
305 self->type == OB_CLIENT_TYPE_DIALOG))
309 if (self->desktop != screen_desktop) {
310 /* activate the window */
313 gboolean group_foc = FALSE;
318 for (it = self->group->members; it; it = g_slist_next(it))
320 if (client_focused(it->data))
328 (!self->transient_for && (!self->group ||
329 !self->group->members->next))) ||
330 client_search_focus_tree_full(self) ||
332 !client_normal(focus_client))
334 /* activate the window */
341 if (ob_state() == OB_STATE_RUNNING) {
342 gint x = self->area.x, ox = x;
343 gint y = self->area.y, oy = y;
345 place_client(self, &x, &y);
347 if (x != ox || y != oy)
348 client_move(self, x, y);
351 keyboard_grab_for_client(self, TRUE);
352 mouse_grab_for_client(self, TRUE);
354 client_showhide(self);
356 /* use client_focus instead of client_activate cuz client_activate does
357 stuff like switch desktops etc and I'm not interested in all that when
358 a window maps since its not based on an action from the user like
359 clicking a window to activate is. so keep the new window out of the way
362 /* if using focus_delay, stop the timer now so that focus doesn't go
364 event_halt_focus_delay();
367 /* since focus can change the stacking orders, if we focus the window
368 then the standard raise it gets is not enough, we need to queue one
369 for after the focus change takes place */
373 /* client_activate does this but we aret using it so we have to do it
375 if (screen_showing_desktop)
376 screen_show_desktop(FALSE);
378 /* add to client list/map */
379 client_list = g_list_append(client_list, self);
380 g_hash_table_insert(window_map, &self->window, self);
382 /* this has to happen after we're in the client_list */
383 screen_update_areas();
385 /* update the list hints */
388 ob_debug("Managed window 0x%lx (%s)\n", window, self->class);
391 void client_unmanage_all()
393 while (client_list != NULL)
394 client_unmanage(client_list->data);
397 void client_unmanage(ObClient *self)
402 ob_debug("Unmanaging window: %lx (%s)\n", self->window, self->class);
404 g_assert(self != NULL);
406 keyboard_grab_for_client(self, FALSE);
407 mouse_grab_for_client(self, FALSE);
409 /* potentially fix focusLast */
410 if (config_focus_last)
411 grab_pointer(TRUE, OB_CURSOR_NONE);
413 /* remove the window from our save set */
414 XChangeSaveSet(ob_display, self->window, SetModeDelete);
416 /* we dont want events no more */
417 XSelectInput(ob_display, self->window, NoEventMask);
419 frame_hide(self->frame);
421 client_list = g_list_remove(client_list, self);
422 stacking_remove(self);
423 g_hash_table_remove(window_map, &self->window);
425 /* update the focus lists */
426 focus_order_remove(self);
428 /* once the client is out of the list, update the struts to remove it's
430 screen_update_areas();
432 for (it = client_destructors; it; it = g_slist_next(it)) {
433 Destructor *d = it->data;
434 d->func(self, d->data);
437 if (focus_client == self) {
440 /* focus the last focused window on the desktop, and ignore enter
441 events from the unmap so it doesnt mess with the focus */
442 while (XCheckTypedEvent(ob_display, EnterNotify, &e));
443 /* remove these flags so we don't end up getting focused in the
445 self->can_focus = FALSE;
446 self->focus_notify = FALSE;
448 client_unfocus(self);
451 /* tell our parent(s) that we're gone */
452 if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */
453 for (it = self->group->members; it; it = g_slist_next(it))
454 if (it->data != self)
455 ((ObClient*)it->data)->transients =
456 g_slist_remove(((ObClient*)it->data)->transients, self);
457 } else if (self->transient_for) { /* transient of window */
458 self->transient_for->transients =
459 g_slist_remove(self->transient_for->transients, self);
462 /* tell our transients that we're gone */
463 for (it = self->transients; it; it = g_slist_next(it)) {
464 if (((ObClient*)it->data)->transient_for != OB_TRAN_GROUP) {
465 ((ObClient*)it->data)->transient_for = NULL;
466 client_calc_layer(it->data);
470 /* remove from its group */
472 group_remove(self->group, self);
476 /* give the client its border back */
477 client_toggle_border(self, TRUE);
479 /* reparent the window out of the frame, and free the frame */
480 frame_release_client(self->frame, self);
483 if (ob_state() != OB_STATE_EXITING) {
484 /* these values should not be persisted across a window
486 PROP_ERASE(self->window, net_wm_desktop);
487 PROP_ERASE(self->window, net_wm_state);
488 PROP_ERASE(self->window, wm_state);
490 /* if we're left in an unmapped state, the client wont be mapped. this
491 is bad, since we will no longer be managing the window on restart */
492 XMapWindow(ob_display, self->window);
496 ob_debug("Unmanaged window 0x%lx\n", self->window);
498 /* free all data allocated in the client struct */
499 g_slist_free(self->transients);
500 for (j = 0; j < self->nicons; ++j)
501 g_free(self->icons[j].data);
502 if (self->nicons > 0)
505 g_free(self->icon_title);
509 g_free(self->sm_client_id);
512 /* update the list hints */
515 if (config_focus_last)
516 grab_pointer(FALSE, OB_CURSOR_NONE);
519 static void client_urgent_notify(ObClient *self)
522 frame_flash_start(self->frame);
524 frame_flash_stop(self->frame);
527 static void client_restore_session_state(ObClient *self)
531 if (!(it = session_state_find(self)))
534 self->session = it->data;
536 RECT_SET_POINT(self->area, self->session->x, self->session->y);
537 self->positioned = TRUE;
538 if (self->session->w > 0)
539 self->area.width = self->session->w;
540 if (self->session->h > 0)
541 self->area.height = self->session->h;
542 XResizeWindow(ob_display, self->window,
543 self->area.width, self->area.height);
545 self->desktop = (self->session->desktop == DESKTOP_ALL ?
546 self->session->desktop :
547 MIN(screen_num_desktops - 1, self->session->desktop));
548 PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop);
550 self->shaded = self->session->shaded;
551 self->iconic = self->session->iconic;
552 self->skip_pager = self->session->skip_pager;
553 self->skip_taskbar = self->session->skip_taskbar;
554 self->fullscreen = self->session->fullscreen;
555 self->above = self->session->above;
556 self->below = self->session->below;
557 self->max_horz = self->session->max_horz;
558 self->max_vert = self->session->max_vert;
561 static void client_restore_session_stacking(ObClient *self)
565 if (!self->session) return;
567 it = g_list_find(session_saved_state, self->session);
568 for (it = g_list_previous(it); it; it = g_list_previous(it)) {
571 for (cit = client_list; cit; cit = g_list_next(cit))
572 if (session_state_cmp(it->data, cit->data))
575 client_calc_layer(self);
576 stacking_below(CLIENT_AS_WINDOW(self),
577 CLIENT_AS_WINDOW(cit->data));
583 void client_move_onscreen(ObClient *self, gboolean rude)
585 gint x = self->area.x;
586 gint y = self->area.y;
587 if (client_find_onscreen(self, &x, &y,
588 self->frame->area.width,
589 self->frame->area.height, rude)) {
590 client_move(self, x, y);
594 gboolean client_find_onscreen(ObClient *self, gint *x, gint *y, gint w, gint h,
598 gint ox = *x, oy = *y;
600 frame_client_gravity(self->frame, x, y); /* get where the frame
603 /* XXX watch for xinerama dead areas */
604 /* This makes sure windows aren't entirely outside of the screen so you
605 * can't see them at all */
606 a = screen_area(self->desktop);
607 if (client_normal(self)) {
608 if (!self->strut.right && *x >= a->x + a->width - 1)
609 *x = a->x + a->width - self->frame->area.width;
610 if (!self->strut.bottom && *y >= a->y + a->height - 1)
611 *y = a->y + a->height - self->frame->area.height;
612 if (!self->strut.left && *x + self->frame->area.width - 1 < a->x)
614 if (!self->strut.top && *y + self->frame->area.height - 1 < a->y)
618 /* This here doesn't let windows even a pixel outside the screen,
619 * not applied to all windows. Not sure if it's going to stay at all.
620 * I wonder if disabling this will break struts somehow? Let's find out. */
622 /* avoid the xinerama monitor divide while we're at it,
623 * remember to fix the placement stuff to avoid it also and
624 * then remove this XXX */
625 a = screen_physical_area_monitor(client_monitor(self));
626 /* this is ben's MOZILLA BITCHSLAP. "oh ya it fucking feels good.
627 Java can suck it too." */
629 /* dont let windows map/move into the strut unless they
630 are bigger than the available area */
632 if (!self->strut.left && *x < a->x) *x = a->x;
633 if (!self->strut.right && *x + w > a->x + a->width)
634 *x = a->x + a->width - w;
636 if (h <= a->height) {
637 if (!self->strut.top && *y < a->y) *y = a->y;
638 if (!self->strut.bottom && *y + h > a->y + a->height)
639 *y = a->y + a->height - h;
643 frame_frame_gravity(self->frame, x, y); /* get where the client
646 return ox != *x || oy != *y;
649 static void client_toggle_border(ObClient *self, gboolean show)
651 /* adjust our idea of where the client is, based on its border. When the
652 border is removed, the client should now be considered to be in a
654 when re-adding the border to the client, the same operation needs to be
656 gint oldx = self->area.x, oldy = self->area.y;
657 gint x = oldx, y = oldy;
658 switch(self->gravity) {
660 case NorthWestGravity:
662 case SouthWestGravity:
664 case NorthEastGravity:
666 case SouthEastGravity:
667 if (show) x -= self->border_width * 2;
668 else x += self->border_width * 2;
675 if (show) x -= self->border_width;
676 else x += self->border_width;
679 switch(self->gravity) {
681 case NorthWestGravity:
683 case NorthEastGravity:
685 case SouthWestGravity:
687 case SouthEastGravity:
688 if (show) y -= self->border_width * 2;
689 else y += self->border_width * 2;
696 if (show) y -= self->border_width;
697 else y += self->border_width;
704 XSetWindowBorderWidth(ob_display, self->window, self->border_width);
706 /* move the client so it is back it the right spot _with_ its
708 if (x != oldx || y != oldy)
709 XMoveWindow(ob_display, self->window, x, y);
711 XSetWindowBorderWidth(ob_display, self->window, 0);
715 static void client_get_all(ObClient *self)
717 client_get_area(self);
718 client_update_transient_for(self);
719 client_update_wmhints(self);
720 client_get_startup_id(self);
721 client_get_desktop(self);
722 client_get_shaped(self);
724 client_get_mwm_hints(self);
725 client_get_type(self);/* this can change the mwmhints for special cases */
727 /* The transient hint is used to pick a type, but the type can also affect
728 transiency (dialogs are always made transients). This is Havoc's idea,
729 but it is needed to make some apps work right (eg tsclient). */
730 client_update_transient_for(self);
732 client_get_state(self);
735 /* a couple type-based defaults for new windows */
737 /* this makes sure that these windows appear on all desktops */
738 if (self->type == OB_CLIENT_TYPE_DESKTOP)
739 self->desktop = DESKTOP_ALL;
742 client_update_protocols(self);
744 client_get_gravity(self); /* get the attribute gravity */
745 client_update_normal_hints(self); /* this may override the attribute
748 /* got the type, the mwmhints, the protocols, and the normal hints
749 (min/max sizes), so we're ready to set up the decorations/functions */
750 client_setup_decor_and_functions(self);
752 client_update_title(self);
753 client_update_class(self);
754 client_update_sm_client_id(self);
755 client_update_strut(self);
756 client_update_icons(self);
759 static void client_get_startup_id(ObClient *self)
761 if (!(PROP_GETS(self->window, net_startup_id, utf8, &self->startup_id)))
763 PROP_GETS(self->group->leader,
764 net_startup_id, utf8, &self->startup_id);
767 static void client_get_area(ObClient *self)
769 XWindowAttributes wattrib;
772 ret = XGetWindowAttributes(ob_display, self->window, &wattrib);
773 g_assert(ret != BadWindow);
775 RECT_SET(self->area, wattrib.x, wattrib.y, wattrib.width, wattrib.height);
776 self->border_width = wattrib.border_width;
779 static void client_get_desktop(ObClient *self)
781 guint32 d = screen_num_desktops; /* an always-invalid value */
783 if (PROP_GET32(self->window, net_wm_desktop, cardinal, &d)) {
784 if (d >= screen_num_desktops && d != DESKTOP_ALL)
785 self->desktop = screen_num_desktops - 1;
789 gboolean trdesk = FALSE;
791 if (self->transient_for) {
792 if (self->transient_for != OB_TRAN_GROUP) {
793 self->desktop = self->transient_for->desktop;
798 for (it = self->group->members; it; it = g_slist_next(it))
799 if (it->data != self &&
800 !((ObClient*)it->data)->transient_for) {
801 self->desktop = ((ObClient*)it->data)->desktop;
808 /* try get from the startup-notification protocol */
809 if (sn_get_desktop(self->startup_id, &self->desktop)) {
810 if (self->desktop >= screen_num_desktops &&
811 self->desktop != DESKTOP_ALL)
812 self->desktop = screen_num_desktops - 1;
814 /* defaults to the current desktop */
815 self->desktop = screen_desktop;
818 if (self->desktop != d) {
819 /* set the desktop hint, to make sure that it always exists */
820 PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop);
824 static void client_get_state(ObClient *self)
829 if (PROP_GETA32(self->window, net_wm_state, atom, &state, &num)) {
831 for (i = 0; i < num; ++i) {
832 if (state[i] == prop_atoms.net_wm_state_modal)
834 else if (state[i] == prop_atoms.net_wm_state_shaded)
836 else if (state[i] == prop_atoms.net_wm_state_hidden)
838 else if (state[i] == prop_atoms.net_wm_state_skip_taskbar)
839 self->skip_taskbar = TRUE;
840 else if (state[i] == prop_atoms.net_wm_state_skip_pager)
841 self->skip_pager = TRUE;
842 else if (state[i] == prop_atoms.net_wm_state_fullscreen)
843 self->fullscreen = TRUE;
844 else if (state[i] == prop_atoms.net_wm_state_maximized_vert)
845 self->max_vert = TRUE;
846 else if (state[i] == prop_atoms.net_wm_state_maximized_horz)
847 self->max_horz = TRUE;
848 else if (state[i] == prop_atoms.net_wm_state_above)
850 else if (state[i] == prop_atoms.net_wm_state_below)
852 else if (state[i] == prop_atoms.ob_wm_state_undecorated)
853 self->undecorated = TRUE;
859 if (!(self->above || self->below)) {
861 /* apply stuff from the group */
865 for (it = self->group->members; it; it = g_slist_next(it)) {
866 ObClient *c = it->data;
867 if (c != self && !client_search_transient(self, c) &&
868 client_normal(self) && client_normal(c))
871 (c->above ? 1 : (c->below ? -1 : 0)));
885 g_assert_not_reached();
892 static void client_get_shaped(ObClient *self)
894 self->shaped = FALSE;
896 if (extensions_shape) {
901 XShapeSelectInput(ob_display, self->window, ShapeNotifyMask);
903 XShapeQueryExtents(ob_display, self->window, &s, &foo,
904 &foo, &ufoo, &ufoo, &foo, &foo, &foo, &ufoo,
906 self->shaped = (s != 0);
911 void client_update_transient_for(ObClient *self)
914 ObClient *target = NULL;
916 if (XGetTransientForHint(ob_display, self->window, &t)) {
917 self->transient = TRUE;
918 if (t != self->window) { /* cant be transient to itself! */
919 target = g_hash_table_lookup(window_map, &t);
920 /* if this happens then we need to check for it*/
921 g_assert(target != self);
922 if (target && !WINDOW_IS_CLIENT(target)) {
923 /* this can happen when a dialog is a child of
924 a dockapp, for example */
928 if (!target && self->group) {
929 /* not transient to a client, see if it is transient for a
931 if (t == self->group->leader ||
933 t == RootWindow(ob_display, ob_screen))
935 /* window is a transient for its group! */
936 target = OB_TRAN_GROUP;
940 } else if (self->type == OB_CLIENT_TYPE_DIALOG && self->group) {
941 self->transient = TRUE;
942 target = OB_TRAN_GROUP;
944 self->transient = FALSE;
946 /* if anything has changed... */
947 if (target != self->transient_for) {
948 if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */
951 /* remove from old parents */
952 for (it = self->group->members; it; it = g_slist_next(it)) {
953 ObClient *c = it->data;
954 if (c != self && !c->transient_for)
955 c->transients = g_slist_remove(c->transients, self);
957 } else if (self->transient_for != NULL) { /* transient of window */
958 /* remove from old parent */
959 self->transient_for->transients =
960 g_slist_remove(self->transient_for->transients, self);
962 self->transient_for = target;
963 if (self->transient_for == OB_TRAN_GROUP) { /* transient of group */
966 /* add to new parents */
967 for (it = self->group->members; it; it = g_slist_next(it)) {
968 ObClient *c = it->data;
969 if (c != self && !c->transient_for)
970 c->transients = g_slist_append(c->transients, self);
973 /* remove all transients which are in the group, that causes
974 circlular pointer hell of doom */
975 for (it = self->group->members; it; it = g_slist_next(it)) {
977 for (sit = self->transients; sit; sit = next) {
978 next = g_slist_next(sit);
979 if (sit->data == it->data)
981 g_slist_delete_link(self->transients, sit);
984 } else if (self->transient_for != NULL) { /* transient of window */
985 /* add to new parent */
986 self->transient_for->transients =
987 g_slist_append(self->transient_for->transients, self);
992 static void client_get_mwm_hints(ObClient *self)
997 self->mwmhints.flags = 0; /* default to none */
999 if (PROP_GETA32(self->window, motif_wm_hints, motif_wm_hints,
1001 if (num >= OB_MWM_ELEMENTS) {
1002 self->mwmhints.flags = hints[0];
1003 self->mwmhints.functions = hints[1];
1004 self->mwmhints.decorations = hints[2];
1010 void client_get_type(ObClient *self)
1017 if (PROP_GETA32(self->window, net_wm_window_type, atom, &val, &num)) {
1018 /* use the first value that we know about in the array */
1019 for (i = 0; i < num; ++i) {
1020 if (val[i] == prop_atoms.net_wm_window_type_desktop)
1021 self->type = OB_CLIENT_TYPE_DESKTOP;
1022 else if (val[i] == prop_atoms.net_wm_window_type_dock)
1023 self->type = OB_CLIENT_TYPE_DOCK;
1024 else if (val[i] == prop_atoms.net_wm_window_type_toolbar)
1025 self->type = OB_CLIENT_TYPE_TOOLBAR;
1026 else if (val[i] == prop_atoms.net_wm_window_type_menu)
1027 self->type = OB_CLIENT_TYPE_MENU;
1028 else if (val[i] == prop_atoms.net_wm_window_type_utility)
1029 self->type = OB_CLIENT_TYPE_UTILITY;
1030 else if (val[i] == prop_atoms.net_wm_window_type_splash)
1031 self->type = OB_CLIENT_TYPE_SPLASH;
1032 else if (val[i] == prop_atoms.net_wm_window_type_dialog)
1033 self->type = OB_CLIENT_TYPE_DIALOG;
1034 else if (val[i] == prop_atoms.net_wm_window_type_normal)
1035 self->type = OB_CLIENT_TYPE_NORMAL;
1036 else if (val[i] == prop_atoms.kde_net_wm_window_type_override) {
1037 /* prevent this window from getting any decor or
1039 self->mwmhints.flags &= (OB_MWM_FLAG_FUNCTIONS |
1040 OB_MWM_FLAG_DECORATIONS);
1041 self->mwmhints.decorations = 0;
1042 self->mwmhints.functions = 0;
1044 if (self->type != (ObClientType) -1)
1045 break; /* grab the first legit type */
1050 if (self->type == (ObClientType) -1) {
1051 /*the window type hint was not set, which means we either classify
1052 ourself as a normal window or a dialog, depending on if we are a
1054 if (self->transient)
1055 self->type = OB_CLIENT_TYPE_DIALOG;
1057 self->type = OB_CLIENT_TYPE_NORMAL;
1061 void client_update_protocols(ObClient *self)
1064 guint num_return, i;
1066 self->focus_notify = FALSE;
1067 self->delete_window = FALSE;
1069 if (PROP_GETA32(self->window, wm_protocols, atom, &proto, &num_return)) {
1070 for (i = 0; i < num_return; ++i) {
1071 if (proto[i] == prop_atoms.wm_delete_window) {
1072 /* this means we can request the window to close */
1073 self->delete_window = TRUE;
1074 } else if (proto[i] == prop_atoms.wm_take_focus)
1075 /* if this protocol is requested, then the window will be
1076 notified whenever we want it to receive focus */
1077 self->focus_notify = TRUE;
1083 static void client_get_gravity(ObClient *self)
1085 XWindowAttributes wattrib;
1088 ret = XGetWindowAttributes(ob_display, self->window, &wattrib);
1089 g_assert(ret != BadWindow);
1090 self->gravity = wattrib.win_gravity;
1093 void client_update_normal_hints(ObClient *self)
1097 gint oldgravity = self->gravity;
1100 self->min_ratio = 0.0f;
1101 self->max_ratio = 0.0f;
1102 SIZE_SET(self->size_inc, 1, 1);
1103 SIZE_SET(self->base_size, 0, 0);
1104 SIZE_SET(self->min_size, 0, 0);
1105 SIZE_SET(self->max_size, G_MAXINT, G_MAXINT);
1107 /* get the hints from the window */
1108 if (XGetWMNormalHints(ob_display, self->window, &size, &ret)) {
1109 /* normal windows can't request placement! har har
1110 if (!client_normal(self))
1112 self->positioned = !!(size.flags & (PPosition|USPosition));
1114 if (size.flags & PWinGravity) {
1115 self->gravity = size.win_gravity;
1117 /* if the client has a frame, i.e. has already been mapped and
1118 is changing its gravity */
1119 if (self->frame && self->gravity != oldgravity) {
1120 /* move our idea of the client's position based on its new
1122 self->area.x = self->frame->area.x;
1123 self->area.y = self->frame->area.y;
1124 frame_frame_gravity(self->frame, &self->area.x, &self->area.y);
1128 if (size.flags & PAspect) {
1129 if (size.min_aspect.y)
1131 (gfloat) size.min_aspect.x / size.min_aspect.y;
1132 if (size.max_aspect.y)
1134 (gfloat) size.max_aspect.x / size.max_aspect.y;
1137 if (size.flags & PMinSize)
1138 SIZE_SET(self->min_size, size.min_width, size.min_height);
1140 if (size.flags & PMaxSize)
1141 SIZE_SET(self->max_size, size.max_width, size.max_height);
1143 if (size.flags & PBaseSize)
1144 SIZE_SET(self->base_size, size.base_width, size.base_height);
1146 if (size.flags & PResizeInc)
1147 SIZE_SET(self->size_inc, size.width_inc, size.height_inc);
1151 void client_setup_decor_and_functions(ObClient *self)
1153 /* start with everything (cept fullscreen) */
1155 (OB_FRAME_DECOR_TITLEBAR |
1156 (ob_rr_theme->show_handle ? OB_FRAME_DECOR_HANDLE : 0) |
1157 OB_FRAME_DECOR_GRIPS |
1158 OB_FRAME_DECOR_BORDER |
1159 OB_FRAME_DECOR_ICON |
1160 OB_FRAME_DECOR_ALLDESKTOPS |
1161 OB_FRAME_DECOR_ICONIFY |
1162 OB_FRAME_DECOR_MAXIMIZE |
1163 OB_FRAME_DECOR_SHADE |
1164 OB_FRAME_DECOR_CLOSE);
1166 (OB_CLIENT_FUNC_RESIZE |
1167 OB_CLIENT_FUNC_MOVE |
1168 OB_CLIENT_FUNC_ICONIFY |
1169 OB_CLIENT_FUNC_MAXIMIZE |
1170 OB_CLIENT_FUNC_SHADE |
1171 OB_CLIENT_FUNC_CLOSE);
1173 if (!(self->min_size.width < self->max_size.width ||
1174 self->min_size.height < self->max_size.height))
1175 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1177 switch (self->type) {
1178 case OB_CLIENT_TYPE_NORMAL:
1179 /* normal windows retain all of the possible decorations and
1180 functionality, and are the only windows that you can fullscreen */
1181 self->functions |= OB_CLIENT_FUNC_FULLSCREEN;
1184 case OB_CLIENT_TYPE_DIALOG:
1185 case OB_CLIENT_TYPE_UTILITY:
1186 /* these windows cannot be maximized */
1187 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1190 case OB_CLIENT_TYPE_MENU:
1191 case OB_CLIENT_TYPE_TOOLBAR:
1192 /* these windows get less functionality */
1193 self->functions &= ~(OB_CLIENT_FUNC_ICONIFY | OB_CLIENT_FUNC_RESIZE);
1196 case OB_CLIENT_TYPE_DESKTOP:
1197 case OB_CLIENT_TYPE_DOCK:
1198 case OB_CLIENT_TYPE_SPLASH:
1199 /* none of these windows are manipulated by the window manager */
1200 self->decorations = 0;
1201 self->functions = 0;
1205 /* Mwm Hints are applied subtractively to what has already been chosen for
1206 decor and functionality */
1207 if (self->mwmhints.flags & OB_MWM_FLAG_DECORATIONS) {
1208 if (! (self->mwmhints.decorations & OB_MWM_DECOR_ALL)) {
1209 if (! ((self->mwmhints.decorations & OB_MWM_DECOR_HANDLE) ||
1210 (self->mwmhints.decorations & OB_MWM_DECOR_TITLE)))
1211 /* if the mwm hints request no handle or title, then all
1212 decorations are disabled */
1213 self->decorations = 0;
1217 if (self->mwmhints.flags & OB_MWM_FLAG_FUNCTIONS) {
1218 if (! (self->mwmhints.functions & OB_MWM_FUNC_ALL)) {
1219 if (! (self->mwmhints.functions & OB_MWM_FUNC_RESIZE))
1220 self->functions &= ~OB_CLIENT_FUNC_RESIZE;
1221 if (! (self->mwmhints.functions & OB_MWM_FUNC_MOVE))
1222 self->functions &= ~OB_CLIENT_FUNC_MOVE;
1223 /* dont let mwm hints kill any buttons
1224 if (! (self->mwmhints.functions & OB_MWM_FUNC_ICONIFY))
1225 self->functions &= ~OB_CLIENT_FUNC_ICONIFY;
1226 if (! (self->mwmhints.functions & OB_MWM_FUNC_MAXIMIZE))
1227 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1229 /* dont let mwm hints kill the close button
1230 if (! (self->mwmhints.functions & MwmFunc_Close))
1231 self->functions &= ~OB_CLIENT_FUNC_CLOSE; */
1235 if (!(self->functions & OB_CLIENT_FUNC_SHADE))
1236 self->decorations &= ~OB_FRAME_DECOR_SHADE;
1237 if (!(self->functions & OB_CLIENT_FUNC_ICONIFY))
1238 self->decorations &= ~OB_FRAME_DECOR_ICONIFY;
1239 if (!(self->functions & OB_CLIENT_FUNC_RESIZE))
1240 self->decorations &= ~OB_FRAME_DECOR_GRIPS;
1242 /* can't maximize without moving/resizing */
1243 if (!((self->functions & OB_CLIENT_FUNC_MAXIMIZE) &&
1244 (self->functions & OB_CLIENT_FUNC_MOVE) &&
1245 (self->functions & OB_CLIENT_FUNC_RESIZE))) {
1246 self->functions &= ~OB_CLIENT_FUNC_MAXIMIZE;
1247 self->decorations &= ~OB_FRAME_DECOR_MAXIMIZE;
1250 /* kill the handle on fully maxed windows */
1251 if (self->max_vert && self->max_horz)
1252 self->decorations &= ~OB_FRAME_DECOR_HANDLE;
1254 /* finally, the user can have requested no decorations, which overrides
1255 everything (but doesnt give it a border if it doesnt have one) */
1256 if (self->undecorated) {
1257 if (config_theme_keepborder)
1258 self->decorations &= OB_FRAME_DECOR_BORDER;
1260 self->decorations = 0;
1263 /* if we don't have a titlebar, then we cannot shade! */
1264 if (!(self->decorations & OB_FRAME_DECOR_TITLEBAR))
1265 self->functions &= ~OB_CLIENT_FUNC_SHADE;
1267 /* now we need to check against rules for the client's current state */
1268 if (self->fullscreen) {
1269 self->functions &= (OB_CLIENT_FUNC_CLOSE |
1270 OB_CLIENT_FUNC_FULLSCREEN |
1271 OB_CLIENT_FUNC_ICONIFY);
1272 self->decorations = 0;
1275 client_change_allowed_actions(self);
1278 /* adjust the client's decorations, etc. */
1279 client_reconfigure(self);
1283 static void client_change_allowed_actions(ObClient *self)
1288 /* desktop windows are kept on all desktops */
1289 if (self->type != OB_CLIENT_TYPE_DESKTOP)
1290 actions[num++] = prop_atoms.net_wm_action_change_desktop;
1292 if (self->functions & OB_CLIENT_FUNC_SHADE)
1293 actions[num++] = prop_atoms.net_wm_action_shade;
1294 if (self->functions & OB_CLIENT_FUNC_CLOSE)
1295 actions[num++] = prop_atoms.net_wm_action_close;
1296 if (self->functions & OB_CLIENT_FUNC_MOVE)
1297 actions[num++] = prop_atoms.net_wm_action_move;
1298 if (self->functions & OB_CLIENT_FUNC_ICONIFY)
1299 actions[num++] = prop_atoms.net_wm_action_minimize;
1300 if (self->functions & OB_CLIENT_FUNC_RESIZE)
1301 actions[num++] = prop_atoms.net_wm_action_resize;
1302 if (self->functions & OB_CLIENT_FUNC_FULLSCREEN)
1303 actions[num++] = prop_atoms.net_wm_action_fullscreen;
1304 if (self->functions & OB_CLIENT_FUNC_MAXIMIZE) {
1305 actions[num++] = prop_atoms.net_wm_action_maximize_horz;
1306 actions[num++] = prop_atoms.net_wm_action_maximize_vert;
1309 PROP_SETA32(self->window, net_wm_allowed_actions, atom, actions, num);
1311 /* make sure the window isn't breaking any rules now */
1313 if (!(self->functions & OB_CLIENT_FUNC_SHADE) && self->shaded) {
1314 if (self->frame) client_shade(self, FALSE);
1315 else self->shaded = FALSE;
1317 if (!(self->functions & OB_CLIENT_FUNC_ICONIFY) && self->iconic) {
1318 if (self->frame) client_iconify(self, FALSE, TRUE);
1319 else self->iconic = FALSE;
1321 if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) && self->fullscreen) {
1322 if (self->frame) client_fullscreen(self, FALSE, TRUE);
1323 else self->fullscreen = FALSE;
1325 if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE) && (self->max_horz ||
1327 if (self->frame) client_maximize(self, FALSE, 0, TRUE);
1328 else self->max_vert = self->max_horz = FALSE;
1332 void client_reconfigure(ObClient *self)
1334 /* by making this pass FALSE for user, we avoid the emacs event storm where
1335 every configurenotify causes an update in its normal hints, i think this
1336 is generally what we want anyways... */
1337 client_configure(self, OB_CORNER_TOPLEFT, self->area.x, self->area.y,
1338 self->area.width, self->area.height, FALSE, TRUE);
1341 void client_update_wmhints(ObClient *self)
1344 gboolean ur = FALSE;
1347 /* assume a window takes input if it doesnt specify */
1348 self->can_focus = TRUE;
1350 if ((hints = XGetWMHints(ob_display, self->window)) != NULL) {
1351 if (hints->flags & InputHint)
1352 self->can_focus = hints->input;
1354 /* only do this when first managing the window *AND* when we aren't
1356 if (ob_state() != OB_STATE_STARTING && self->frame == NULL)
1357 if (hints->flags & StateHint)
1358 self->iconic = hints->initial_state == IconicState;
1360 if (hints->flags & XUrgencyHint)
1363 if (!(hints->flags & WindowGroupHint))
1364 hints->window_group = None;
1366 /* did the group state change? */
1367 if (hints->window_group !=
1368 (self->group ? self->group->leader : None)) {
1369 /* remove from the old group if there was one */
1370 if (self->group != NULL) {
1371 /* remove transients of the group */
1372 for (it = self->group->members; it; it = g_slist_next(it))
1373 self->transients = g_slist_remove(self->transients,
1376 /* remove myself from parents in the group */
1377 if (self->transient_for == OB_TRAN_GROUP) {
1378 for (it = self->group->members; it;
1379 it = g_slist_next(it))
1381 ObClient *c = it->data;
1383 if (c != self && !c->transient_for)
1384 c->transients = g_slist_remove(c->transients,
1389 group_remove(self->group, self);
1392 if (hints->window_group != None) {
1393 self->group = group_add(hints->window_group, self);
1395 /* i can only have transients from the group if i am not
1397 if (!self->transient_for) {
1398 /* add other transients of the group that are already
1400 for (it = self->group->members; it;
1401 it = g_slist_next(it))
1403 ObClient *c = it->data;
1404 if (c != self && c->transient_for == OB_TRAN_GROUP)
1406 g_slist_append(self->transients, c);
1411 /* because the self->transient flag wont change from this call,
1412 we don't need to update the window's type and such, only its
1413 transient_for, and the transients lists of other windows in
1414 the group may be affected */
1415 client_update_transient_for(self);
1418 /* the WM_HINTS can contain an icon */
1419 client_update_icons(self);
1424 if (ur != self->urgent) {
1426 /* fire the urgent callback if we're mapped, otherwise, wait until
1427 after we're mapped */
1429 client_urgent_notify(self);
1433 void client_update_title(ObClient *self)
1439 gboolean read_title;
1442 old_title = self->title;
1445 if (!PROP_GETS(self->window, net_wm_name, utf8, &data)) {
1446 /* try old x stuff */
1447 if (!PROP_GETS(self->window, wm_name, locale, &data)) {
1448 // http://developer.gnome.org/projects/gup/hig/draft_hig_new/windows-alert.html
1449 if (self->transient) {
1450 data = g_strdup("");
1453 data = g_strdup("Unnamed Window");
1457 /* did the title change? then reset the title_count */
1458 if (old_title && 0 != strncmp(old_title, data, strlen(data)))
1459 self->title_count = 1;
1461 /* look for duplicates and append a number */
1463 for (it = client_list; it; it = g_list_next(it))
1464 if (it->data != self) {
1465 ObClient *c = it->data;
1466 if (0 == strncmp(c->title, data, strlen(data)))
1467 nums |= 1 << c->title_count;
1469 /* find first free number */
1470 for (i = 1; i <= 32; ++i)
1471 if (!(nums & (1 << i))) {
1472 if (self->title_count == 1 || i == 1)
1473 self->title_count = i;
1476 /* dont display the number for the first window */
1477 if (self->title_count > 1) {
1479 ndata = g_strdup_printf("%s - [%u]", data, self->title_count);
1484 PROP_SETS(self->window, net_wm_visible_name, data);
1489 frame_adjust_title(self->frame);
1493 /* update the icon title */
1495 g_free(self->icon_title);
1499 if (!PROP_GETS(self->window, net_wm_icon_name, utf8, &data))
1500 /* try old x stuff */
1501 if (!PROP_GETS(self->window, wm_icon_name, locale, &data)) {
1502 data = g_strdup(self->title);
1506 /* append the title count, dont display the number for the first window */
1507 if (read_title && self->title_count > 1) {
1508 gchar *vdata, *ndata;
1509 ndata = g_strdup_printf(" - [%u]", self->title_count);
1510 vdata = g_strconcat(data, ndata, NULL);
1516 PROP_SETS(self->window, net_wm_visible_icon_name, data);
1518 self->icon_title = data;
1521 void client_update_class(ObClient *self)
1526 if (self->name) g_free(self->name);
1527 if (self->class) g_free(self->class);
1528 if (self->role) g_free(self->role);
1530 self->name = self->class = self->role = NULL;
1532 if (PROP_GETSS(self->window, wm_class, locale, &data)) {
1534 self->name = g_strdup(data[0]);
1536 self->class = g_strdup(data[1]);
1541 if (PROP_GETS(self->window, wm_window_role, locale, &s))
1544 if (self->name == NULL) self->name = g_strdup("");
1545 if (self->class == NULL) self->class = g_strdup("");
1546 if (self->role == NULL) self->role = g_strdup("");
1549 void client_update_strut(ObClient *self)
1553 gboolean got = FALSE;
1556 if (PROP_GETA32(self->window, net_wm_strut_partial, cardinal,
1560 STRUT_PARTIAL_SET(strut,
1561 data[0], data[2], data[1], data[3],
1562 data[4], data[5], data[8], data[9],
1563 data[6], data[7], data[10], data[11]);
1569 PROP_GETA32(self->window, net_wm_strut, cardinal, &data, &num)) {
1575 /* use the screen's width/height */
1576 a = screen_physical_area();
1578 STRUT_PARTIAL_SET(strut,
1579 data[0], data[2], data[1], data[3],
1580 a->y, a->y + a->height - 1,
1581 a->x, a->x + a->width - 1,
1582 a->y, a->y + a->height - 1,
1583 a->x, a->x + a->width - 1);
1589 STRUT_PARTIAL_SET(strut, 0, 0, 0, 0,
1590 0, 0, 0, 0, 0, 0, 0, 0);
1592 if (!STRUT_EQUAL(strut, self->strut)) {
1593 self->strut = strut;
1595 /* updating here is pointless while we're being mapped cuz we're not in
1596 the client list yet */
1598 screen_update_areas();
1602 void client_update_icons(ObClient *self)
1608 for (i = 0; i < self->nicons; ++i)
1609 g_free(self->icons[i].data);
1610 if (self->nicons > 0)
1611 g_free(self->icons);
1614 if (PROP_GETA32(self->window, net_wm_icon, cardinal, &data, &num)) {
1615 /* figure out how many valid icons are in here */
1617 while (num - i > 2) {
1621 if (i > num || w*h == 0) break;
1625 self->icons = g_new(ObClientIcon, self->nicons);
1627 /* store the icons */
1629 for (j = 0; j < self->nicons; ++j) {
1632 w = self->icons[j].width = data[i++];
1633 h = self->icons[j].height = data[i++];
1635 if (w*h == 0) continue;
1637 self->icons[j].data = g_new(RrPixel32, w * h);
1638 for (x = 0, y = 0, t = 0; t < w * h; ++t, ++x, ++i) {
1643 self->icons[j].data[t] =
1644 (((data[i] >> 24) & 0xff) << RrDefaultAlphaOffset) +
1645 (((data[i] >> 16) & 0xff) << RrDefaultRedOffset) +
1646 (((data[i] >> 8) & 0xff) << RrDefaultGreenOffset) +
1647 (((data[i] >> 0) & 0xff) << RrDefaultBlueOffset);
1653 } else if (PROP_GETA32(self->window, kwm_win_icon,
1654 kwm_win_icon, &data, &num)) {
1657 self->icons = g_new(ObClientIcon, self->nicons);
1658 xerror_set_ignore(TRUE);
1659 if (!RrPixmapToRGBA(ob_rr_inst,
1661 &self->icons[self->nicons-1].width,
1662 &self->icons[self->nicons-1].height,
1663 &self->icons[self->nicons-1].data)) {
1664 g_free(&self->icons[self->nicons-1]);
1667 xerror_set_ignore(FALSE);
1673 if ((hints = XGetWMHints(ob_display, self->window))) {
1674 if (hints->flags & IconPixmapHint) {
1676 self->icons = g_new(ObClientIcon, self->nicons);
1677 xerror_set_ignore(TRUE);
1678 if (!RrPixmapToRGBA(ob_rr_inst,
1680 (hints->flags & IconMaskHint ?
1681 hints->icon_mask : None),
1682 &self->icons[self->nicons-1].width,
1683 &self->icons[self->nicons-1].height,
1684 &self->icons[self->nicons-1].data)){
1685 g_free(&self->icons[self->nicons-1]);
1688 xerror_set_ignore(FALSE);
1695 frame_adjust_icon(self->frame);
1698 static void client_change_state(ObClient *self)
1701 guint32 netstate[11];
1704 state[0] = self->wmstate;
1706 PROP_SETA32(self->window, wm_state, wm_state, state, 2);
1710 netstate[num++] = prop_atoms.net_wm_state_modal;
1712 netstate[num++] = prop_atoms.net_wm_state_shaded;
1714 netstate[num++] = prop_atoms.net_wm_state_hidden;
1715 if (self->skip_taskbar)
1716 netstate[num++] = prop_atoms.net_wm_state_skip_taskbar;
1717 if (self->skip_pager)
1718 netstate[num++] = prop_atoms.net_wm_state_skip_pager;
1719 if (self->fullscreen)
1720 netstate[num++] = prop_atoms.net_wm_state_fullscreen;
1722 netstate[num++] = prop_atoms.net_wm_state_maximized_vert;
1724 netstate[num++] = prop_atoms.net_wm_state_maximized_horz;
1726 netstate[num++] = prop_atoms.net_wm_state_above;
1728 netstate[num++] = prop_atoms.net_wm_state_below;
1729 if (self->undecorated)
1730 netstate[num++] = prop_atoms.ob_wm_state_undecorated;
1731 PROP_SETA32(self->window, net_wm_state, atom, netstate, num);
1733 client_calc_layer(self);
1736 frame_adjust_state(self->frame);
1739 ObClient *client_search_focus_tree(ObClient *self)
1744 for (it = self->transients; it; it = g_slist_next(it)) {
1745 if (client_focused(it->data)) return it->data;
1746 if ((ret = client_search_focus_tree(it->data))) return ret;
1751 ObClient *client_search_focus_tree_full(ObClient *self)
1753 if (self->transient_for) {
1754 if (self->transient_for != OB_TRAN_GROUP) {
1755 return client_search_focus_tree_full(self->transient_for);
1758 gboolean recursed = FALSE;
1760 for (it = self->group->members; it; it = g_slist_next(it))
1761 if (!((ObClient*)it->data)->transient_for) {
1763 if ((c = client_search_focus_tree_full(it->data)))
1772 /* this function checks the whole tree, the client_search_focus_tree~
1773 does not, so we need to check this window */
1774 if (client_focused(self))
1776 return client_search_focus_tree(self);
1779 static ObStackingLayer calc_layer(ObClient *self)
1783 if (self->fullscreen &&
1784 (client_focused(self) || client_search_focus_tree(self)))
1785 l = OB_STACKING_LAYER_FULLSCREEN;
1786 else if (self->type == OB_CLIENT_TYPE_DESKTOP)
1787 l = OB_STACKING_LAYER_DESKTOP;
1788 else if (self->type == OB_CLIENT_TYPE_DOCK) {
1789 if (self->above) l = OB_STACKING_LAYER_DOCK_ABOVE;
1790 else if (self->below) l = OB_STACKING_LAYER_DOCK_BELOW;
1791 else l = OB_STACKING_LAYER_DOCK_NORMAL;
1793 else if (self->above) l = OB_STACKING_LAYER_ABOVE;
1794 else if (self->below) l = OB_STACKING_LAYER_BELOW;
1795 else l = OB_STACKING_LAYER_NORMAL;
1800 static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
1801 ObStackingLayer l, gboolean raised)
1803 ObStackingLayer old, own;
1807 own = calc_layer(self);
1808 self->layer = l > own ? l : own;
1810 for (it = self->transients; it; it = g_slist_next(it))
1811 client_calc_layer_recursive(it->data, orig,
1812 l, raised ? raised : l != old);
1814 if (!raised && l != old)
1815 if (orig->frame) { /* only restack if the original window is managed */
1816 stacking_remove(CLIENT_AS_WINDOW(self));
1817 stacking_add(CLIENT_AS_WINDOW(self));
1821 void client_calc_layer(ObClient *self)
1828 /* transients take on the layer of their parents */
1829 self = client_search_top_transient(self);
1831 l = calc_layer(self);
1833 client_calc_layer_recursive(self, orig, l, FALSE);
1836 gboolean client_should_show(ObClient *self)
1840 if (client_normal(self) && screen_showing_desktop)
1843 if (self->transient_for) {
1844 if (self->transient_for != OB_TRAN_GROUP)
1845 return client_should_show(self->transient_for);
1849 for (it = self->group->members; it; it = g_slist_next(it)) {
1850 ObClient *c = it->data;
1851 if (c != self && !c->transient_for) {
1852 if (client_should_show(c))
1859 if (self->desktop == screen_desktop || self->desktop == DESKTOP_ALL)
1865 static void client_showhide(ObClient *self)
1868 if (client_should_show(self))
1869 frame_show(self->frame);
1871 frame_hide(self->frame);
1874 gboolean client_normal(ObClient *self) {
1875 return ! (self->type == OB_CLIENT_TYPE_DESKTOP ||
1876 self->type == OB_CLIENT_TYPE_DOCK ||
1877 self->type == OB_CLIENT_TYPE_SPLASH);
1880 static void client_apply_startup_state(ObClient *self)
1882 /* these are in a carefully crafted order.. */
1885 self->iconic = FALSE;
1886 client_iconify(self, TRUE, FALSE);
1888 if (self->fullscreen) {
1889 self->fullscreen = FALSE;
1890 client_fullscreen(self, TRUE, FALSE);
1892 if (self->undecorated) {
1893 self->undecorated = FALSE;
1894 client_set_undecorated(self, TRUE);
1897 self->shaded = FALSE;
1898 client_shade(self, TRUE);
1901 client_urgent_notify(self);
1903 if (self->max_vert && self->max_horz) {
1904 self->max_vert = self->max_horz = FALSE;
1905 client_maximize(self, TRUE, 0, FALSE);
1906 } else if (self->max_vert) {
1907 self->max_vert = FALSE;
1908 client_maximize(self, TRUE, 2, FALSE);
1909 } else if (self->max_horz) {
1910 self->max_horz = FALSE;
1911 client_maximize(self, TRUE, 1, FALSE);
1914 /* nothing to do for the other states:
1923 void client_configure_full(ObClient *self, ObCorner anchor,
1924 gint x, gint y, gint w, gint h,
1925 gboolean user, gboolean final,
1926 gboolean force_reply)
1929 gboolean send_resize_client;
1930 gboolean moved = FALSE, resized = FALSE;
1931 guint fdecor = self->frame->decorations;
1932 gboolean fhorz = self->frame->max_horz;
1934 /* make the frame recalculate its dimentions n shit without changing
1935 anything visible for real, this way the constraints below can work with
1936 the updated frame dimensions. */
1937 frame_adjust_area(self->frame, TRUE, TRUE, TRUE);
1939 /* gets the frame's position */
1940 frame_client_gravity(self->frame, &x, &y);
1942 /* these positions are frame positions, not client positions */
1944 /* set the size and position if fullscreen */
1945 if (self->fullscreen) {
1948 XF86VidModeModeLine mode;
1953 i = client_monitor(self);
1954 a = screen_physical_area_monitor(i);
1957 if (i == 0 && /* primary head */
1958 extensions_vidmode &&
1959 XF86VidModeGetViewPort(ob_display, ob_screen, &x, &y) &&
1960 /* get the mode last so the mode.privsize isnt freed incorrectly */
1961 XF86VidModeGetModeLine(ob_display, ob_screen, &dot, &mode)) {
1966 if (mode.privsize) XFree(mode.private);
1976 user = FALSE; /* ignore that increment etc shit when in fullscreen */
1980 a = screen_area_monitor(self->desktop, client_monitor(self));
1982 /* set the size and position if maximized */
1983 if (self->max_horz) {
1985 w = a->width - self->frame->size.left - self->frame->size.right;
1987 if (self->max_vert) {
1989 h = a->height - self->frame->size.top - self->frame->size.bottom;
1993 /* gets the client's position */
1994 frame_frame_gravity(self->frame, &x, &y);
1996 /* these override the above states! if you cant move you can't move! */
1998 if (!(self->functions & OB_CLIENT_FUNC_MOVE)) {
2002 if (!(self->functions & OB_CLIENT_FUNC_RESIZE)) {
2003 w = self->area.width;
2004 h = self->area.height;
2008 if (!(w == self->area.width && h == self->area.height)) {
2009 gint basew, baseh, minw, minh;
2011 /* base size is substituted with min size if not specified */
2012 if (self->base_size.width || self->base_size.height) {
2013 basew = self->base_size.width;
2014 baseh = self->base_size.height;
2016 basew = self->min_size.width;
2017 baseh = self->min_size.height;
2019 /* min size is substituted with base size if not specified */
2020 if (self->min_size.width || self->min_size.height) {
2021 minw = self->min_size.width;
2022 minh = self->min_size.height;
2024 minw = self->base_size.width;
2025 minh = self->base_size.height;
2028 /* if this is a user-requested resize, then check against min/max
2031 /* smaller than min size or bigger than max size? */
2032 if (w > self->max_size.width) w = self->max_size.width;
2033 if (w < minw) w = minw;
2034 if (h > self->max_size.height) h = self->max_size.height;
2035 if (h < minh) h = minh;
2040 /* keep to the increments */
2041 w /= self->size_inc.width;
2042 h /= self->size_inc.height;
2044 /* you cannot resize to nothing */
2045 if (basew + w < 1) w = 1 - basew;
2046 if (baseh + h < 1) h = 1 - baseh;
2048 /* store the logical size */
2049 SIZE_SET(self->logical_size,
2050 self->size_inc.width > 1 ? w : w + basew,
2051 self->size_inc.height > 1 ? h : h + baseh);
2053 w *= self->size_inc.width;
2054 h *= self->size_inc.height;
2059 /* adjust the height to match the width for the aspect ratios.
2060 for this, min size is not substituted for base size ever. */
2061 w -= self->base_size.width;
2062 h -= self->base_size.height;
2064 if (!self->fullscreen) {
2065 if (self->min_ratio)
2066 if (h * self->min_ratio > w) {
2067 h = (gint)(w / self->min_ratio);
2069 /* you cannot resize to nothing */
2072 w = (gint)(h * self->min_ratio);
2075 if (self->max_ratio)
2076 if (h * self->max_ratio < w) {
2077 h = (gint)(w / self->max_ratio);
2079 /* you cannot resize to nothing */
2082 w = (gint)(h * self->min_ratio);
2087 w += self->base_size.width;
2088 h += self->base_size.height;
2095 case OB_CORNER_TOPLEFT:
2097 case OB_CORNER_TOPRIGHT:
2098 x -= w - self->area.width;
2100 case OB_CORNER_BOTTOMLEFT:
2101 y -= h - self->area.height;
2103 case OB_CORNER_BOTTOMRIGHT:
2104 x -= w - self->area.width;
2105 y -= h - self->area.height;
2109 moved = x != self->area.x || y != self->area.y;
2110 resized = w != self->area.width || h != self->area.height;
2112 oldw = self->area.width;
2113 oldh = self->area.height;
2114 RECT_SET(self->area, x, y, w, h);
2116 /* for app-requested resizes, always resize if 'resized' is true.
2117 for user-requested ones, only resize if final is true, or when
2118 resizing in redraw mode */
2119 send_resize_client = ((!user && resized) ||
2121 (resized && config_resize_redraw))));
2123 /* if the client is enlarging, then resize the client before the frame */
2124 if (send_resize_client && user && (w > oldw || h > oldh))
2125 XResizeWindow(ob_display, self->window, MAX(w, oldw), MAX(h, oldh));
2127 /* move/resize the frame to match the request */
2129 if (self->decorations != fdecor || self->max_horz != fhorz)
2130 moved = resized = TRUE;
2132 if (moved || resized)
2133 frame_adjust_area(self->frame, moved, resized, FALSE);
2135 if (!resized && (force_reply || ((!user && moved) || (user && final))))
2138 event.type = ConfigureNotify;
2139 event.xconfigure.display = ob_display;
2140 event.xconfigure.event = self->window;
2141 event.xconfigure.window = self->window;
2143 /* root window real coords */
2144 event.xconfigure.x = self->frame->area.x + self->frame->size.left -
2146 event.xconfigure.y = self->frame->area.y + self->frame->size.top -
2148 event.xconfigure.width = w;
2149 event.xconfigure.height = h;
2150 event.xconfigure.border_width = 0;
2151 event.xconfigure.above = self->frame->plate;
2152 event.xconfigure.override_redirect = FALSE;
2153 XSendEvent(event.xconfigure.display, event.xconfigure.window,
2154 FALSE, StructureNotifyMask, &event);
2158 /* if the client is shrinking, then resize the frame before the client */
2159 if (send_resize_client && (!user || (w <= oldw || h <= oldh)))
2160 XResizeWindow(ob_display, self->window, w, h);
2165 void client_fullscreen(ObClient *self, gboolean fs, gboolean savearea)
2169 if (!(self->functions & OB_CLIENT_FUNC_FULLSCREEN) || /* can't */
2170 self->fullscreen == fs) return; /* already done */
2172 self->fullscreen = fs;
2173 client_change_state(self); /* change the state hints on the client,
2174 and adjust out layer/stacking */
2178 self->pre_fullscreen_area = self->area;
2180 /* these are not actually used cuz client_configure will set them
2181 as appropriate when the window is fullscreened */
2186 if (self->pre_fullscreen_area.width > 0 &&
2187 self->pre_fullscreen_area.height > 0)
2189 x = self->pre_fullscreen_area.x;
2190 y = self->pre_fullscreen_area.y;
2191 w = self->pre_fullscreen_area.width;
2192 h = self->pre_fullscreen_area.height;
2193 RECT_SET(self->pre_fullscreen_area, 0, 0, 0, 0);
2195 /* pick some fallbacks... */
2196 a = screen_area_monitor(self->desktop, 0);
2197 x = a->x + a->width / 4;
2198 y = a->y + a->height / 4;
2204 client_setup_decor_and_functions(self);
2206 client_move_resize(self, x, y, w, h);
2208 /* try focus us when we go into fullscreen mode */
2212 static void client_iconify_recursive(ObClient *self,
2213 gboolean iconic, gboolean curdesk)
2216 gboolean changed = FALSE;
2219 if (self->iconic != iconic) {
2220 ob_debug("%sconifying window: 0x%lx\n", (iconic ? "I" : "Uni"),
2223 self->iconic = iconic;
2226 if (self->functions & OB_CLIENT_FUNC_ICONIFY) {
2229 old = self->wmstate;
2230 self->wmstate = IconicState;
2231 if (old != self->wmstate)
2232 PROP_MSG(self->window, kde_wm_change_state,
2233 self->wmstate, 1, 0, 0);
2235 /* update the focus lists.. iconic windows go to the bottom of
2236 the list, put the new iconic window at the 'top of the
2238 focus_order_to_top(self);
2246 client_set_desktop(self, screen_desktop, FALSE);
2248 old = self->wmstate;
2249 self->wmstate = self->shaded ? IconicState : NormalState;
2250 if (old != self->wmstate)
2251 PROP_MSG(self->window, kde_wm_change_state,
2252 self->wmstate, 1, 0, 0);
2254 /* this puts it after the current focused window */
2255 focus_order_remove(self);
2256 focus_order_add_new(self);
2258 /* this is here cuz with the VIDMODE extension, the viewport can
2259 change while a fullscreen window is iconic, and when it
2260 uniconifies, it would be nice if it did so to the new position
2262 client_reconfigure(self);
2269 client_change_state(self);
2270 client_showhide(self);
2271 screen_update_areas();
2274 /* iconify all transients */
2275 for (it = self->transients; it; it = g_slist_next(it))
2276 if (it->data != self) client_iconify_recursive(it->data,
2280 void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk)
2282 /* move up the transient chain as far as possible first */
2283 self = client_search_top_transient(self);
2285 client_iconify_recursive(client_search_top_transient(self),
2289 void client_maximize(ObClient *self, gboolean max, gint dir, gboolean savearea)
2293 g_assert(dir == 0 || dir == 1 || dir == 2);
2294 if (!(self->functions & OB_CLIENT_FUNC_MAXIMIZE)) return; /* can't */
2296 /* check if already done */
2298 if (dir == 0 && self->max_horz && self->max_vert) return;
2299 if (dir == 1 && self->max_horz) return;
2300 if (dir == 2 && self->max_vert) return;
2302 if (dir == 0 && !self->max_horz && !self->max_vert) return;
2303 if (dir == 1 && !self->max_horz) return;
2304 if (dir == 2 && !self->max_vert) return;
2307 /* we just tell it to configure in the same place and client_configure
2308 worries about filling the screen with the window */
2311 w = self->area.width;
2312 h = self->area.height;
2316 if ((dir == 0 || dir == 1) && !self->max_horz) { /* horz */
2317 RECT_SET(self->pre_max_area,
2318 self->area.x, self->pre_max_area.y,
2319 self->area.width, self->pre_max_area.height);
2321 if ((dir == 0 || dir == 2) && !self->max_vert) { /* vert */
2322 RECT_SET(self->pre_max_area,
2323 self->pre_max_area.x, self->area.y,
2324 self->pre_max_area.width, self->area.height);
2330 a = screen_area_monitor(self->desktop, 0);
2331 if ((dir == 0 || dir == 1) && self->max_horz) { /* horz */
2332 if (self->pre_max_area.width > 0) {
2333 x = self->pre_max_area.x;
2334 w = self->pre_max_area.width;
2336 RECT_SET(self->pre_max_area, 0, self->pre_max_area.y,
2337 0, self->pre_max_area.height);
2339 /* pick some fallbacks... */
2340 x = a->x + a->width / 4;
2344 if ((dir == 0 || dir == 2) && self->max_vert) { /* vert */
2345 if (self->pre_max_area.height > 0) {
2346 y = self->pre_max_area.y;
2347 h = self->pre_max_area.height;
2349 RECT_SET(self->pre_max_area, self->pre_max_area.x, 0,
2350 self->pre_max_area.width, 0);
2352 /* pick some fallbacks... */
2353 y = a->y + a->height / 4;
2359 if (dir == 0 || dir == 1) /* horz */
2360 self->max_horz = max;
2361 if (dir == 0 || dir == 2) /* vert */
2362 self->max_vert = max;
2364 client_change_state(self); /* change the state hints on the client */
2366 client_setup_decor_and_functions(self);
2368 client_move_resize(self, x, y, w, h);
2371 void client_shade(ObClient *self, gboolean shade)
2373 if ((!(self->functions & OB_CLIENT_FUNC_SHADE) &&
2374 shade) || /* can't shade */
2375 self->shaded == shade) return; /* already done */
2377 /* when we're iconic, don't change the wmstate */
2378 if (!self->iconic) {
2381 old = self->wmstate;
2382 self->wmstate = shade ? IconicState : NormalState;
2383 if (old != self->wmstate)
2384 PROP_MSG(self->window, kde_wm_change_state,
2385 self->wmstate, 1, 0, 0);
2388 self->shaded = shade;
2389 client_change_state(self);
2390 /* resize the frame to just the titlebar */
2391 frame_adjust_area(self->frame, FALSE, FALSE, FALSE);
2394 void client_close(ObClient *self)
2398 if (!(self->functions & OB_CLIENT_FUNC_CLOSE)) return;
2400 /* in the case that the client provides no means to requesting that it
2401 close, we just kill it */
2402 if (!self->delete_window)
2406 XXX: itd be cool to do timeouts and shit here for killing the client's
2408 like... if the window is around after 5 seconds, then the close button
2409 turns a nice red, and if this function is called again, the client is
2413 ce.xclient.type = ClientMessage;
2414 ce.xclient.message_type = prop_atoms.wm_protocols;
2415 ce.xclient.display = ob_display;
2416 ce.xclient.window = self->window;
2417 ce.xclient.format = 32;
2418 ce.xclient.data.l[0] = prop_atoms.wm_delete_window;
2419 ce.xclient.data.l[1] = event_lasttime;
2420 ce.xclient.data.l[2] = 0l;
2421 ce.xclient.data.l[3] = 0l;
2422 ce.xclient.data.l[4] = 0l;
2423 XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
2426 void client_kill(ObClient *self)
2428 XKillClient(ob_display, self->window);
2431 void client_set_desktop_recursive(ObClient *self,
2432 guint target, gboolean donthide)
2437 if (target != self->desktop) {
2439 ob_debug("Setting desktop %u\n", target+1);
2441 g_assert(target < screen_num_desktops || target == DESKTOP_ALL);
2443 /* remove from the old desktop(s) */
2444 focus_order_remove(self);
2446 old = self->desktop;
2447 self->desktop = target;
2448 PROP_SET32(self->window, net_wm_desktop, cardinal, target);
2449 /* the frame can display the current desktop state */
2450 frame_adjust_state(self->frame);
2451 /* 'move' the window to the new desktop */
2453 client_showhide(self);
2454 /* raise if it was not already on the desktop */
2455 if (old != DESKTOP_ALL)
2457 screen_update_areas();
2459 /* add to the new desktop(s) */
2460 if (config_focus_new)
2461 focus_order_to_top(self);
2463 focus_order_to_bottom(self);
2466 /* move all transients */
2467 for (it = self->transients; it; it = g_slist_next(it))
2468 if (it->data != self) client_set_desktop_recursive(it->data,
2472 void client_set_desktop(ObClient *self, guint target, gboolean donthide)
2474 client_set_desktop_recursive(client_search_top_transient(self),
2478 ObClient *client_search_modal_child(ObClient *self)
2483 for (it = self->transients; it; it = g_slist_next(it)) {
2484 ObClient *c = it->data;
2485 if ((ret = client_search_modal_child(c))) return ret;
2486 if (c->modal) return c;
2491 gboolean client_validate(ObClient *self)
2495 XSync(ob_display, FALSE); /* get all events on the server */
2497 if (XCheckTypedWindowEvent(ob_display, self->window, DestroyNotify, &e) ||
2498 XCheckTypedWindowEvent(ob_display, self->window, UnmapNotify, &e)) {
2499 XPutBackEvent(ob_display, &e);
2506 void client_set_wm_state(ObClient *self, glong state)
2508 if (state == self->wmstate) return; /* no change */
2512 client_iconify(self, TRUE, TRUE);
2515 client_iconify(self, FALSE, TRUE);
2520 void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
2522 gboolean shaded = self->shaded;
2523 gboolean fullscreen = self->fullscreen;
2524 gboolean undecorated = self->undecorated;
2525 gboolean max_horz = self->max_horz;
2526 gboolean max_vert = self->max_vert;
2527 gboolean modal = self->modal;
2528 gboolean iconic = self->iconic;
2531 if (!(action == prop_atoms.net_wm_state_add ||
2532 action == prop_atoms.net_wm_state_remove ||
2533 action == prop_atoms.net_wm_state_toggle))
2534 /* an invalid action was passed to the client message, ignore it */
2537 for (i = 0; i < 2; ++i) {
2538 Atom state = i == 0 ? data1 : data2;
2540 if (!state) continue;
2542 /* if toggling, then pick whether we're adding or removing */
2543 if (action == prop_atoms.net_wm_state_toggle) {
2544 if (state == prop_atoms.net_wm_state_modal)
2545 action = modal ? prop_atoms.net_wm_state_remove :
2546 prop_atoms.net_wm_state_add;
2547 else if (state == prop_atoms.net_wm_state_maximized_vert)
2548 action = self->max_vert ? prop_atoms.net_wm_state_remove :
2549 prop_atoms.net_wm_state_add;
2550 else if (state == prop_atoms.net_wm_state_maximized_horz)
2551 action = self->max_horz ? prop_atoms.net_wm_state_remove :
2552 prop_atoms.net_wm_state_add;
2553 else if (state == prop_atoms.net_wm_state_shaded)
2554 action = shaded ? prop_atoms.net_wm_state_remove :
2555 prop_atoms.net_wm_state_add;
2556 else if (state == prop_atoms.net_wm_state_skip_taskbar)
2557 action = self->skip_taskbar ?
2558 prop_atoms.net_wm_state_remove :
2559 prop_atoms.net_wm_state_add;
2560 else if (state == prop_atoms.net_wm_state_skip_pager)
2561 action = self->skip_pager ?
2562 prop_atoms.net_wm_state_remove :
2563 prop_atoms.net_wm_state_add;
2564 else if (state == prop_atoms.net_wm_state_hidden)
2565 action = self->iconic ?
2566 prop_atoms.net_wm_state_remove :
2567 prop_atoms.net_wm_state_add;
2568 else if (state == prop_atoms.net_wm_state_fullscreen)
2569 action = fullscreen ?
2570 prop_atoms.net_wm_state_remove :
2571 prop_atoms.net_wm_state_add;
2572 else if (state == prop_atoms.net_wm_state_above)
2573 action = self->above ? prop_atoms.net_wm_state_remove :
2574 prop_atoms.net_wm_state_add;
2575 else if (state == prop_atoms.net_wm_state_below)
2576 action = self->below ? prop_atoms.net_wm_state_remove :
2577 prop_atoms.net_wm_state_add;
2578 else if (state == prop_atoms.ob_wm_state_undecorated)
2579 action = undecorated ? prop_atoms.net_wm_state_remove :
2580 prop_atoms.net_wm_state_add;
2583 if (action == prop_atoms.net_wm_state_add) {
2584 if (state == prop_atoms.net_wm_state_modal) {
2586 } else if (state == prop_atoms.net_wm_state_maximized_vert) {
2588 } else if (state == prop_atoms.net_wm_state_maximized_horz) {
2590 } else if (state == prop_atoms.net_wm_state_shaded) {
2592 } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
2593 self->skip_taskbar = TRUE;
2594 } else if (state == prop_atoms.net_wm_state_skip_pager) {
2595 self->skip_pager = TRUE;
2596 } else if (state == prop_atoms.net_wm_state_hidden) {
2598 } else if (state == prop_atoms.net_wm_state_fullscreen) {
2600 } else if (state == prop_atoms.net_wm_state_above) {
2602 self->below = FALSE;
2603 } else if (state == prop_atoms.net_wm_state_below) {
2604 self->above = FALSE;
2606 } else if (state == prop_atoms.ob_wm_state_undecorated) {
2610 } else { /* action == prop_atoms.net_wm_state_remove */
2611 if (state == prop_atoms.net_wm_state_modal) {
2613 } else if (state == prop_atoms.net_wm_state_maximized_vert) {
2615 } else if (state == prop_atoms.net_wm_state_maximized_horz) {
2617 } else if (state == prop_atoms.net_wm_state_shaded) {
2619 } else if (state == prop_atoms.net_wm_state_skip_taskbar) {
2620 self->skip_taskbar = FALSE;
2621 } else if (state == prop_atoms.net_wm_state_skip_pager) {
2622 self->skip_pager = FALSE;
2623 } else if (state == prop_atoms.net_wm_state_hidden) {
2625 } else if (state == prop_atoms.net_wm_state_fullscreen) {
2627 } else if (state == prop_atoms.net_wm_state_above) {
2628 self->above = FALSE;
2629 } else if (state == prop_atoms.net_wm_state_below) {
2630 self->below = FALSE;
2631 } else if (state == prop_atoms.ob_wm_state_undecorated) {
2632 undecorated = FALSE;
2636 if (max_horz != self->max_horz || max_vert != self->max_vert) {
2637 if (max_horz != self->max_horz && max_vert != self->max_vert) {
2639 if (max_horz == max_vert) { /* both going the same way */
2640 client_maximize(self, max_horz, 0, TRUE);
2642 client_maximize(self, max_horz, 1, TRUE);
2643 client_maximize(self, max_vert, 2, TRUE);
2647 if (max_horz != self->max_horz)
2648 client_maximize(self, max_horz, 1, TRUE);
2650 client_maximize(self, max_vert, 2, TRUE);
2653 /* change fullscreen state before shading, as it will affect if the window
2655 if (fullscreen != self->fullscreen)
2656 client_fullscreen(self, fullscreen, TRUE);
2657 if (shaded != self->shaded)
2658 client_shade(self, shaded);
2659 if (undecorated != self->undecorated)
2660 client_set_undecorated(self, undecorated);
2661 if (modal != self->modal) {
2662 self->modal = modal;
2663 /* when a window changes modality, then its stacking order with its
2664 transients needs to change */
2667 if (iconic != self->iconic)
2668 client_iconify(self, iconic, FALSE);
2670 client_calc_layer(self);
2671 client_change_state(self); /* change the hint to reflect these changes */
2674 ObClient *client_focus_target(ObClient *self)
2678 /* if we have a modal child, then focus it, not us */
2679 child = client_search_modal_child(client_search_top_transient(self));
2680 if (child) return child;
2684 gboolean client_can_focus(ObClient *self)
2688 /* choose the correct target */
2689 self = client_focus_target(self);
2691 if (!self->frame->visible)
2694 if (!(self->can_focus || self->focus_notify))
2697 /* do a check to see if the window has already been unmapped or destroyed
2698 do this intelligently while watching out for unmaps we've generated
2699 (ignore_unmaps > 0) */
2700 if (XCheckTypedWindowEvent(ob_display, self->window,
2701 DestroyNotify, &ev)) {
2702 XPutBackEvent(ob_display, &ev);
2705 while (XCheckTypedWindowEvent(ob_display, self->window,
2706 UnmapNotify, &ev)) {
2707 if (self->ignore_unmaps) {
2708 self->ignore_unmaps--;
2710 XPutBackEvent(ob_display, &ev);
2718 gboolean client_focus(ObClient *self)
2720 /* choose the correct target */
2721 self = client_focus_target(self);
2723 if (!client_can_focus(self)) {
2724 if (!self->frame->visible) {
2725 /* update the focus lists */
2726 focus_order_to_top(self);
2731 if (self->can_focus) {
2732 /* RevertToPointerRoot causes much more headache than RevertToNone, so
2733 I choose to use it always, hopefully to find errors quicker, if any
2734 are left. (I hate X. I hate focus events.)
2736 Update: Changing this to RevertToNone fixed a bug with mozilla (bug
2737 #799. So now it is RevertToNone again.
2739 XSetInputFocus(ob_display, self->window, RevertToNone,
2743 if (self->focus_notify) {
2745 ce.xclient.type = ClientMessage;
2746 ce.xclient.message_type = prop_atoms.wm_protocols;
2747 ce.xclient.display = ob_display;
2748 ce.xclient.window = self->window;
2749 ce.xclient.format = 32;
2750 ce.xclient.data.l[0] = prop_atoms.wm_take_focus;
2751 ce.xclient.data.l[1] = event_lasttime;
2752 ce.xclient.data.l[2] = 0l;
2753 ce.xclient.data.l[3] = 0l;
2754 ce.xclient.data.l[4] = 0l;
2755 XSendEvent(ob_display, self->window, FALSE, NoEventMask, &ce);
2759 ob_debug("%sively focusing %lx at %d\n",
2760 (self->can_focus ? "act" : "pass"),
2761 self->window, (gint) event_lasttime);
2764 /* Cause the FocusIn to come back to us. Important for desktop switches,
2765 since otherwise we'll have no FocusIn on the queue and send it off to
2766 the focus_backup. */
2767 XSync(ob_display, FALSE);
2771 void client_unfocus(ObClient *self)
2773 if (focus_client == self) {
2775 ob_debug("client_unfocus for %lx\n", self->window);
2777 focus_fallback(OB_FOCUS_FALLBACK_UNFOCUSING);
2781 void client_activate(ObClient *self, gboolean here)
2783 if (client_normal(self) && screen_showing_desktop)
2784 screen_show_desktop(FALSE);
2786 client_iconify(self, FALSE, here);
2787 if (self->desktop != DESKTOP_ALL &&
2788 self->desktop != screen_desktop) {
2790 client_set_desktop(self, screen_desktop, FALSE);
2792 screen_set_desktop(self->desktop);
2793 } else if (!self->frame->visible)
2794 /* if its not visible for other reasons, then don't mess
2798 client_shade(self, FALSE);
2802 /* we do this an action here. this is rather important. this is because
2803 we want the results from the focus change to take place BEFORE we go
2804 about raising the window. when a fullscreen window loses focus, we need
2805 this or else the raise wont be able to raise above the to-lose-focus
2806 fullscreen window. */
2810 void client_raise(ObClient *self)
2812 action_run_string("Raise", self);
2815 void client_lower(ObClient *self)
2817 action_run_string("Lower", self);
2820 gboolean client_focused(ObClient *self)
2822 return self == focus_client;
2825 static ObClientIcon* client_icon_recursive(ObClient *self, gint w, gint h)
2828 /* si is the smallest image >= req */
2829 /* li is the largest image < req */
2830 gulong size, smallest = 0xffffffff, largest = 0, si = 0, li = 0;
2832 if (!self->nicons) {
2833 ObClientIcon *parent = NULL;
2835 if (self->transient_for) {
2836 if (self->transient_for != OB_TRAN_GROUP)
2837 parent = client_icon_recursive(self->transient_for, w, h);
2840 for (it = self->group->members; it; it = g_slist_next(it)) {
2841 ObClient *c = it->data;
2842 if (c != self && !c->transient_for) {
2843 if ((parent = client_icon_recursive(c, w, h)))
2853 for (i = 0; i < self->nicons; ++i) {
2854 size = self->icons[i].width * self->icons[i].height;
2855 if (size < smallest && size >= (unsigned)(w * h)) {
2859 if (size > largest && size <= (unsigned)(w * h)) {
2864 if (largest == 0) /* didnt find one smaller than the requested size */
2865 return &self->icons[si];
2866 return &self->icons[li];
2869 const ObClientIcon* client_icon(ObClient *self, gint w, gint h)
2872 static ObClientIcon deficon;
2874 if (!(ret = client_icon_recursive(self, w, h))) {
2875 deficon.width = deficon.height = 48;
2876 deficon.data = ob_rr_theme->def_win_icon;
2882 /* this be mostly ripped from fvwm */
2883 ObClient *client_find_directional(ObClient *c, ObDirection dir)
2885 gint my_cx, my_cy, his_cx, his_cy;
2888 gint score, best_score;
2889 ObClient *best_client, *cur;
2895 /* first, find the centre coords of the currently focused window */
2896 my_cx = c->frame->area.x + c->frame->area.width / 2;
2897 my_cy = c->frame->area.y + c->frame->area.height / 2;
2902 for(it = g_list_first(client_list); it; it = g_list_next(it)) {
2905 /* the currently selected window isn't interesting */
2908 if (!client_normal(cur))
2910 /* using c->desktop instead of screen_desktop doesn't work if the
2911 * current window was omnipresent, hope this doesn't have any other
2913 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
2917 if(client_focus_target(cur) == cur &&
2918 !(cur->can_focus || cur->focus_notify))
2921 /* find the centre coords of this window, from the
2922 * currently focused window's point of view */
2923 his_cx = (cur->frame->area.x - my_cx)
2924 + cur->frame->area.width / 2;
2925 his_cy = (cur->frame->area.y - my_cy)
2926 + cur->frame->area.height / 2;
2928 if(dir == OB_DIRECTION_NORTHEAST || dir == OB_DIRECTION_SOUTHEAST ||
2929 dir == OB_DIRECTION_SOUTHWEST || dir == OB_DIRECTION_NORTHWEST) {
2931 /* Rotate the diagonals 45 degrees counterclockwise.
2932 * To do this, multiply the matrix /+h +h\ with the
2933 * vector (x y). \-h +h/
2934 * h = sqrt(0.5). We can set h := 1 since absolute
2935 * distance doesn't matter here. */
2936 tx = his_cx + his_cy;
2937 his_cy = -his_cx + his_cy;
2942 case OB_DIRECTION_NORTH:
2943 case OB_DIRECTION_SOUTH:
2944 case OB_DIRECTION_NORTHEAST:
2945 case OB_DIRECTION_SOUTHWEST:
2946 offset = (his_cx < 0) ? -his_cx : his_cx;
2947 distance = ((dir == OB_DIRECTION_NORTH ||
2948 dir == OB_DIRECTION_NORTHEAST) ?
2951 case OB_DIRECTION_EAST:
2952 case OB_DIRECTION_WEST:
2953 case OB_DIRECTION_SOUTHEAST:
2954 case OB_DIRECTION_NORTHWEST:
2955 offset = (his_cy < 0) ? -his_cy : his_cy;
2956 distance = ((dir == OB_DIRECTION_WEST ||
2957 dir == OB_DIRECTION_NORTHWEST) ?
2962 /* the target must be in the requested direction */
2966 /* Calculate score for this window. The smaller the better. */
2967 score = distance + offset;
2969 /* windows more than 45 degrees off the direction are
2970 * heavily penalized and will only be chosen if nothing
2971 * else within a million pixels */
2972 if(offset > distance)
2975 if(best_score == -1 || score < best_score)
2983 void client_set_layer(ObClient *self, gint layer)
2987 self->above = FALSE;
2988 } else if (layer == 0) {
2989 self->below = self->above = FALSE;
2991 self->below = FALSE;
2994 client_calc_layer(self);
2995 client_change_state(self); /* reflect this in the state hints */
2998 void client_set_undecorated(ObClient *self, gboolean undecorated)
3000 if (self->undecorated != undecorated) {
3001 self->undecorated = undecorated;
3002 client_setup_decor_and_functions(self);
3003 client_change_state(self); /* reflect this in the state hints */
3007 /* Determines which physical monitor a client is on by calculating the
3008 area of the part of the client on each monitor. The number of the
3009 monitor containing the greatest area of the client is returned.*/
3010 guint client_monitor(ObClient *self)
3016 for (i = 0; i < screen_num_monitors; ++i) {
3017 Rect *area = screen_physical_area_monitor(i);
3018 if (RECT_INTERSECTS_RECT(*area, self->frame->area)) {
3022 RECT_SET_INTERSECTION(r, *area, self->frame->area);
3023 v = r.width * r.height;
3034 ObClient *client_search_top_transient(ObClient *self)
3036 /* move up the transient chain as far as possible */
3037 if (self->transient_for) {
3038 if (self->transient_for != OB_TRAN_GROUP) {
3039 return client_search_top_transient(self->transient_for);
3043 g_assert(self->group);
3045 for (it = self->group->members; it; it = g_slist_next(it)) {
3046 ObClient *c = it->data;
3048 /* checking transient_for prevents infinate loops! */
3049 if (c != self && !c->transient_for)
3060 ObClient *client_search_focus_parent(ObClient *self)
3062 if (self->transient_for) {
3063 if (self->transient_for != OB_TRAN_GROUP) {
3064 if (client_focused(self->transient_for))
3065 return self->transient_for;
3069 for (it = self->group->members; it; it = g_slist_next(it)) {
3070 ObClient *c = it->data;
3072 /* checking transient_for prevents infinate loops! */
3073 if (c != self && !c->transient_for)
3074 if (client_focused(c))
3083 ObClient *client_search_parent(ObClient *self, ObClient *search)
3085 if (self->transient_for) {
3086 if (self->transient_for != OB_TRAN_GROUP) {
3087 if (self->transient_for == search)
3092 for (it = self->group->members; it; it = g_slist_next(it)) {
3093 ObClient *c = it->data;
3095 /* checking transient_for prevents infinate loops! */
3096 if (c != self && !c->transient_for)
3106 ObClient *client_search_transient(ObClient *self, ObClient *search)
3110 for (sit = self->transients; sit; sit = g_slist_next(sit)) {
3111 if (sit->data == search)
3113 if (client_search_transient(sit->data, search))
3119 void client_update_sm_client_id(ObClient *self)
3121 g_free(self->sm_client_id);
3122 self->sm_client_id = NULL;
3124 if (!PROP_GETS(self->window, sm_client_id, locale, &self->sm_client_id) &&
3126 PROP_GETS(self->group->leader, sm_client_id, locale,
3127 &self->sm_client_id);
3130 /* finds the nearest edge in the given direction from the current client
3131 * note to self: the edge is the -frame- edge (the actual one), not the
3134 gint client_directional_edge_search(ObClient *c, ObDirection dir)
3136 gint dest, monitor_dest;
3137 gint my_edge_start, my_edge_end, my_offset;
3144 a = screen_area(c->desktop);
3145 monitor = screen_area_monitor(c->desktop, client_monitor(c));
3148 case OB_DIRECTION_NORTH:
3149 my_edge_start = c->frame->area.x;
3150 my_edge_end = c->frame->area.x + c->frame->area.width;
3151 my_offset = c->frame->area.y;
3153 /* default: top of screen */
3155 monitor_dest = monitor->y;
3156 /* if the monitor edge comes before the screen edge, */
3157 /* use that as the destination instead. (For xinerama) */
3158 if (monitor_dest != dest && my_offset > monitor_dest)
3159 dest = monitor_dest;
3161 for(it = client_list; it && my_offset != dest; it = g_list_next(it)) {
3162 gint his_edge_start, his_edge_end, his_offset;
3163 ObClient *cur = it->data;
3167 if(!client_normal(cur))
3169 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
3173 if(cur->layer < c->layer && !config_resist_layers_below)
3176 his_edge_start = cur->frame->area.x;
3177 his_edge_end = cur->frame->area.x + cur->frame->area.width;
3178 his_offset = cur->frame->area.y + cur->frame->area.height;
3180 if(his_offset + 1 > my_offset)
3183 if(his_offset < dest)
3186 if(his_edge_start >= my_edge_start &&
3187 his_edge_start <= my_edge_end)
3190 if(my_edge_start >= his_edge_start &&
3191 my_edge_start <= his_edge_end)
3196 case OB_DIRECTION_SOUTH:
3197 my_edge_start = c->frame->area.x;
3198 my_edge_end = c->frame->area.x + c->frame->area.width;
3199 my_offset = c->frame->area.y + c->frame->area.height;
3201 /* default: bottom of screen */
3202 dest = a->y + a->height;
3203 monitor_dest = monitor->y + monitor->height;
3204 /* if the monitor edge comes before the screen edge, */
3205 /* use that as the destination instead. (For xinerama) */
3206 if (monitor_dest != dest && my_offset < monitor_dest)
3207 dest = monitor_dest;
3209 for(it = client_list; it && my_offset != dest; it = g_list_next(it)) {
3210 gint his_edge_start, his_edge_end, his_offset;
3211 ObClient *cur = it->data;
3215 if(!client_normal(cur))
3217 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
3221 if(cur->layer < c->layer && !config_resist_layers_below)
3224 his_edge_start = cur->frame->area.x;
3225 his_edge_end = cur->frame->area.x + cur->frame->area.width;
3226 his_offset = cur->frame->area.y;
3229 if(his_offset - 1 < my_offset)
3232 if(his_offset > dest)
3235 if(his_edge_start >= my_edge_start &&
3236 his_edge_start <= my_edge_end)
3239 if(my_edge_start >= his_edge_start &&
3240 my_edge_start <= his_edge_end)
3245 case OB_DIRECTION_WEST:
3246 my_edge_start = c->frame->area.y;
3247 my_edge_end = c->frame->area.y + c->frame->area.height;
3248 my_offset = c->frame->area.x;
3250 /* default: leftmost egde of screen */
3252 monitor_dest = monitor->x;
3253 /* if the monitor edge comes before the screen edge, */
3254 /* use that as the destination instead. (For xinerama) */
3255 if (monitor_dest != dest && my_offset > monitor_dest)
3256 dest = monitor_dest;
3258 for(it = client_list; it && my_offset != dest; it = g_list_next(it)) {
3259 gint his_edge_start, his_edge_end, his_offset;
3260 ObClient *cur = it->data;
3264 if(!client_normal(cur))
3266 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
3270 if(cur->layer < c->layer && !config_resist_layers_below)
3273 his_edge_start = cur->frame->area.y;
3274 his_edge_end = cur->frame->area.y + cur->frame->area.height;
3275 his_offset = cur->frame->area.x + cur->frame->area.width;
3277 if(his_offset + 1 > my_offset)
3280 if(his_offset < dest)
3283 if(his_edge_start >= my_edge_start &&
3284 his_edge_start <= my_edge_end)
3287 if(my_edge_start >= his_edge_start &&
3288 my_edge_start <= his_edge_end)
3294 case OB_DIRECTION_EAST:
3295 my_edge_start = c->frame->area.y;
3296 my_edge_end = c->frame->area.y + c->frame->area.height;
3297 my_offset = c->frame->area.x + c->frame->area.width;
3299 /* default: rightmost edge of screen */
3300 dest = a->x + a->width;
3301 monitor_dest = monitor->x + monitor->width;
3302 /* if the monitor edge comes before the screen edge, */
3303 /* use that as the destination instead. (For xinerama) */
3304 if (monitor_dest != dest && my_offset < monitor_dest)
3305 dest = monitor_dest;
3307 for(it = client_list; it && my_offset != dest; it = g_list_next(it)) {
3308 gint his_edge_start, his_edge_end, his_offset;
3309 ObClient *cur = it->data;
3313 if(!client_normal(cur))
3315 if(screen_desktop != cur->desktop && cur->desktop != DESKTOP_ALL)
3319 if(cur->layer < c->layer && !config_resist_layers_below)
3322 his_edge_start = cur->frame->area.y;
3323 his_edge_end = cur->frame->area.y + cur->frame->area.height;
3324 his_offset = cur->frame->area.x;
3326 if(his_offset - 1 < my_offset)
3329 if(his_offset > dest)
3332 if(his_edge_start >= my_edge_start &&
3333 his_edge_start <= my_edge_end)
3336 if(my_edge_start >= his_edge_start &&
3337 my_edge_start <= his_edge_end)
3342 case OB_DIRECTION_NORTHEAST:
3343 case OB_DIRECTION_SOUTHEAST:
3344 case OB_DIRECTION_NORTHWEST:
3345 case OB_DIRECTION_SOUTHWEST:
3346 /* not implemented */
3348 g_assert_not_reached();
3349 dest = 0; /* suppress warning */
3354 ObClient* client_under_pointer()
3358 ObClient *ret = NULL;
3360 if (screen_pointer_pos(&x, &y)) {
3361 for (it = stacking_list; it; it = g_list_next(it)) {
3362 if (WINDOW_IS_CLIENT(it->data)) {
3363 ObClient *c = WINDOW_AS_CLIENT(it->data);
3364 if (c->frame->visible &&
3365 RECT_CONTAINS(c->frame->area, x, y)) {
3375 gboolean client_has_group_siblings(ObClient *self)
3377 return self->group && self->group->members->next;