stacking has been made more reliable with groups and group transients.
authorDana Jansens <danakj@orodu.net>
Sun, 11 Mar 2007 16:58:19 +0000 (16:58 +0000)
committerDana Jansens <danakj@orodu.net>
Sun, 11 Mar 2007 16:58:19 +0000 (16:58 +0000)
this was a pretty invasive change in client.c though, so it may break things?

it did expose some bugginess in client_calc_layer, which is now better than ever, hopefully there isn't more to be found.

openbox/client.c
openbox/client.h
openbox/focus.c
openbox/stacking.c

index c32a463..5bc7984 100644 (file)
@@ -301,6 +301,8 @@ void client_manage(Window window)
     client_get_all(self);
     client_restore_session_state(self);
 
+    client_calc_layer(self);
+
     {
         Time t = sn_app_started(self->startup_id, self->class);
         if (t) self->user_time = t;
@@ -461,7 +463,8 @@ void client_manage(Window window)
         /* This is focus stealing prevention, if a user_time has been set */
         ob_debug("Want to focus new window 0x%x with time %u (last time %u)\n",
                  self->window, self->user_time, client_last_user_time);
-        if (!self->user_time || self->user_time >= client_last_user_time)
+        if (!self->user_time || self->user_time >= client_last_user_time ||
+            client_search_focus_parent(self) != NULL)
         {
             /* since focus can change the stacking orders, if we focus the
                window then the standard raise it gets is not enough, we need
@@ -1901,8 +1904,6 @@ static void client_change_state(ObClient *self)
         netstate[num++] = prop_atoms.ob_wm_state_undecorated;
     PROP_SETA32(self->window, net_wm_state, atom, netstate, num);
 
-    client_calc_layer(self);
-
     if (self->frame)
         frame_adjust_state(self->frame);
 }
@@ -1968,20 +1969,23 @@ static ObStackingLayer calc_layer(ObClient *self)
 }
 
 static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
-                                        ObStackingLayer l, gboolean raised)
+                                        ObStackingLayer min, gboolean raised)
 {
     ObStackingLayer old, own;
     GSList *it;
 
     old = self->layer;
     own = calc_layer(self);
-    self->layer = l > own ? l : own;
+    self->layer = MAX(own, min);
+
+    ob_debug("layer for %s: %d\n", self->title, self->layer);
 
     for (it = self->transients; it; it = g_slist_next(it))
         client_calc_layer_recursive(it->data, orig,
-                                    l, raised ? raised : l != old);
+                                    self->layer,
+                                    raised ? raised : self->layer != old);
 
-    if (!raised && l != old)
+    if (!raised && self->layer != old)
         if (orig->frame) { /* only restack if the original window is managed */
             stacking_remove(CLIENT_AS_WINDOW(self));
             stacking_add(CLIENT_AS_WINDOW(self));
@@ -1990,17 +1994,16 @@ static void client_calc_layer_recursive(ObClient *self, ObClient *orig,
 
 void client_calc_layer(ObClient *self)
 {
-    ObStackingLayer l;
     ObClient *orig;
+    GSList *it;
 
     orig = self;
 
     /* transients take on the layer of their parents */
-    self = client_search_top_transient(self);
-
-    l = calc_layer(self);
+    it = client_search_top_transients(self);
 
-    client_calc_layer_recursive(self, orig, l, FALSE);
+    for (; it; it = g_slist_next(it))
+        client_calc_layer_recursive(it->data, orig, 0, FALSE);
 }
 
 gboolean client_should_show(ObClient *self)
@@ -2323,8 +2326,8 @@ void client_fullscreen(ObClient *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,
-                                  and adjust out layer/stacking */
+    client_change_state(self); /* change the state hints on the client */
+    client_calc_layer(self);   /* and adjust out layer/stacking */
 
     if (fs) {
         if (savearea)
@@ -2427,11 +2430,13 @@ static void client_iconify_recursive(ObClient *self,
 
 void client_iconify(ObClient *self, gboolean iconic, gboolean curdesk)
 {
+    GSList *it;
+
     /* move up the transient chain as far as possible first */
-    self = client_search_top_transient(self);
+    it = client_search_top_transients(self);
 
-    client_iconify_recursive(client_search_top_transient(self),
-                             iconic, curdesk);
+    for (; it; it = g_slist_next(it))
+    client_iconify_recursive(it->data, iconic, curdesk);
 }
 
 void client_maximize(ObClient *self, gboolean max, gint dir, gboolean savearea)
@@ -2631,8 +2636,12 @@ void client_set_desktop_recursive(ObClient *self,
 
 void client_set_desktop(ObClient *self, guint target, gboolean donthide)
 {
-    client_set_desktop_recursive(client_search_top_transient(self),
-                                 target, donthide);
+    GSList *it;
+
+    it = client_search_top_transients(self);
+
+    for(; it; it = g_slist_next(it))
+        client_set_desktop_recursive(it->data, target, donthide);
 }
 
 ObClient *client_search_modal_child(ObClient *self)
@@ -2839,16 +2848,14 @@ void client_set_state(ObClient *self, Atom action, glong data1, glong data2)
     if (demands_attention != self->demands_attention)
         client_hilite(self, demands_attention);
 
-    client_calc_layer(self);
     client_change_state(self); /* change the hint to reflect these changes */
 }
 
 ObClient *client_focus_target(ObClient *self)
 {
-    ObClient *child;
-     
-    /* if we have a modal child, then focus it, not us */
-    child = client_search_modal_child(client_search_top_transient(self));
+    ObClient *child = NULL;
+
+    child = client_search_modal_child(self);
     if (child) return child;
     return self;
 }
@@ -3224,13 +3231,17 @@ guint client_monitor(ObClient *self)
     return most;
 }
 
-ObClient *client_search_top_transient(ObClient *self)
+GSList *client_search_top_transients(ObClient *self)
 {
-    /* move up the transient chain as far as possible */
-    if (self->transient_for) {
-        if (self->transient_for != OB_TRAN_GROUP) {
-            return client_search_top_transient(self->transient_for);
-        } else {
+    GSList *ret = NULL;
+
+    /* move up the direct transient chain as far as possible */
+    while (self->transient_for && self->transient_for != OB_TRAN_GROUP)
+        self = self->transient_for;
+
+    if (!self->transient_for)
+        ret = g_slist_prepend(ret, self);
+    else {
             GSList *it;
 
             g_assert(self->group);
@@ -3238,16 +3249,15 @@ ObClient *client_search_top_transient(ObClient *self)
             for (it = self->group->members; it; it = g_slist_next(it)) {
                 ObClient *c = it->data;
 
-                /* checking transient_for prevents infinate loops! */
-                if (c != self && !c->transient_for)
-                    break;
+                if (!c->transient_for)
+                    ret = g_slist_prepend(ret, c);
             }
-            if (it)
-                return it->data;
-        }
+
+            if (ret == NULL) /* no group parents */
+                ret = g_slist_prepend(ret, self);
     }
 
-    return self;
+    return ret;
 }
 
 ObClient *client_search_focus_parent(ObClient *self)
index 043a6de..b418964 100644 (file)
@@ -571,7 +571,11 @@ ObClient *client_search_focus_tree_full(ObClient *self);
 */
 ObClient *client_search_modal_child(ObClient *self);
 
-ObClient *client_search_top_transient(ObClient *self);
+/*! Returns a list of top-level windows which this is a transient for.
+  It will only contain more than 1 element if the client is transient for its
+  group.
+*/
+GSList *client_search_top_transients(ObClient *self);
 
 /*! 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. */
index 2d6804b..73cb6f5 100644 (file)
@@ -260,11 +260,15 @@ ObClient* focus_fallback_target(ObFocusFallbackType type)
             if (!config_focus_follow || config_focus_last)
                 trans = TRUE;
             else {
-                if ((target = client_under_pointer()) &&
-                    client_search_transient
-                    (client_search_top_transient(target), old))
-                {
-                    trans = TRUE;
+                if ((target = client_under_pointer())) {
+                    GSList *sit;
+
+                    sit = client_search_top_transients(target);
+                    for (; sit; sit = g_slist_next(sit))
+                        if (client_search_transient(sit->data, old)) {
+                            trans = TRUE;
+                            break;
+                        }
                 }
             }
 
index ff5a943..34064db 100644 (file)
@@ -158,7 +158,8 @@ static void do_lower(GList *wins)
     }
 }
 
-static GList *pick_windows(ObClient *top, ObClient *selected, gboolean raise)
+static GList *pick_windows_recur(ObClient *top, ObClient *selected,
+                                 gboolean raise)
 {
     GList *ret = NULL;
     GList *it, *next, *prev;
@@ -194,21 +195,19 @@ static GList *pick_windows(ObClient *top, ObClient *selected, gboolean raise)
 
             if (!c->modal) {
                 if (!sel_child) {
-                    trans = g_list_concat(trans,
-                                          pick_windows(c, selected, raise));
+                    trans = g_list_concat
+                        (trans, pick_windows_recur(c, selected, raise));
                 } else {
-                    trans_sel = g_list_concat(trans_sel,
-                                                 pick_windows(c, selected,
-                                                              raise));
+                    trans_sel = g_list_concat
+                        (trans_sel, pick_windows_recur(c, selected, raise));
                 }
             } else {
                 if (!sel_child) {
-                    modals = g_list_concat(modals,
-                                           pick_windows(c, selected, raise));
+                    modals = g_list_concat
+                        (modals, pick_windows_recur(c, selected, raise));
                 } else {
-                    modal_sel = g_list_concat(modal_sel,
-                                                 pick_windows(c, selected,
-                                                              raise));
+                    modal_sel = g_list_concat
+                        (modal_sel, pick_windows_recur(c, selected, raise));
                 }
             }
             /* if we dont have a prev then start back at the beginning,
@@ -230,8 +229,8 @@ static GList *pick_windows(ObClient *top, ObClient *selected, gboolean raise)
     return ret;
 }
 
-static GList *pick_group_windows(ObClient *top, ObClient *selected,
-                                 gboolean raise, gboolean normal)
+static GList *pick_group_windows_recur(ObClient *top, ObClient *selected,
+                                       gboolean raise, gboolean normal)
 {
     GList *ret = NULL;
     GList *it, *next, *prev;
@@ -262,8 +261,8 @@ static GList *pick_group_windows(ObClient *top, ObClient *selected,
                      (normal && t == OB_CLIENT_TYPE_NORMAL)))
                 {
                     ret = g_list_concat(ret,
-                                        pick_windows(sit->data,
-                                                     selected, raise)); 
+                                        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;
@@ -274,18 +273,43 @@ static GList *pick_group_windows(ObClient *top, ObClient *selected,
     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);
+
+    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));
+
+    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;
+}
+
 void stacking_raise(ObWindow *window, gboolean group)
 {
     GList *wins;
 
     if (WINDOW_IS_CLIENT(window)) {
-        ObClient *c;
         ObClient *selected;
         selected = WINDOW_AS_CLIENT(window);
-        c = client_search_top_transient(selected);
-        wins = pick_windows(c, selected, TRUE);
-        wins = g_list_concat(wins,
-                             pick_group_windows(c, selected, TRUE, group));
+        wins = pick_windows(selected, TRUE, group);
     } else {
         wins = g_list_append(NULL, window);
         stacking_list = g_list_remove(stacking_list, window);
@@ -299,13 +323,9 @@ void stacking_lower(ObWindow *window, gboolean group)
     GList *wins;
 
     if (WINDOW_IS_CLIENT(window)) {
-        ObClient *c;
         ObClient *selected;
         selected = WINDOW_AS_CLIENT(window);
-        c = client_search_top_transient(selected);
-        wins = pick_windows(c, selected, FALSE);
-        wins = g_list_concat(pick_group_windows(c, selected, FALSE, group),
-                             wins);
+        wins = pick_windows(selected, FALSE, group);
     } else {
         wins = g_list_append(NULL, window);
         stacking_list = g_list_remove(stacking_list, window);
@@ -341,7 +361,7 @@ void stacking_add_nonintrusive(ObWindow *win)
 {
     ObClient *client;
     ObClient *parent = NULL;
-    GList *it_before = NULL;
+    GList *it_below = NULL;
 
     if (!WINDOW_IS_CLIENT(win)) {
         stacking_add(win); /* no special rules for others */
@@ -373,29 +393,38 @@ void stacking_add_nonintrusive(ObWindow *win)
         }
     }
 
-    if (!(it_before = g_list_find(stacking_list, parent))) {
+    if (!(it_below = g_list_find(stacking_list, parent))) {
         /* no parent to put above, try find the focused client to go
            under */
         if (focus_client && focus_client->layer == client->layer) {
-            if ((it_before = g_list_find(stacking_list, focus_client)))
-                it_before = it_before->next;
+            if ((it_below = g_list_find(stacking_list, focus_client)))
+                it_below = it_below->next;
         }
     }
-    if (!it_before) {
+    if (!it_below) {
         /* out of ideas, just add it normally... */
         stacking_add(win);
     } else {
-        GList *it;
-
         /* make sure it's not in the wrong layer though ! */
-        while (it_before && client->layer < ((ObClient*)it_before->data)->layer)
-            it_before = g_list_next(it_before);
-        while (it_before != stacking_list &&
-               client->layer > ((ObClient*)g_list_previous(it_before)->data)->layer)
-            it_before = g_list_previous(it_before);
+        for (; it_below; it_below = g_list_next(it_below))
+        {
+            /* stop when the window is not in a higher layer than the window
+               it is going above (it_below) */
+            if (client->layer >= window_layer(it_below->data))
+                break;
+        }
+        for (; it_below != stacking_list;
+             it_below = g_list_previous(it_below))
+        {
+            /* stop when the window is not in a lower layer than the
+               window it is going under (it_above) */
+            GList *it_above = g_list_previous(it_below);
+            if (client->layer <= window_layer(it_above->data))
+                break;
+        }
 
         GList *wins = g_list_append(NULL, win);
-        do_restack(wins, it_before);
+        do_restack(wins, it_below);
         g_list_free(wins);
     }
 }