From: Dana Jansens Date: Mon, 12 Mar 2007 02:24:40 +0000 (+0000) Subject: Rewrite the stacking code. It's a lot faster now, I should think. It's def a more... X-Git-Url: https://git.brokenzipper.com/gitweb?a=commitdiff_plain;h=d33f44cd86c1474a2c84376509691869aca7bd9f;p=chaz%2Fopenbox Rewrite the stacking code. It's a lot faster now, I should think. It's def a more clever algorithm. It deals with group transients much better. On that note, utility and menu and toolbar window types are now treated as group transients in terms of stacking and focus and such. --- diff --git a/openbox/client.c b/openbox/client.c index c59baf96..37c7f40b 100644 --- a/openbox/client.c +++ b/openbox/client.c @@ -1117,9 +1117,15 @@ void client_update_transient_for(ObClient *self) } } } - } else if (self->type == OB_CLIENT_TYPE_DIALOG && self->group) { - self->transient = TRUE; - target = OB_TRAN_GROUP; + } else if (self->group) { + if (self->type == OB_CLIENT_TYPE_DIALOG || + self->type == OB_CLIENT_TYPE_TOOLBAR || + self->type == OB_CLIENT_TYPE_MENU || + self->type == OB_CLIENT_TYPE_UTILITY) + { + self->transient = TRUE; + target = OB_TRAN_GROUP; + } } else self->transient = FALSE; @@ -2024,7 +2030,7 @@ void client_calc_layer(ObClient *self) orig = self; /* transients take on the layer of their parents */ - it = client_search_top_transients(self); + it = client_search_all_top_parents(self); for (; it; it = g_slist_next(it)) client_calc_layer_recursive(it->data, orig, 0, FALSE); @@ -2449,21 +2455,18 @@ static void client_iconify_recursive(ObClient *self, screen_update_areas(); } - /* iconify all transients */ + /* iconify all direct transients */ for (it = self->transients; it; it = g_slist_next(it)) - if (it->data != self) client_iconify_recursive(it->data, - iconic, curdesk); + if (it->data != self) + if (client_is_direct_child(self, it->data)) + client_iconify_recursive(it->data, iconic, curdesk); } void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk) { - GSList *it; - /* move up the transient chain as far as possible first */ - it = client_search_top_transients(self); - - for (; it; it = g_slist_next(it)) - client_iconify_recursive(it->data, iconic, curdesk); + self = client_search_top_parent(self); + client_iconify_recursive(self, iconic, curdesk); } void client_maximize(ObClient *self, gboolean max, gint dir, gboolean savearea) @@ -2657,18 +2660,23 @@ void client_set_desktop_recursive(ObClient *self, /* move all transients */ for (it = self->transients; it; it = g_slist_next(it)) - if (it->data != self) client_set_desktop_recursive(it->data, - target, donthide); + if (it->data != self) + if (client_is_direct_child(self, it->data)) + client_set_desktop_recursive(it->data, target, donthide); } void client_set_desktop(ObClient *self, guint target, gboolean donthide) { - GSList *it; - - it = client_search_top_transients(self); + self = client_search_top_parent(self); + client_set_desktop_recursive(self, target, donthide); +} - for(; it; it = g_slist_next(it)) - client_set_desktop_recursive(it->data, target, donthide); +gboolean client_is_direct_child(ObClient *parent, ObClient *child) +{ + while (child != parent && + child->transient_for && child->transient_for != OB_TRAN_GROUP) + child = child->transient_for; + return child == parent; } ObClient *client_search_modal_child(ObClient *self) @@ -3236,7 +3244,14 @@ guint client_monitor(ObClient *self) return screen_find_monitor(&self->frame->area); } -GSList *client_search_top_transients(ObClient *self) +ObClient *client_search_top_parent(ObClient *self) +{ + while (self->transient_for && self->transient_for != OB_TRAN_GROUP) + self = self->transient_for; + return self; +} + +GSList *client_search_all_top_parents(ObClient *self) { GSList *ret = NULL; diff --git a/openbox/client.h b/openbox/client.h index b418964d..ff460153 100644 --- a/openbox/client.h +++ b/openbox/client.h @@ -575,7 +575,15 @@ ObClient *client_search_modal_child(ObClient *self); It will only contain more than 1 element if the client is transient for its group. */ -GSList *client_search_top_transients(ObClient *self); +GSList *client_search_all_top_parents(ObClient *self); + +/*! Returns a window's top level parent. This only counts direct parents, + not groups if it is transient for its group. +*/ +ObClient *client_search_top_parent(ObClient *self); + +/*! Is one client a direct child of another (i.e. not through the group.) */ +gboolean client_is_direct_child(ObClient *parent, ObClient *child); /*! Search for a parent of a client. This only searches up *ONE LEVEL*, and returns the searched for parent if it is a parent, or NULL if not. */ diff --git a/openbox/stacking.c b/openbox/stacking.c index 34064db7..db1606b5 100644 --- a/openbox/stacking.c +++ b/openbox/stacking.c @@ -158,180 +158,182 @@ static void do_lower(GList *wins) } } -static GList *pick_windows_recur(ObClient *top, ObClient *selected, - gboolean raise) +static void restack_windows(ObClient *selected, gboolean raise, gboolean group) { - GList *ret = NULL; - GList *it, *next, *prev; - GSList *sit; - gint i, n; + GList *it, *last, *below, *above, *next; + GList *wins = NULL; + + GList *group_modals = NULL; + GList *group_trans = NULL; GList *modals = NULL; GList *trans = NULL; - GList *modal_sel = NULL; /* the selected guys if modal */ - GList *trans_sel = NULL; /* the selected guys if not */ - - /* remove first so we can't run into ourself */ - if ((it = g_list_find(stacking_list, top))) - stacking_list = g_list_delete_link(stacking_list, it); - else - return NULL; - i = 0; - n = g_slist_length(top->transients); - for (it = stacking_list; i < n && it; it = next) { - prev = g_list_previous(it); - next = g_list_next(it); - - if ((sit = g_slist_find(top->transients, it->data))) { - ObClient *c = sit->data; - gboolean sel_child; - - ++i; + if (!raise && selected->transient_for) { + GSList *top, *top_it; + GSList *top_reorder = NULL; + + /* if it's a transient lowering, lower its parents so that we can lower + this window, or it won't move */ + top = client_search_all_top_parents(selected); + + /* go thru stacking list backwards so we can use g_slist_prepend */ + for (it = g_list_last(stacking_list); it && top; + it = g_list_previous(it)) + if ((top_it = g_slist_find(top, it->data))) { + top_reorder = g_slist_prepend(top_reorder, top_it->data); + top = g_slist_delete_link(top, top_it); + } + g_assert(top == NULL); - if (c == selected) - sel_child = TRUE; - else - sel_child = client_search_transient(c, selected) != NULL; + /* call restack for each of these to lower them */ + for (top_it = top_reorder; top_it; top_it = g_slist_next(top_it)) + restack_windows(top_it->data, raise, group); + return; + } - if (!c->modal) { - if (!sel_child) { - trans = g_list_concat - (trans, pick_windows_recur(c, selected, raise)); - } else { - trans_sel = g_list_concat - (trans_sel, pick_windows_recur(c, selected, raise)); + /* remove first so we can't run into ourself */ + it = g_list_find(stacking_list, selected); + g_assert(it); + stacking_list = g_list_delete_link(stacking_list, it); + + /* go from the bottom of the stacking list up */ + for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) { + if (WINDOW_IS_CLIENT(it->data)) { + ObClient *ch = it->data; + + /* only move windows in the same stacking layer */ + if (ch->layer == selected->layer && + client_search_transient(selected, ch)) + { + if (client_is_direct_child(selected, ch)) { + if (ch->modal) + modals = g_list_prepend(modals, ch); + else + trans = g_list_prepend(trans, ch); } - } else { - if (!sel_child) { - modals = g_list_concat - (modals, pick_windows_recur(c, selected, raise)); - } else { - modal_sel = g_list_concat - (modal_sel, pick_windows_recur(c, selected, raise)); + else { + if (ch->modal) + group_modals = g_list_prepend(group_modals, ch); + else + group_trans = g_list_prepend(group_trans, ch); } + stacking_list = g_list_delete_link(stacking_list, it); } - /* if we dont have a prev then start back at the beginning, - otherwise skip back to the prev's next */ - next = prev ? g_list_next(prev) : stacking_list; } } - ret = g_list_concat((raise ? modal_sel : modals), - (raise ? modals : modal_sel)); - - ret = g_list_concat(ret, (raise ? trans_sel : trans)); - ret = g_list_concat(ret, (raise ? trans : trans_sel)); + /* put transients of the selected window right above it */ + wins = g_list_concat(modals, trans); + wins = g_list_append(wins, selected); + /* if selected window is transient for group then raise it above others */ + if (selected->transient_for == OB_TRAN_GROUP) { + /* if it's modal, raise it above those also */ + if (selected->modal) { + wins = g_list_concat(wins, group_modals); + group_modals = NULL; + } + wins = g_list_concat(wins, group_trans); + group_trans = NULL; + } - /* add itself */ - ret = g_list_append(ret, top); - - return ret; -} + /* find where to put the selected window, start from bottom of list, + this is the window below everything we are re-adding to the list */ + last = NULL; + for (it = g_list_last(stacking_list); it; it = g_list_previous(it)) + { + if (window_layer(it->data) < selected->layer) + continue; + /* if lowering, stop at the beginning of the layer */ + if (!raise) + break; + /* if raising, stop at the end of the layer */ + if (window_layer(it->data) > selected->layer) + break; + + last = it; + } -static GList *pick_group_windows_recur(ObClient *top, ObClient *selected, - gboolean raise, gboolean normal) -{ - GList *ret = NULL; - GList *it, *next, *prev; - GSList *sit; - gint i, n; - - /* add group members in their stacking order */ - if (top->group) { - i = 0; - n = g_slist_length(top->group->members) - 1; - for (it = stacking_list; i < n && it; it = next) { - prev = g_list_previous(it); - next = g_list_next(it); - - if ((sit = g_slist_find(top->group->members, it->data))) { - ObClient *c; - ObClientType t; - - ++i; - c = it->data; - t = c->type; - - if ((c->desktop == selected->desktop || - c->desktop == DESKTOP_ALL) && - (t == OB_CLIENT_TYPE_TOOLBAR || - t == OB_CLIENT_TYPE_MENU || - t == OB_CLIENT_TYPE_UTILITY || - (normal && t == OB_CLIENT_TYPE_NORMAL))) - { - ret = g_list_concat(ret, - pick_windows_recur(sit->data, - selected, raise)); - /* if we dont have a prev then start back at the beginning, - otherwise skip back to the prev's next */ - next = prev ? g_list_next(prev) : stacking_list; - } - } + /* save this position in the stacking list */ + below = last; + + /* find where to put the group transients, start from the top of list */ + for (it = stacking_list; it; it = g_list_next(it)) { + /* skip past higher layers */ + if (window_layer(it->data) > selected->layer) + continue; + /* if we reach the end of the layer (how?) then don't go further */ + if (window_layer(it->data) < selected->layer) + break; + /* stop when we reach the first window in the group */ + if (WINDOW_IS_CLIENT(it->data)) { + ObClient *c = it->data; + if (c->group == selected->group) + break; } + /* if we don't hit any other group members, stop here because this + is where we are putting the selected window (and its children) */ + if (it == below) + break; } - return ret; -} -static GList *pick_windows(ObClient *selected, gboolean raise, gboolean group) -{ - GList *it; - GSList *top, *top_it; - GSList *top_reorder = NULL; - GList *ret = NULL; - - top = client_search_top_transients(selected); - - /* go thru stacking list backwords so we can use g_slist_prepend */ - for (it = g_list_last(stacking_list); it && top; - it = g_list_previous(it)) - if ((top_it = g_slist_find(top, it->data))) { - top_reorder = g_slist_prepend(top_reorder, top_it->data); - top = g_slist_delete_link(top, top_it); - } - g_assert(top == NULL); + /* save this position, this is the top of the group of windows between the + group transient ones we're restacking and the others up above that we're + restacking - for (top_it = top_reorder; top_it; top_it = g_slist_next(top_it)) - ret = g_list_concat(ret, - pick_windows_recur(top_it->data, selected, raise)); + we actually want to save 1 position _above_ that, for for loops to work + nicely, so move back one position in the list while saving it + */ + above = it ? g_list_previous(it) : g_list_last(stacking_list); + + /* put the windows inside the gap to the other windows we're stacking + into the restacking list, go from the bottom up so that we can use + g_list_prepend */ + if (below) it = g_list_previous(below); + else it = g_list_last(stacking_list); + for (; it != above; it = next) { + next = g_list_previous(it); + wins = g_list_prepend(wins, it->data); + stacking_list = g_list_delete_link(stacking_list, it); + } + + /* group transients go above the rest of the stuff acquired to now */ + wins = g_list_concat(group_trans, wins); + /* group modals go on the very top */ + wins = g_list_concat(group_modals, wins); - for (top_it = top_reorder; top_it; top_it = g_slist_next(top_it)) - ret = g_list_concat(ret, - pick_group_windows_recur(top_it->data, - selected, raise, group)); - return ret; + do_restack(wins, below); + g_list_free(wins); } void stacking_raise(ObWindow *window, gboolean group) { - GList *wins; - if (WINDOW_IS_CLIENT(window)) { ObClient *selected; selected = WINDOW_AS_CLIENT(window); - wins = pick_windows(selected, TRUE, group); + restack_windows(selected, TRUE, group); } else { + GList *wins; wins = g_list_append(NULL, window); stacking_list = g_list_remove(stacking_list, window); + do_raise(wins); + g_list_free(wins); } - do_raise(wins); - g_list_free(wins); } void stacking_lower(ObWindow *window, gboolean group) { - GList *wins; - if (WINDOW_IS_CLIENT(window)) { ObClient *selected; selected = WINDOW_AS_CLIENT(window); - wins = pick_windows(selected, FALSE, group); + restack_windows(selected, FALSE, group); } else { + GList *wins; wins = g_list_append(NULL, window); stacking_list = g_list_remove(stacking_list, window); + do_lower(wins); + g_list_free(wins); } - do_lower(wins); - g_list_free(wins); } void stacking_below(ObWindow *window, ObWindow *below)