X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=openbox%2Fclient.c;h=6cd8454b062247b0186765e2b4e728e6e72be55e;hb=58cfbb7f8419e084af6b6b8b00c88ed270c29e88;hp=7b7872b37d7a0f8ea5b51093565f92ee6be18901;hpb=00042637e8b8bef92456d96547230ec710cf6a7b;p=chaz%2Fopenbox diff --git a/openbox/client.c b/openbox/client.c index 7b7872b3..6cd8454b 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -1,5 +1,8 @@ #include "client.h" +#include "slit.h" +#include "startup.h" #include "screen.h" +#include "moveresize.h" #include "prop.h" #include "extensions.h" #include "frame.h" @@ -8,6 +11,7 @@ #include "focus.h" #include "stacking.h" #include "dispatch.h" +#include "openbox.h" #include "group.h" #include "config.h" @@ -24,9 +28,6 @@ GList *client_list = NULL; GHashTable *client_map = NULL; -static Window *client_startup_stack_order = NULL; -static gulong client_startup_stack_size = 0; - static void client_get_all(Client *self); static void client_toggle_border(Client *self, gboolean show); static void client_get_area(Client *self); @@ -38,10 +39,7 @@ static void client_get_gravity(Client *self); static void client_showhide(Client *self); static void client_change_allowed_actions(Client *self); static void client_change_state(Client *self); -static void client_move_onscreen(Client *self); -static Client *search_focus_tree(Client *node, Client *skip); static void client_apply_startup_state(Client *self); -static Client *search_modal_tree(Client *node, Client *skip); static guint map_hash(Window *w) { return *w; } static gboolean map_key_comp(Window *w1, Window *w2) { return *w1 == *w2; } @@ -51,10 +49,6 @@ void client_startup() client_map = g_hash_table_new((GHashFunc)map_hash, (GEqualFunc)map_key_comp); - /* save the stacking order on startup! */ - PROP_GET32U(ob_root, net_client_list_stacking, window, - client_startup_stack_order, client_startup_stack_size); - client_set_list(); } @@ -78,7 +72,7 @@ void client_set_list() } else windows = NULL; - PROP_SET32A(ob_root, net_client_list, window, windows, size); + PROP_SETA32(ob_root, net_client_list, window, (guint32*)windows, size); if (windows) g_free(windows); @@ -86,12 +80,44 @@ void client_set_list() stacking_set_list(); } +/* +void client_foreach_transient(Client *self, ClientForeachFunc func, void *data) +{ + GSList *it; + + for (it = self->transients; it; it = it->next) { + if (!func(it->data, data)) return; + client_foreach_transient(it->data, func, data); + } +} + +void client_foreach_ancestor(Client *self, ClientForeachFunc func, void *data) +{ + if (self->transient_for) { + if (self->transient_for != TRAN_GROUP) { + if (!func(self->transient_for, data)) return; + client_foreach_ancestor(self->transient_for, func, data); + } else { + GSList *it; + + for (it = self->group->members; it; it = it->next) + if (it->data != self && + ((Client*)it->data)->transient_for != TRAN_GROUP) { + if (!func(it->data, data)) return; + client_foreach_ancestor(it->data, func, data); + } + } + } +} +*/ + void client_manage_all() { unsigned int i, j, nchild; Window w, *children; XWMHints *wmhints; XWindowAttributes attrib; + Client *active; XQueryTree(ob_display, ob_root, &w, &w, &children, &nchild); @@ -127,19 +153,22 @@ void client_manage_all() why with stacking_lower? Why, because then windows who aren't in the stacking list are on the top where you can see them instead of buried at the bottom! */ - for (i = client_startup_stack_size; i > 0; --i) { + for (i = startup_stack_size; i > 0; --i) { Client *c; - w = client_startup_stack_order[i-1]; + w = startup_stack_order[i-1]; c = g_hash_table_lookup(client_map, &w); - if (c) stacking_lower(c); + if (c) stacking_lower(CLIENT_AS_WINDOW(c)); } - g_free(client_startup_stack_order); - client_startup_stack_order = NULL; - client_startup_stack_size = 0; + g_free(startup_stack_order); + startup_stack_order = NULL; + startup_stack_size = 0; - if (config_focus_new) - focus_fallback(Fallback_NoFocus); + if (config_focus_new) { + active = g_hash_table_lookup(client_map, &startup_active); + if (!(active && client_focus(active))) + focus_fallback(Fallback_NoFocus); + } } void client_manage(Window window) @@ -148,8 +177,7 @@ void client_manage(Window window) XEvent e; XWindowAttributes attrib; XSetWindowAttributes attrib_set; -/* XWMHints *wmhint; */ - guint i; + XWMHints *wmhint; grab_server(TRUE); @@ -170,18 +198,18 @@ void client_manage(Window window) return; /* don't manage it */ } -/* /\* is the window a docking app *\/ + /* is the window a docking app */ if ((wmhint = XGetWMHints(ob_display, window))) { if ((wmhint->flags & StateHint) && wmhint->initial_state == WithdrawnState) { - /\* XXX: make dock apps work! *\/ + slit_add(window, wmhint); grab_server(FALSE); XFree(wmhint); return; } XFree(wmhint); } -*/ + g_message("Managing window: %lx", window); /* choose the events we want to receive on the CLIENT window */ @@ -194,6 +222,7 @@ void client_manage(Window window) /* create the Client struct, and populate it from the hints on the window */ self = g_new(Client, 1); + self->obwin.type = Window_Client; self->window = window; client_get_all(self); @@ -214,20 +243,14 @@ void client_manage(Window window) grab_server(FALSE); client_list = g_list_append(client_list, self); - stacking_list = g_list_append(stacking_list, self); + stacking_add(self); g_assert(!g_hash_table_lookup(client_map, &self->window)); g_hash_table_insert(client_map, &self->window, self); /* update the focus lists */ - if (self->desktop == DESKTOP_ALL) { - for (i = 0; i < screen_num_desktops; ++i) - focus_order[i] = g_list_append(focus_order[i], self); - } else { - i = self->desktop; - focus_order[i] = g_list_append(focus_order[i], self); - } + focus_order_add_new(self); - stacking_raise(self); + stacking_raise(CLIENT_AS_WINDOW(self)); screen_update_struts(); @@ -235,26 +258,47 @@ void client_manage(Window window) client_showhide(self); - dispatch_client(Event_Client_Mapped, self, 0, 0); - /* focus the new window? */ if (ob_state != State_Starting) { - if (config_focus_new) - client_focus(self); - else if (self->transient_for) { + Client *parent; + gboolean group_foc = FALSE; + + parent = NULL; + + if (self->group) { + GSList *it; + + for (it = self->group->members; it; it = it->next) + if (client_focused(it->data)) { + group_foc = TRUE; + break; + } + } + if (!group_foc && self->transient_for) { if (self->transient_for != TRAN_GROUP) {/* transient of a window */ - if (focus_client == self->transient_for) - client_focus(self); + parent = self->transient_for; } else { /* transient of a group */ GSList *it; for (it = self->group->members; it; it = it->next) - if (focus_client == it->data) { - client_focus(self); - break; - } + if (it->data != self && + ((Client*)it->data)->transient_for != TRAN_GROUP) + parent = it->data; } } + /* note the check against Type_Normal, not client_normal(self), which + would also include dialog types. in this case we want more strict + rules for focus */ + if ((config_focus_new && + (self->type == Type_Normal || + (self->type == Type_Dialog && + (group_foc || + (!parent && (!self->group || + !self->group->members->next)))))) || + (parent && (client_focused(parent) || + client_search_focus_tree(parent)))) { + client_focus(self); + } } /* update the list hints */ @@ -263,6 +307,8 @@ void client_manage(Window window) /* make sure the window is visible */ client_move_onscreen(self); + dispatch_client(Event_Client_Mapped, self, 0, 0); + g_message("Managed window 0x%lx", window); } @@ -274,7 +320,6 @@ void client_unmanage_all() void client_unmanage(Client *self) { - guint i; int j; GSList *it; @@ -292,17 +337,11 @@ void client_unmanage(Client *self) frame_hide(self->frame); client_list = g_list_remove(client_list, self); - stacking_list = g_list_remove(stacking_list, self); + stacking_remove(self); g_hash_table_remove(client_map, &self->window); /* update the focus lists */ - if (self->desktop == DESKTOP_ALL) { - for (i = 0; i < screen_num_desktops; ++i) - focus_order[i] = g_list_remove(focus_order[i], self); - } else { - i = self->desktop; - focus_order[i] = g_list_remove(focus_order[i], self); - } + focus_order_remove(self); /* once the client is out of the list, update the struts to remove it's influence */ @@ -329,8 +368,17 @@ void client_unmanage(Client *self) } } - if (focus_client == self) + if (moveresize_client == self) + moveresize_end(TRUE); + + if (focus_client == self) { + XEvent e; + + /* focus the last focused window on the desktop, and ignore enter + events from the unmap so it doesnt mess with the focus */ + while (XCheckTypedEvent(ob_display, EnterNotify, &e)); client_unfocus(self); + } /* remove from its group */ if (self->group) { @@ -361,6 +409,7 @@ void client_unmanage(Client *self) XMapWindow(ob_display, self->window); } + g_message("Unmanaged window 0x%lx", self->window); /* free all data allocated in the client struct */ @@ -380,7 +429,7 @@ void client_unmanage(Client *self) client_set_list(); } -static void client_move_onscreen(Client *self) +void client_move_onscreen(Client *self) { Rect *a; int x = self->frame->area.x, y = self->frame->area.y; @@ -396,7 +445,7 @@ static void client_move_onscreen(Client *self) y = a->y; frame_frame_gravity(self->frame, &x, &y); /* get where the client - should be */ + should be */ client_configure(self , Corner_TopLeft, x, y, self->area.width, self->area.height, TRUE, TRUE); @@ -477,6 +526,7 @@ static void client_get_all(Client *self) /* defaults */ self->frame = NULL; self->title = self->icon_title = NULL; + self->title_count = 1; self->name = self->class = self->role = NULL; self->wmstate = NormalState; self->transient = FALSE; @@ -490,11 +540,12 @@ static void client_get_all(Client *self) self->nicons = 0; client_get_area(self); + client_update_transient_for(self); + client_update_wmhints(self); client_get_desktop(self); client_get_state(self); client_get_shaped(self); - client_update_transient_for(self); client_get_mwm_hints(self); client_get_type(self);/* this can change the mwmhints for special cases */ @@ -508,22 +559,12 @@ static void client_get_all(Client *self) (min/max sizes), so we're ready to set up the decorations/functions */ client_setup_decor_and_functions(self); - client_update_wmhints(self); client_update_title(self); - client_update_icon_title(self); client_update_class(self); client_update_strut(self); client_update_icons(self); client_update_kwm_icon(self); - /* this makes sure that these windows appear on all desktops */ - if (self->type == Type_Desktop) - self->desktop = DESKTOP_ALL; - - /* set the desktop hint, to make sure that it always exists, and to - reflect any changes we've made here */ - PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop); - client_change_state(self); } @@ -541,28 +582,50 @@ static void client_get_area(Client *self) static void client_get_desktop(Client *self) { - unsigned int d; + guint32 d; - if (PROP_GET32(self->window, net_wm_desktop, cardinal, d)) { + if (PROP_GET32(self->window, net_wm_desktop, cardinal, &d)) { if (d >= screen_num_desktops && d != DESKTOP_ALL) d = screen_num_desktops - 1; self->desktop = d; - } else { - /* defaults to the current desktop */ - self->desktop = screen_desktop; + } else { + gboolean trdesk = FALSE; + + if (self->transient_for) { + if (self->transient_for != TRAN_GROUP) { + self->desktop = self->transient_for->desktop; + trdesk = TRUE; + } else { + GSList *it; + + for (it = self->group->members; it; it = it->next) + if (it->data != self && + ((Client*)it->data)->transient_for != TRAN_GROUP) { + self->desktop = ((Client*)it->data)->desktop; + trdesk = TRUE; + break; + } + } + } + if (!trdesk) + /* defaults to the current desktop */ + self->desktop = screen_desktop; + + /* set the desktop hint, to make sure that it always exists */ + PROP_SET32(self->window, net_wm_desktop, cardinal, self->desktop); } } static void client_get_state(Client *self) { - gulong *state; - gulong num; + guint32 *state; + guint num; self->modal = self->shaded = self->max_horz = self->max_vert = self->fullscreen = self->above = self->below = self->iconic = self->skip_taskbar = self->skip_pager = FALSE; - if (PROP_GET32U(self->window, net_wm_state, atom, state, num)) { + if (PROP_GETA32(self->window, net_wm_state, atom, &state, &num)) { gulong i; for (i = 0; i < num; ++i) { if (state[i] == prop_atoms.net_wm_state_modal) @@ -615,22 +678,24 @@ void client_update_transient_for(Client *self) Window t = None; Client *c = NULL; - if (XGetTransientForHint(ob_display, self->window, &t) && - t != self->window) { /* cant be transient to itself! */ + if (XGetTransientForHint(ob_display, self->window, &t)) { self->transient = TRUE; - c = g_hash_table_lookup(client_map, &t); - g_assert(c != self);/* if this happens then we need to check for it*/ - - if (!c && self->group) { - /* not transient to a client, see if it is transient for a - group */ - if (t == self->group->leader || - t == None || - t == ob_root) { - /* window is a transient for its group! */ - c = TRAN_GROUP; - } - } + if (t != self->window) { /* cant be transient to itself! */ + c = g_hash_table_lookup(client_map, &t); + /* if this happens then we need to check for it*/ + g_assert(c != self); + + if (!c && self->group) { + /* not transient to a client, see if it is transient for a + group */ + if (t == self->group->leader || + t == None || + t == ob_root) { + /* window is a transient for its group! */ + c = TRAN_GROUP; + } + } + } } else self->transient = FALSE; @@ -641,7 +706,8 @@ void client_update_transient_for(Client *self) /* remove from old parents */ for (it = self->group->members; it; it = it->next) - if (it->data != self) + if (it->data != self && + (((Client*)it->data)->transient_for != TRAN_GROUP)) ((Client*)it->data)->transients = g_slist_remove(((Client*)it->data)->transients, self); } else if (self->transient_for != NULL) { /* transient of window */ @@ -655,9 +721,22 @@ void client_update_transient_for(Client *self) /* add to new parents */ for (it = self->group->members; it; it = it->next) - if (it->data != self) + if (it->data != self && + (((Client*)it->data)->transient_for != TRAN_GROUP)) ((Client*)it->data)->transients = g_slist_append(((Client*)it->data)->transients, self); + + /* remove all transients which are in the group, that causes + circlular pointer hell of doom */ + for (it = self->group->members; it; it = it->next) { + GSList *sit, *next; + for (sit = self->transients; sit; sit = next) { + next = sit->next; + if (sit->data == it->data) + self->transients = g_slist_remove(self->transients, + sit->data); + } + } } else if (self->transient_for != NULL) { /* transient of window */ /* add to new parent */ self->transient_for->transients = @@ -668,12 +747,13 @@ void client_update_transient_for(Client *self) static void client_get_mwm_hints(Client *self) { - unsigned long num; - unsigned long *hints; + guint num; + guint32 *hints; self->mwmhints.flags = 0; /* default to none */ - if (PROP_GET32U(self->window, motif_wm_hints, motif_wm_hints, hints, num)) { + if (PROP_GETA32(self->window, motif_wm_hints, motif_wm_hints, + &hints, &num)) { if (num >= MWM_ELEMENTS) { self->mwmhints.flags = hints[0]; self->mwmhints.functions = hints[1]; @@ -685,11 +765,12 @@ static void client_get_mwm_hints(Client *self) void client_get_type(Client *self) { - gulong *val, num, i; + guint num, i; + guint32 *val; self->type = -1; - if (PROP_GET32U(self->window, net_wm_window_type, atom, val, num)) { + if (PROP_GETA32(self->window, net_wm_window_type, atom, &val, &num)) { /* use the first value that we know about in the array */ for (i = 0; i < num; ++i) { if (val[i] == prop_atoms.net_wm_window_type_desktop) @@ -735,13 +816,13 @@ void client_get_type(Client *self) void client_update_protocols(Client *self) { - Atom *proto; - gulong num_return, i; + guint32 *proto; + guint num_return, i; self->focus_notify = FALSE; self->delete_window = FALSE; - if (PROP_GET32U(self->window, wm_protocols, atom, proto, num_return)) { + if (PROP_GETA32(self->window, wm_protocols, atom, &proto, &num_return)) { for (i = 0; i < num_return; ++i) { if (proto[i] == prop_atoms.wm_delete_window) { /* this means we can request the window to close */ @@ -927,23 +1008,39 @@ void client_setup_decor_and_functions(Client *self) if (!(self->decorations & Decor_Titlebar)) self->functions &= ~Func_Shade; + /* now we need to check against rules for the client's current state */ + if (self->fullscreen) { + self->functions &= (Func_Close | Func_Fullscreen | Func_Iconify); + self->decorations = 0; + } + client_change_allowed_actions(self); if (self->frame) { + /* this makes sure that these windows appear on all desktops */ + if (self->type == Type_Desktop && self->desktop != DESKTOP_ALL) + client_set_desktop(self, DESKTOP_ALL, FALSE); + /* change the decors on the frame, and with more/less decorations, we may also need to be repositioned */ frame_adjust_area(self->frame, TRUE, TRUE); /* with new decor, the window's maximized size may change */ client_remaximize(self); + } else { + /* this makes sure that these windows appear on all desktops */ + if (self->type == Type_Desktop && self->desktop != DESKTOP_ALL) + self->desktop = DESKTOP_ALL; } } static void client_change_allowed_actions(Client *self) { - Atom actions[9]; + guint32 actions[9]; int num = 0; - actions[num++] = prop_atoms.net_wm_action_change_desktop; + /* desktop windows are kept on all desktops */ + if (self->type != Type_Desktop) + actions[num++] = prop_atoms.net_wm_action_change_desktop; if (self->functions & Func_Shade) actions[num++] = prop_atoms.net_wm_action_shade; @@ -962,7 +1059,7 @@ static void client_change_allowed_actions(Client *self) actions[num++] = prop_atoms.net_wm_action_maximize_vert; } - PROP_SET32A(self->window, net_wm_allowed_actions, atom, actions, num); + PROP_SETA32(self->window, net_wm_allowed_actions, atom, actions, num); /* make sure the window isn't breaking any rules now */ @@ -1005,6 +1102,7 @@ void client_update_wmhints(Client *self) { XWMHints *hints; gboolean ur = FALSE; + GSList *it; /* assume a window takes input if it doesnt specify */ self->can_focus = TRUE; @@ -1023,35 +1121,57 @@ void client_update_wmhints(Client *self) ur = TRUE; if (!(hints->flags & WindowGroupHint)) - hints->window_group = None; /* no group */ + hints->window_group = None; + /* did the group state change? */ - if (hints->window_group != (self->group ? self->group->leader : None)){ + if (hints->window_group != + (self->group ? self->group->leader : None)) { /* remove from the old group if there was one */ - if (self->group != NULL) + if (self->group != NULL) { + /* remove transients of the group */ + for (it = self->group->members; it; it = it->next) + if (it->data != self && + ((Client*)it->data)->transient_for == TRAN_GROUP) { + self->transients = g_slist_remove(self->transients, + it->data); + } group_remove(self->group, self); - if (hints->window_group != None) + self->group = NULL; + } + if (hints->window_group != None) { self->group = group_add(hints->window_group, self); + /* add other transients of the group that are already + set up */ + for (it = self->group->members; it; it = it->next) + if (it->data != self && + ((Client*)it->data)->transient_for == TRAN_GROUP) + self->transients = g_slist_append(self->transients, + it->data); + } + /* because the self->transient flag wont change from this call, we don't need to update the window's type and such, only its - transient_for, and the transients lists of other windows in the - group may be affected */ + transient_for, and the transients lists of other windows in + the group may be affected */ client_update_transient_for(self); } - if (hints->flags & IconPixmapHint) { - client_update_kwm_icon(self); - /* try get the kwm icon first, this is a fallback only */ - if (self->pixmap_icon == None) { - self->pixmap_icon = hints->icon_pixmap; - if (hints->flags & IconMaskHint) - self->pixmap_icon_mask = hints->icon_mask; - else - self->pixmap_icon_mask = None; - - if (self->frame) - frame_adjust_icon(self->frame); - } + client_update_kwm_icon(self); + /* try get the kwm icon first, this is a fallback only */ + if (self->pixmap_icon == None) { + if (hints->flags & IconPixmapHint) { + if (self->pixmap_icon == None) { + self->pixmap_icon = hints->icon_pixmap; + if (hints->flags & IconMaskHint) + self->pixmap_icon_mask = hints->icon_mask; + else + self->pixmap_icon_mask = None; + + if (self->frame) + frame_adjust_icon(self->frame); + } + } } XFree(hints); @@ -1070,74 +1190,84 @@ void client_update_wmhints(Client *self) void client_update_title(Client *self) { - gchar *data = NULL; + GList *it; + guint32 nums; + guint i; + char *data = NULL; + gboolean read_title; g_free(self->title); /* try netwm */ - if (!PROP_GETS(self->window, net_wm_name, utf8, data)) { + if (!PROP_GETS(self->window, net_wm_name, utf8, &data)) /* try old x stuff */ - if (PROP_GETS(self->window, wm_name, string, data)) { - /* convert it to UTF-8 */ - gsize r, w; - gchar *u; - - u = g_locale_to_utf8(data, -1, &r, &w, NULL); - if (u == NULL) { - g_warning("Unable to convert string to UTF-8"); - } else { - g_free(data); - data = u; - } - } - if (data == NULL) + if (!PROP_GETS(self->window, wm_name, locale, &data)) data = g_strdup("Unnamed Window"); - PROP_SETS(self->window, net_wm_visible_name, utf8, data); + /* look for duplicates and append a number */ + nums = 0; + for (it = client_list; it; it = it->next) + if (it->data != self) { + Client *c = it->data; + if (0 == strncmp(c->title, data, strlen(data))) + nums |= 1 << c->title_count; + } + /* find first free number */ + for (i = 1; i <= 32; ++i) + if (!(nums & (1 << i))) { + if (self->title_count == 1 || i == 1) + self->title_count = i; + break; + } + /* dont display the number for the first window */ + if (self->title_count > 1) { + char *vdata, *ndata; + ndata = g_strdup_printf(" - [%u]", self->title_count); + vdata = g_strconcat(data, ndata, NULL); + g_free(ndata); + g_free(data); + data = vdata; } + PROP_SETS(self->window, net_wm_visible_name, data); + self->title = data; if (self->frame) frame_adjust_title(self->frame); -} - -void client_update_icon_title(Client *self) -{ - gchar *data = NULL; + /* update the icon title */ + data = NULL; g_free(self->icon_title); - + + read_title = TRUE; /* try netwm */ - if (!PROP_GETS(self->window, net_wm_icon_name, utf8, data)) { + if (!PROP_GETS(self->window, net_wm_icon_name, utf8, &data)) /* try old x stuff */ - if (PROP_GETS(self->window, wm_icon_name, string, data)) { - /* convert it to UTF-8 */ - gsize r, w; - gchar *u; - - u = g_locale_to_utf8(data, -1, &r, &w, NULL); - if (u == NULL) { - g_warning("Unable to convert string to UTF-8"); - } else { - g_free(data); - data = u; - } - } - if (data == NULL) - data = g_strdup("Unnamed Window"); + if (!PROP_GETS(self->window, wm_icon_name, locale, &data)) { + data = g_strdup(self->title); + read_title = FALSE; + } - PROP_SETS(self->window, net_wm_visible_icon_name, utf8, data); + /* append the title count, dont display the number for the first window */ + if (read_title && self->title_count > 1) { + char *vdata, *ndata; + ndata = g_strdup_printf(" - [%u]", self->title_count); + vdata = g_strconcat(data, ndata, NULL); + g_free(ndata); + g_free(data); + data = vdata; } + PROP_SETS(self->window, net_wm_visible_icon_name, data); + self->icon_title = data; } void client_update_class(Client *self) { - GPtrArray *data; - gchar *s; - guint i; + char **data; + char *s; if (self->name) g_free(self->name); if (self->class) g_free(self->class); @@ -1145,20 +1275,16 @@ void client_update_class(Client *self) self->name = self->class = self->role = NULL; - data = g_ptr_array_new(); - - if (PROP_GETSA(self->window, wm_class, string, data)) { - if (data->len > 0) - self->name = g_strdup(g_ptr_array_index(data, 0)); - if (data->len > 1) - self->class = g_strdup(g_ptr_array_index(data, 1)); + if (PROP_GETSS(self->window, wm_class, locale, &data)) { + if (data[0]) { + self->name = g_strdup(data[0]); + if (data[1]) + self->class = g_strdup(data[1]); + } + g_strfreev(data); } - - for (i = 0; i < data->len; ++i) - g_free(g_ptr_array_index(data, i)); - g_ptr_array_free(data, TRUE); - if (PROP_GETS(self->window, wm_window_role, string, s)) + if (PROP_GETS(self->window, wm_window_role, locale, &s)) self->role = g_strdup(s); if (self->name == NULL) self->name = g_strdup(""); @@ -1168,13 +1294,18 @@ void client_update_class(Client *self) void client_update_strut(Client *self) { - gulong *data; + guint num; + guint32 *data; - if (PROP_GET32A(self->window, net_wm_strut, cardinal, data, 4)) { - STRUT_SET(self->strut, data[0], data[2], data[1], data[3]); - g_free(data); - } else + if (!PROP_GETA32(self->window, net_wm_strut, cardinal, &data, &num)) { STRUT_SET(self->strut, 0, 0, 0, 0); + } else { + if (num == 4) + STRUT_SET(self->strut, data[0], data[2], data[1], data[3]); + else + STRUT_SET(self->strut, 0, 0, 0, 0); + g_free(data); + } /* updating here is pointless while we're being mapped cuz we're not in the client list yet */ @@ -1184,9 +1315,9 @@ void client_update_strut(Client *self) void client_update_icons(Client *self) { - unsigned long num; - unsigned long *data; - unsigned long w, h, i; + guint num; + guint32 *data; + guint w, h, i; int j; for (j = 0; j < self->nicons; ++j) @@ -1195,7 +1326,7 @@ void client_update_icons(Client *self) g_free(self->icons); self->nicons = 0; - if (PROP_GET32U(self->window, net_wm_icon, cardinal, data, num)) { + if (PROP_GETA32(self->window, net_wm_icon, cardinal, &data, &num)) { /* figure out how many valid icons are in here */ i = 0; while (num - i > 2) { @@ -1228,14 +1359,18 @@ void client_update_icons(Client *self) void client_update_kwm_icon(Client *self) { - Pixmap *data; + guint num; + guint32 *data; - if (PROP_GET32A(self->window, kwm_win_icon, kwm_win_icon, data, 2)) { - self->pixmap_icon = data[0]; - self->pixmap_icon_mask = data[1]; - g_free(data); - } else { + if (!PROP_GETA32(self->window, kwm_win_icon, kwm_win_icon, &data, &num)) { self->pixmap_icon = self->pixmap_icon_mask = None; + } else { + if (num == 2) { + self->pixmap_icon = data[0]; + self->pixmap_icon_mask = data[1]; + } else + self->pixmap_icon = self->pixmap_icon_mask = None; + g_free(data); } if (self->frame) frame_adjust_icon(self->frame); @@ -1243,13 +1378,13 @@ void client_update_kwm_icon(Client *self) static void client_change_state(Client *self) { - unsigned long state[2]; - Atom netstate[10]; - int num; + guint32 state[2]; + guint32 netstate[10]; + guint num; state[0] = self->wmstate; state[1] = None; - PROP_SET32A(self->window, wm_state, wm_state, state, 2); + PROP_SETA32(self->window, wm_state, wm_state, state, 2); num = 0; if (self->modal) @@ -1272,7 +1407,7 @@ static void client_change_state(Client *self) netstate[num++] = prop_atoms.net_wm_state_above; if (self->below) netstate[num++] = prop_atoms.net_wm_state_below; - PROP_SET32A(self->window, net_wm_state, atom, netstate, num); + PROP_SETA32(self->window, net_wm_state, atom, netstate, num); client_calc_layer(self); @@ -1280,58 +1415,104 @@ static void client_change_state(Client *self) frame_adjust_state(self->frame); } -static Client *search_focus_tree(Client *node, Client *skip) +Client *client_search_focus_tree(Client *self) { GSList *it; Client *ret; - for (it = node->transients; it != NULL; it = it->next) { - Client *c = it->data; - if (c == skip) continue; /* circular? */ - if ((ret = search_focus_tree(c, skip))) return ret; - if (client_focused(c)) return c; + for (it = self->transients; it != NULL; it = it->next) { + if (client_focused(it->data)) return it->data; + if ((ret = client_search_focus_tree(it->data))) return ret; } return NULL; } -void client_calc_layer(Client *self) +Client *client_search_focus_tree_full(Client *self) { - StackLayer l; - gboolean fs; - Client *c; - - /* are we fullscreen, or do we have a fullscreen transient parent? */ - c = self; - fs = FALSE; - while (c && c != TRAN_GROUP) { /* XXX do smthng with the TRAN_GROUP case?*/ - if (c->fullscreen) { - fs = TRUE; - break; - } - c = c->transient_for; - } - if (!fs && self->fullscreen) { - /* is one of our transients focused? */ - c = search_focus_tree(self, self); - if (c != NULL) fs = TRUE; + if (self->transient_for) { + if (self->transient_for != TRAN_GROUP) { + return client_search_focus_tree_full(self->transient_for); + } else { + GSList *it; + + for (it = self->group->members; it; it = it->next) + if (((Client*)it->data)->transient_for != TRAN_GROUP) { + Client *c; + if ((c = client_search_focus_tree_full(it->data))) + return c; + } + return NULL; + } + } else { + /* this function checks the whole tree, the client_search_focus_tree + does not, so we need to check this window */ + if (client_focused(self)) + return self; + return client_search_focus_tree(self); } - - if (self->iconic) l = Layer_Icon; - else if (fs) l = Layer_Fullscreen; +} + +static StackLayer calc_layer(Client *self) +{ + StackLayer l; + + if (self->fullscreen) l = Layer_Fullscreen; else if (self->type == Type_Desktop) l = Layer_Desktop; else if (self->type == Type_Dock) { - if (!self->below) l = Layer_Top; - else l = Layer_Normal; + if (!self->below) l = Layer_Top; + else l = Layer_Normal; } else if (self->above) l = Layer_Above; else if (self->below) l = Layer_Below; else l = Layer_Normal; - - if (l != self->layer) { - self->layer = l; - if (self->frame) - stacking_raise(self); + + return l; +} + +static void calc_recursive(Client *self, Client *orig, StackLayer l, + gboolean raised) +{ + StackLayer old, own; + GSList *it; + + old = self->layer; + own = calc_layer(self); + self->layer = l > own ? l : own; + + for (it = self->transients; it; it = it->next) + calc_recursive(it->data, orig, l, raised ? raised : l != old); + + if (!raised && l != old) + if (orig->frame) /* only restack if the original window is managed */ + stacking_raise(CLIENT_AS_WINDOW(self)); +} + +void client_calc_layer(Client *self) +{ + StackLayer l; + Client *orig; + + orig = self; + + /* transients take on the layer of their parents */ + if (self->transient_for) { + if (self->transient_for != TRAN_GROUP) { + self = self->transient_for; + } else { + GSList *it; + + for (it = self->group->members; it; it = it->next) + if (it->data != self && + ((Client*)it->data)->transient_for != TRAN_GROUP) { + self = it->data; + break; + } + } } + + l = calc_layer(self); + + calc_recursive(self, orig, l, FALSE); } gboolean client_should_show(Client *self) @@ -1413,6 +1594,7 @@ void client_configure(Client *self, Corner anchor, int x, int y, int w, int h, y = 0; w = screen_physical_size.width; h = screen_physical_size.height; + user = FALSE; /* ignore that increment etc shit when in fullscreen */ } else { /* set the size and position if maximized */ if (self->max_horz) { @@ -1557,24 +1739,18 @@ void client_fullscreen(Client *self, gboolean fs, gboolean savearea) self->fullscreen == fs) return; /* already done */ self->fullscreen = fs; - client_change_state(self); /* change the state hints on the client */ + client_change_state(self); /* change the state hints on the client, + and adjust out layer/stacking */ if (fs) { - /* save the functions and remove them */ - self->pre_fs_func = self->functions; - self->functions &= (Func_Close | Func_Fullscreen | - Func_Iconify); - /* save the decorations and remove them */ - self->pre_fs_decor = self->decorations; - self->decorations = 0; if (savearea) { - long dimensions[4]; + guint32 dimensions[4]; dimensions[0] = self->area.x; dimensions[1] = self->area.y; dimensions[2] = self->area.width; dimensions[3] = self->area.height; - PROP_SET32A(self->window, openbox_premax, cardinal, + PROP_SETA32(self->window, openbox_premax, cardinal, dimensions, 4); } @@ -1582,37 +1758,32 @@ void client_fullscreen(Client *self, gboolean fs, gboolean savearea) as appropriate when the window is fullscreened */ x = y = w = h = 0; } else { - long *dimensions; - - self->functions = self->pre_fs_func; - self->decorations = self->pre_fs_decor; - - if (PROP_GET32A(self->window, openbox_premax, cardinal, - dimensions, 4)) { - x = dimensions[0]; - y = dimensions[1]; - w = dimensions[2]; - h = dimensions[3]; + guint num; + gint32 *dimensions; + + /* pick some fallbacks... */ + x = screen_area(self->desktop)->x + + screen_area(self->desktop)->width / 4; + y = screen_area(self->desktop)->y + + screen_area(self->desktop)->height / 4; + w = screen_area(self->desktop)->width / 2; + h = screen_area(self->desktop)->height / 2; + + if (PROP_GETA32(self->window, openbox_premax, cardinal, + (guint32**)&dimensions, &num)) { + if (num == 4) { + x = dimensions[0]; + y = dimensions[1]; + w = dimensions[2]; + h = dimensions[3]; + } g_free(dimensions); - } else { - /* pick some fallbacks... */ - x = screen_area(self->desktop)->x + - screen_area(self->desktop)->width / 4; - y = screen_area(self->desktop)->y + - screen_area(self->desktop)->height / 4; - w = screen_area(self->desktop)->width / 2; - h = screen_area(self->desktop)->height / 2; } } - client_change_allowed_actions(self); /* based on the new _functions */ - - /* when fullscreening, don't obey things like increments, fill the - screen */ - client_configure(self, Corner_TopLeft, x, y, w, h, !fs, TRUE); + client_setup_decor_and_functions(self); - /* raise (back) into our stacking layer */ - stacking_raise(self); + client_configure(self, Corner_TopLeft, x, y, w, h, TRUE, TRUE); /* try focus us when we go into fullscreen mode */ client_focus(self); @@ -1620,32 +1791,32 @@ void client_fullscreen(Client *self, gboolean fs, gboolean savearea) void client_iconify(Client *self, gboolean iconic, gboolean curdesk) { - /* move up the transient chain as far as possible first if deiconifying */ - if (!iconic) - while (self->transient_for) { - if (self->transient_for != TRAN_GROUP) { - if (self->transient_for->iconic == iconic) - break; - self = self->transient_for; - } else { - GSList *it; + GSList *it; - /* the check for TRAN_GROUP is to prevent an infinate loop with - 2 transients of the same group at the head of the group's - members list */ - for (it = self->group->members; it; it = it->next) { - Client *c = it->data; + /* move up the transient chain as far as possible first */ + if (self->transient_for) { + if (self->transient_for != TRAN_GROUP) { + if (self->transient_for->iconic != iconic) { + client_iconify(self->transient_for, iconic, curdesk); + return; + } + } else { + GSList *it; - if (c != self && c->transient_for->iconic != iconic && - (c->transient_for != TRAN_GROUP || - c->group != self->group)) { - self = it->data; - break; - } + /* the check for TRAN_GROUP is to prevent an infinate loop with + 2 transients of the same group at the head of the group's + members list */ + for (it = self->group->members; it; it = it->next) { + Client *c = it->data; + if (c != self && c->iconic != iconic && + c->transient_for != TRAN_GROUP) { + client_iconify(it->data, iconic, curdesk); + break; } - if (it == NULL) break; } + if (it != NULL) return; } + } if (self->iconic == iconic) return; /* nothing to do */ @@ -1660,11 +1831,19 @@ void client_iconify(Client *self, gboolean iconic, gboolean curdesk) /* we unmap the client itself so that we can get MapRequest events, and because the ICCCM tells us to! */ XUnmapWindow(ob_display, self->window); + + /* update the focus lists.. iconic windows go to the bottom of the + list, put the new iconic window at the 'top of the bottom'. */ + focus_order_to_top(self); } else { if (curdesk) client_set_desktop(self, screen_desktop, FALSE); self->wmstate = self->shaded ? IconicState : NormalState; XMapWindow(ob_display, self->window); + + /* this puts it after the current focused window */ + focus_order_remove(self); + focus_order_add_new(self); } client_change_state(self); client_showhide(self); @@ -1674,12 +1853,8 @@ void client_iconify(Client *self, gboolean iconic, gboolean curdesk) self, 0, 0); /* iconify all transients */ - if (self->transients) { - GSList *it; - - for (it = self->transients; it != NULL; it = it->next) - if (it->data != self) client_iconify(it->data, iconic, curdesk); - } + for (it = self->transients; it != NULL; it = it->next) + if (it->data != self) client_iconify(it->data, iconic, curdesk); } void client_maximize(Client *self, gboolean max, int dir, gboolean savearea) @@ -1708,8 +1883,9 @@ void client_maximize(Client *self, gboolean max, int dir, gboolean savearea) if (max) { if (savearea) { - long dimensions[4]; - long *readdim; + gint32 dimensions[4]; + gint32 *readdim; + guint num; dimensions[0] = x; dimensions[1] = y; @@ -1718,48 +1894,53 @@ void client_maximize(Client *self, gboolean max, int dir, gboolean savearea) /* get the property off the window and use it for the dimensions we are already maxed on */ - if (PROP_GET32A(self->window, openbox_premax, cardinal, - readdim, 4)) { - if (self->max_horz) { - dimensions[0] = readdim[0]; - dimensions[2] = readdim[2]; - } - if (self->max_vert) { - dimensions[1] = readdim[1]; - dimensions[3] = readdim[3]; - } + if (PROP_GETA32(self->window, openbox_premax, cardinal, + (guint32**)&readdim, &num)) { + if (num == 4) { + if (self->max_horz) { + dimensions[0] = readdim[0]; + dimensions[2] = readdim[2]; + } + if (self->max_vert) { + dimensions[1] = readdim[1]; + dimensions[3] = readdim[3]; + } + } g_free(readdim); } - PROP_SET32A(self->window, openbox_premax, cardinal, - dimensions, 4); + PROP_SETA32(self->window, openbox_premax, cardinal, + (guint32*)dimensions, 4); } } else { - long *dimensions; + guint num; + gint32 *dimensions; + + /* pick some fallbacks... */ + if (dir == 0 || dir == 1) { /* horz */ + x = screen_area(self->desktop)->x + + screen_area(self->desktop)->width / 4; + w = screen_area(self->desktop)->width / 2; + } + if (dir == 0 || dir == 2) { /* vert */ + y = screen_area(self->desktop)->y + + screen_area(self->desktop)->height / 4; + h = screen_area(self->desktop)->height / 2; + } - if (PROP_GET32A(self->window, openbox_premax, cardinal, - dimensions, 4)) { - if (dir == 0 || dir == 1) { /* horz */ - x = dimensions[0]; - w = dimensions[2]; - } - if (dir == 0 || dir == 2) { /* vert */ - y = dimensions[1]; - h = dimensions[3]; - } - g_free(dimensions); - } else { - /* pick some fallbacks... */ - if (dir == 0 || dir == 1) { /* horz */ - x = screen_area(self->desktop)->x + - screen_area(self->desktop)->width / 4; - w = screen_area(self->desktop)->width / 2; - } - if (dir == 0 || dir == 2) { /* vert */ - y = screen_area(self->desktop)->y + - screen_area(self->desktop)->height / 4; - h = screen_area(self->desktop)->height / 2; - } + if (PROP_GETA32(self->window, openbox_premax, cardinal, + (guint32**)&dimensions, &num)) { + if (num == 4) { + if (dir == 0 || dir == 1) { /* horz */ + x = dimensions[0]; + w = dimensions[2]; + } + if (dir == 0 || dir == 2) { /* vert */ + y = dimensions[1]; + h = dimensions[3]; + } + } + g_free(dimensions); } } @@ -1826,7 +2007,7 @@ void client_kill(Client *self) void client_set_desktop(Client *self, guint target, gboolean donthide) { - guint old, i; + guint old; if (target == self->desktop) return; @@ -1834,6 +2015,9 @@ void client_set_desktop(Client *self, guint target, gboolean donthide) g_assert(target < screen_num_desktops || target == DESKTOP_ALL); + /* remove from the old desktop(s) */ + focus_order_remove(self); + old = self->desktop; self->desktop = target; PROP_SET32(self->window, net_wm_desktop, cardinal, target); @@ -1844,51 +2028,31 @@ void client_set_desktop(Client *self, guint target, gboolean donthide) client_showhide(self); /* raise if it was not already on the desktop */ if (old != DESKTOP_ALL) - stacking_raise(self); + stacking_raise(CLIENT_AS_WINDOW(self)); screen_update_struts(); - /* update the focus lists */ - if (old == DESKTOP_ALL) { - for (i = 0; i < screen_num_desktops; ++i) - focus_order[i] = g_list_remove(focus_order[i], self); - } else - focus_order[old] = g_list_remove(focus_order[old], self); - if (target == DESKTOP_ALL) { - for (i = 0; i < screen_num_desktops; ++i) { - if (config_focus_new) - focus_order[i] = g_list_prepend(focus_order[i], self); - else - focus_order[i] = g_list_append(focus_order[i], self); - } - } else { - if (config_focus_new) - focus_order[target] = g_list_prepend(focus_order[target], self); - else - focus_order[target] = g_list_append(focus_order[target], self); - } + /* add to the new desktop(s) */ + if (config_focus_new) + focus_order_to_top(self); + else + focus_order_to_bottom(self); dispatch_client(Event_Client_Desktop, self, target, old); } -static Client *search_modal_tree(Client *node, Client *skip) +Client *client_search_modal_child(Client *self) { GSList *it; Client *ret; - for (it = node->transients; it != NULL; it = it->next) { + for (it = self->transients; it != NULL; it = it->next) { Client *c = it->data; - if (c == skip) continue; /* circular? */ - if ((ret = search_modal_tree(c, skip))) return ret; + if ((ret = client_search_modal_child(c))) return ret; if (c->modal) return c; } return NULL; } -Client *client_find_modal_child(Client *self) -{ - return search_modal_tree(self, self); -} - gboolean client_validate(Client *self) { XEvent e; @@ -2047,19 +2211,11 @@ Client *client_focus_target(Client *self) Client *child; /* if we have a modal child, then focus it, not us */ - child = client_find_modal_child(self); + child = client_search_modal_child(self); if (child) return child; return self; } -gboolean client_focusable(Client *self) -{ - /* won't try focus if the client doesn't want it, or if the window isn't - visible on the screen */ - return self->frame->visible && - (self->can_focus || self->focus_notify); -} - gboolean client_focus(Client *self) { XEvent ev; @@ -2067,7 +2223,16 @@ gboolean client_focus(Client *self) /* choose the correct target */ self = client_focus_target(self); - if (!client_focusable(self)) + if (self->desktop != DESKTOP_ALL && self->desktop != screen_desktop) { + /* update the focus lists */ + focus_order_to_top(self); + return FALSE; + } + + if (!((self->can_focus || self->focus_notify) && + (self->desktop == screen_desktop || + self->desktop == DESKTOP_ALL) && + !self->iconic)) return FALSE; /* do a check to see if the window has already been unmapped or destroyed @@ -2089,7 +2254,7 @@ gboolean client_focus(Client *self) } if (self->can_focus) - /* RevertToPointerRoot causes much more headache than TevertToNone, so + /* RevertToPointerRoot causes much more headache than RevertToNone, so I choose to use it always, hopefully to find errors quicker, if any are left. (I hate X. I hate focus events.) */ XSetInputFocus(ob_display, self->window, RevertToPointerRoot, @@ -2111,7 +2276,9 @@ gboolean client_focus(Client *self) } #ifdef DEBUG_FOCUS - g_message("focusing %lx", self->window); + g_message("%sively focusing %lx at %d", (self->can_focus ? "act" : "pass"), + self->window, (int) + event_lasttime); #endif /* Cause the FocusIn to come back to us. Important for desktop switches, @@ -2124,12 +2291,28 @@ gboolean client_focus(Client *self) void client_unfocus(Client *self) { g_assert(focus_client == self); -#ifndef DEBUG_FOCUS - g_message("client_unfocus"); +#ifdef DEBUG_FOCUS + g_message("client_unfocus for %lx", self->window); #endif focus_fallback(Fallback_Unfocusing); } +void client_activate(Client *self) +{ + if (client_normal(self) && screen_showing_desktop) + screen_show_desktop(FALSE); + if (self->iconic) + client_iconify(self, FALSE, TRUE); + else if (!self->frame->visible) + /* if its not visible for other reasons, then don't mess + with it */ + return; + if (self->shaded) + client_shade(self, FALSE); + client_focus(self); + stacking_raise(CLIENT_AS_WINDOW(self)); +} + gboolean client_focused(Client *self) { return self == focus_client;