X-Git-Url: https://git.dogcows.com/gitweb?a=blobdiff_plain;f=openbox%2Fclient.c;h=6cd8454b062247b0186765e2b4e728e6e72be55e;hb=58cfbb7f8419e084af6b6b8b00c88ed270c29e88;hp=a473fdb187b1f2c3f2f0007b72a42efd28d07939;hpb=8dfa16abaed94350a901dea0969752d0692753fe;p=chaz%2Fopenbox diff --git a/openbox/client.c b/openbox/client.c index a473fdb1..6cd8454b 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -1,4 +1,6 @@ #include "client.h" +#include "slit.h" +#include "startup.h" #include "screen.h" #include "moveresize.h" #include "prop.h" @@ -26,9 +28,6 @@ GList *client_list = NULL; GHashTable *client_map = NULL; -static Window *client_startup_stack_order = NULL; -static guint 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); @@ -40,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; } @@ -53,11 +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_GETA32(ob_root, net_client_list_stacking, window, - (guint32**)&client_startup_stack_order, - &client_startup_stack_size); - client_set_list(); } @@ -89,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); @@ -130,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) @@ -151,9 +177,7 @@ void client_manage(Window window) XEvent e; XWindowAttributes attrib; XSetWindowAttributes attrib_set; -/* XWMHints *wmhint; */ - guint i; - Client *parent; + XWMHints *wmhint; grab_server(TRUE); @@ -174,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 */ @@ -198,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); @@ -218,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_insert(focus_order[i], self, 1); - } else { - i = self->desktop; - focus_order[i] = g_list_insert(focus_order[i], self, 1); - } + focus_order_add_new(self); - stacking_raise(self); + stacking_raise(CLIENT_AS_WINDOW(self)); screen_update_struts(); @@ -241,9 +260,21 @@ void client_manage(Window window) /* focus the new window? */ if (ob_state != State_Starting) { + Client *parent; + gboolean group_foc = FALSE; + parent = NULL; - if (self->transient_for) { + 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 */ parent = self->transient_for; } else { /* transient of a group */ @@ -258,9 +289,14 @@ void client_manage(Window window) /* 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) || + 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) || - search_focus_tree(parent, parent)))) { + client_search_focus_tree(parent)))) { client_focus(self); } } @@ -284,7 +320,6 @@ void client_unmanage_all() void client_unmanage(Client *self) { - guint i; int j; GSList *it; @@ -302,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 */ @@ -400,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; @@ -416,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); @@ -497,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; @@ -530,7 +560,6 @@ static void client_get_all(Client *self) client_setup_decor_and_functions(self); client_update_title(self); - client_update_icon_title(self); client_update_class(self); client_update_strut(self); client_update_icons(self); @@ -649,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; @@ -675,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 */ @@ -689,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 = @@ -767,10 +812,6 @@ void client_get_type(Client *self) else self->type = Type_Normal; } - - /* this makes sure that these windows appear on all desktops */ - if (self->type == Type_Desktop) - self->desktop = DESKTOP_ALL; } void client_update_protocols(Client *self) @@ -976,11 +1017,19 @@ void client_setup_decor_and_functions(Client *self) 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; } } @@ -989,7 +1038,9 @@ static void client_change_allowed_actions(Client *self) 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; @@ -1139,7 +1190,11 @@ void client_update_wmhints(Client *self) void client_update_title(Client *self) { + GList *it; + guint32 nums; + guint i; char *data = NULL; + gboolean read_title; g_free(self->title); @@ -1150,6 +1205,29 @@ void client_update_title(Client *self) data = g_strdup("Unnamed Window"); /* 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); @@ -1157,19 +1235,29 @@ void client_update_title(Client *self) if (self->frame) frame_adjust_title(self->frame); -} - -void client_update_icon_title(Client *self) -{ - char *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)) /* try old x stuff */ - if (!PROP_GETS(self->window, wm_icon_name, locale, &data)) - data = g_strdup("Unnamed Window"); + if (!PROP_GETS(self->window, wm_icon_name, locale, &data)) { + data = g_strdup(self->title); + read_title = FALSE; + } + + /* 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); @@ -1327,40 +1415,84 @@ 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; } -static void calc_recursive(Client *self, StackLayer l, gboolean raised) +Client *client_search_focus_tree_full(Client *self) +{ + 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); + } +} + +static StackLayer calc_layer(Client *self) { - StackLayer old; + 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; + } + else if (self->above) l = Layer_Above; + else if (self->below) l = Layer_Below; + else l = Layer_Normal; + + return l; +} + +static void calc_recursive(Client *self, Client *orig, StackLayer l, + gboolean raised) +{ + StackLayer old, own; GSList *it; old = self->layer; - self->layer = l; + own = calc_layer(self); + self->layer = l > own ? l : own; for (it = self->transients; it; it = it->next) - calc_recursive(it->data, l, raised ? raised : l != old); + calc_recursive(it->data, orig, l, raised ? raised : l != old); if (!raised && l != old) - if (self->frame) - stacking_raise(self); + 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; - gboolean f; + Client *orig; + + orig = self; /* transients take on the layer of their parents */ if (self->transient_for) { @@ -1378,27 +1510,9 @@ void client_calc_layer(Client *self) } } - /* is us or one of our transients focused? */ - if (client_focused(self)) - f = TRUE; - else if (search_focus_tree(self, self)) - f = TRUE; - else - f = FALSE; - - if (self->iconic) l = Layer_Icon; - /* fullscreen windows are only in the fullscreen layer while focused */ - else if (self->fullscreen && f) 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; - } - else if (self->above) l = Layer_Above; - else if (self->below) l = Layer_Below; - else l = Layer_Normal; + l = calc_layer(self); - calc_recursive(self, l, FALSE); + calc_recursive(self, orig, l, FALSE); } gboolean client_should_show(Client *self) @@ -1679,31 +1793,30 @@ void client_iconify(Client *self, gboolean iconic, gboolean curdesk) { GSList *it; - /* 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; - - /* 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) { - 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 */ @@ -1718,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); @@ -1886,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; @@ -1894,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); @@ -1904,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; @@ -2107,43 +2211,28 @@ 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; - guint i; /* choose the correct target */ self = client_focus_target(self); if (self->desktop != DESKTOP_ALL && self->desktop != screen_desktop) { /* 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); - focus_order[i] = g_list_prepend(focus_order[i], self); - } - } else { - i = self->desktop; - focus_order[i] = g_list_remove(focus_order[i], self); - focus_order[i] = g_list_prepend(focus_order[i], self); - } + focus_order_to_top(self); return FALSE; } - if (!client_focusable(self)) + 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 @@ -2208,6 +2297,22 @@ void client_unfocus(Client *self) 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;