From 04bc846ef366964505abbb016c383becfb5fca88 Mon Sep 17 00:00:00 2001 From: "Owen W. Taylor" Date: Wed, 10 Sep 2014 11:58:42 -0400 Subject: [PATCH] Move logic for syncing the stack to the X server into MetaStackTracker stack.c:sync_stack_to_xserver had both code for assembling the desired stack, and code for enforcing the desired stack on the actual stack of X and Wayland windows; the latter part is properly the domain of stack-tracker.c; moving the code to apply the stack there both simplifies it and keeps stack.c more manageable. https://bugzilla.gnome.org/show_bug.cgi?id=736559 --- src/core/stack-tracker.c | 107 +++++++++++++--- src/core/stack-tracker.h | 6 +- src/core/stack.c | 260 ++------------------------------------- 3 files changed, 108 insertions(+), 265 deletions(-) diff --git a/src/core/stack-tracker.c b/src/core/stack-tracker.c index 4ef4026e0..b328d5ef3 100644 --- a/src/core/stack-tracker.c +++ b/src/core/stack-tracker.c @@ -816,9 +816,6 @@ meta_stack_tracker_get_stack (MetaStackTracker *tracker, stack = tracker->predicted_stack; } - meta_topic (META_DEBUG_STACK, "Get Stack\n"); - meta_stack_tracker_dump (tracker); - if (windows) *windows = (guint64 *)stack->data; if (n_windows) @@ -1044,18 +1041,100 @@ meta_stack_tracker_raise_above (MetaStackTracker *tracker, } void -meta_stack_tracker_restack_windows (MetaStackTracker *tracker, - const guint64 *windows, - int n_windows) +meta_stack_tracker_restack_managed (MetaStackTracker *tracker, + const guint64 *managed, + int n_managed) { - int i; + guint64 *windows; + int n_windows; + int old_pos, new_pos; - /* XRestackWindows() isn't actually a X requests - it's broken down - * by XLib into a series of XConfigureWindow(StackMode=below); we - * just do the same here directly. The main disadvantage of is that - * we allocate individual ops for each lower, and also that we are - * grabbing the libX11 lock separately for individual component. + if (n_managed == 0) + return; + + meta_stack_tracker_get_stack (tracker, &windows, &n_windows); + + /* If the top window has to be restacked, we don't want to move it to the very + * top of the stack, since apps expect override-redirect windows to stay near + * the top of the X stack; we instead move it above all managed windows (or + * above the guard window if there are no non-hidden managed windows.) */ - for (i = 0; i < n_windows - 1; i++) - meta_stack_tracker_lower_below (tracker, windows[i + 1], windows[i]); + old_pos = n_windows - 1; + for (old_pos = n_windows - 1; old_pos >= 0; old_pos--) + { + MetaWindow *old_window = meta_display_lookup_stack_id (tracker->screen->display, windows[old_pos]); + if ((old_window && !old_window->override_redirect) || windows[old_pos] == tracker->screen->guard_window) + break; + } + g_assert (old_pos >= 0); + + new_pos = n_managed - 1; + if (managed[new_pos] != windows[old_pos]) + { + /* Move the first managed window in the new stack above all managed windows */ + meta_stack_tracker_raise_above (tracker, managed[new_pos], windows[old_pos]); + meta_stack_tracker_get_stack (tracker, &windows, &n_windows); + /* Moving managed[new_pos] above windows[old_pos], moves the window at old_pos down by one */ + } + + old_pos--; + new_pos--; + + while (old_pos >= 0 && new_pos >= 0) + { + if (windows[old_pos] == tracker->screen->guard_window) + break; + + if (windows[old_pos] == managed[new_pos]) + { + old_pos--; + new_pos--; + continue; + } + + MetaWindow *old_window = meta_display_lookup_stack_id (tracker->screen->display, windows[old_pos]); + if (!old_window || old_window->override_redirect) + { + old_pos--; + continue; + } + + meta_stack_tracker_lower_below (tracker, managed[new_pos], managed[new_pos + 1]); + meta_stack_tracker_get_stack (tracker, &windows, &n_windows); + /* Moving managed[new_pos] above windows[old_pos] moves the window at old_pos down by one, + * we'll examine it again to see if it matches the next new window */ + old_pos--; + new_pos--; + } + + while (new_pos >= 0) + { + meta_stack_tracker_lower_below (tracker, managed[new_pos], managed[new_pos - 1]); + new_pos--; + } +} + +void +meta_stack_tracker_restack_at_bottom (MetaStackTracker *tracker, + const guint64 *new_order, + int n_new_order) +{ + guint64 *windows; + int n_windows; + int pos; + + meta_stack_tracker_get_stack (tracker, &windows, &n_windows); + + for (pos = 0; pos < n_new_order; pos++) + { + if (pos >= n_windows || windows[pos] != new_order[pos]) + { + if (pos == 0) + meta_stack_tracker_lower (tracker, new_order[pos]); + else + meta_stack_tracker_raise_above (tracker, new_order[pos], new_order[pos - 1]); + + meta_stack_tracker_get_stack (tracker, &windows, &n_windows); + } + } } diff --git a/src/core/stack-tracker.h b/src/core/stack-tracker.h index 1c9ee8fc2..e787feb15 100644 --- a/src/core/stack-tracker.h +++ b/src/core/stack-tracker.h @@ -73,9 +73,13 @@ void meta_stack_tracker_lower_below (MetaStackTracker *tracker, guint64 sibling); void meta_stack_tracker_lower (MetaStackTracker *tracker, guint64 window); -void meta_stack_tracker_restack_windows (MetaStackTracker *tracker, + +void meta_stack_tracker_restack_managed (MetaStackTracker *tracker, const guint64 *windows, int n_windows); +void meta_stack_tracker_restack_at_bottom (MetaStackTracker *tracker, + const guint64 *new_order, + int n_new_order); /* These functions are used to update the stack when we get events * reflecting changes to the stacking order */ diff --git a/src/core/stack.c b/src/core/stack.c index e4f66a51f..a9c80b6f6 100644 --- a/src/core/stack.c +++ b/src/core/stack.c @@ -101,7 +101,7 @@ void meta_stack_add (MetaStack *stack, MetaWindow *window) { - g_return_if_fail (window->override_redirect); + g_return_if_fail (!window->override_redirect); meta_topic (META_DEBUG_STACK, "Adding window %s to the stack\n", window->desc); @@ -1020,109 +1020,6 @@ stack_ensure_sorted (MetaStack *stack) stack_do_resort (stack); } -static guint64 -find_top_most_managed_window (MetaScreen *screen, - guint64 ignore) -{ - MetaStackTracker *stack_tracker = screen->stack_tracker; - guint64 *windows; - int n_windows; - int i; - - meta_stack_tracker_get_stack (stack_tracker, - &windows, &n_windows); - - /* Children are in order from bottom to top. We want to - * find the topmost managed child, then configure - * our window to be above it. - */ - for (i = n_windows -1; i >= 0; i--) - { - guint64 other_window = windows[i]; - - if (other_window == ignore) - { - /* Do nothing. This means we're already the topmost managed - * window, but it DOES NOT mean we are already just above - * the topmost managed window. This is important because if - * an override redirect window is up, and we map a new - * managed window, the new window is probably above the old - * popup by default, and we want to push it below that - * popup. So keep looking for a sibling managed window - * to be moved below. - */ - } - else - { - if (META_STACK_ID_IS_X11 (other_window)) - { - MetaWindow *other = meta_display_lookup_x_window (screen->display, - other_window); - - if (other != NULL && !other->override_redirect) - return other_window; - } - else - { - /* All wayland windows are currently considered "managed" - * TODO: consider wayland pop-up windows like override - * redirect windows here. */ - return other_window; - } - } - } - - return 0; -} - -/** - * raise_window_relative_to_managed_windows: - * - * This function is used to avoid raising a window above popup - * menus and other such things. - * - * The key to the operation of this function is that we are expecting - * at most one window to be added at a time. If xwindow is newly added, - * then its own stack position will be too high (the frame window - * is created at the top of the stack), but if we ignore xwindow, - * then the *next* managed window in the stack will be a window that - * we've already stacked. - * - * We could generalize this and remove the assumption that windows - * are added one at a time by keeping an explicit ->stacked flag in - * MetaWindow. - * - * An alternate approach would be to reverse the stacking algorithm to - * work by placing each window above the others, and start by lowering - * a window to the bottom (instead of the current way, which works by - * placing each window below another and starting with a raise) - */ -static void -raise_window_relative_to_managed_windows (MetaScreen *screen, - guint64 stack_id) -{ - guint64 sibling; - - sibling = find_top_most_managed_window (screen, stack_id); - if (!sibling) - { - /* No sibling to use, just lower ourselves to the bottom - * to be sure we're below any override redirect windows. - */ - meta_stack_tracker_lower (screen->stack_tracker, stack_id); - return; - } - - /* window is the topmost managed child */ - meta_topic (META_DEBUG_STACK, - "Moving %s above topmost managed child window %s\n", - meta_display_describe_stack_id (screen->display, stack_id), - meta_display_describe_stack_id (screen->display, sibling)); - - meta_stack_tracker_raise_above (screen->stack_tracker, - stack_id, sibling); -} - /** * stack_sync_to_server: * @@ -1152,10 +1049,7 @@ stack_sync_to_xserver (MetaStack *stack) stack_ensure_sorted (stack); - /* Create stacked xwindow arrays. - * Painfully, "stacked" is in bottom-to-top order for the - * _NET hints, and "root_children_stacked" is in top-to-bottom - * order for XRestackWindows() + /* Create stacked xwindow arrays, in bottom-to-top order */ x11_stacked = g_array_new (FALSE, FALSE, sizeof (Window)); @@ -1169,7 +1063,7 @@ stack_sync_to_xserver (MetaStack *stack) meta_topic (META_DEBUG_STACK, "Top to bottom: "); meta_push_no_msg_prefix (); - for (tmp = stack->sorted; tmp != NULL; tmp = tmp->next) + for (tmp = g_list_last(stack->sorted); tmp != NULL; tmp = tmp->prev) { MetaWindow *w = tmp->data; Window top_level_window; @@ -1178,8 +1072,7 @@ stack_sync_to_xserver (MetaStack *stack) meta_topic (META_DEBUG_STACK, "%u:%d - %s ", w->layer, w->stack_position, w->desc); - /* remember, stacked is in reverse order (bottom to top) */ - g_array_prepend_val (x11_stacked, w->xwindow); + g_array_append_val (x11_stacked, w->xwindow); if (w->frame) top_level_window = w->frame->xwindow; @@ -1216,143 +1109,12 @@ stack_sync_to_xserver (MetaStack *stack) meta_topic (META_DEBUG_STACK, "Restacking %u windows\n", all_root_children_stacked->len); - meta_error_trap_push (stack->screen->display); - - if (all_root_children_stacked->len > 0) - { - /* Try to do minimal window moves to get the stack in order */ - /* A point of note: these arrays include frames not client windows, - * so if a client window has changed frame since last_root_children_stacked - * was saved, then we may have inefficiency, but I don't think things - * break... - */ - guint64 *old_stack; - int old_len; - meta_stack_tracker_get_stack (stack->screen->stack_tracker, - &old_stack, &old_len); - - const guint64 *new_stack = (guint64 *) all_root_children_stacked->data; - const int new_len = all_root_children_stacked->len; - - const guint64 *oldp = old_stack; - const guint64 *newp = new_stack; - const guint64 *old_end = old_stack + old_len; - const guint64 *new_end = new_stack + new_len; - Window last_xwindow = None; - guint64 last_window = 0; - - while (oldp != old_end && - newp != new_end) - { - if (*oldp == *newp) - { - /* Stacks are the same here, move on */ - ++oldp; - if (META_STACK_ID_IS_X11 (*newp)) - last_xwindow = (Window)*newp; - last_window = *newp; - ++newp; - - continue; - } - - MetaWindow *old_window = meta_display_lookup_stack_id (stack->screen->display, - *oldp); - - if (old_window == NULL || old_window->override_redirect) - { - /* *oldp is not known to us (probably unmanaged), or override_redirect, so - * we should skip it. - */ - ++oldp; - } - else - { - /* Move *newp below the last_window */ - if (!last_window) - { - meta_topic (META_DEBUG_STACK, "Using window %s as topmost (but leaving it in-place)\n", - meta_display_describe_stack_id (stack->screen->display, *newp)); - - raise_window_relative_to_managed_windows (stack->screen, *newp); - } - else if (META_STACK_ID_IS_X11 (*newp) && - last_xwindow == None) - { - /* In this case we have an X window that we need to - * put below a wayland window and this is the - * topmost X window. */ - - /* In X terms (because this is the topmost X window) - * we want to - * raise_window_relative_to_managed_windows() to - * ensure the X window is below override-redirect - * pop-up windows. - * - * In Wayland terms we just want to ensure - * newp is lowered below last_window (which - * notably doesn't require an X request because we - * know last_window isn't an X window). - */ - - raise_window_relative_to_managed_windows (stack->screen, *newp); - - meta_stack_tracker_record_lower_below (stack->screen->stack_tracker, - *newp, last_window, - 0); /* no x request serial */ - } - else - { - /* This means that if last_xwindow is dead, but not - * *newp, then we fail to restack *newp; but on - * unmanaging last_xwindow, we'll fix it up. - */ - - meta_topic (META_DEBUG_STACK, "Placing window %s below 0x%lx\n", - meta_display_describe_stack_id (stack->screen->display, *newp), - last_xwindow); - - meta_stack_tracker_lower_below (stack->screen->stack_tracker, - *newp, last_window); - } - - if (META_STACK_ID_IS_X11 (*newp)) - last_xwindow = *newp; - last_window = *newp; - ++newp; - } - } - - if (newp != new_end) - { - /* Restack remaining windows */ - meta_topic (META_DEBUG_STACK, "Restacking remaining %d windows\n", - (int) (new_end - newp)); - - /* We need to include an already-stacked window - * in the restack call, so we get in the proper position - * with respect to it. - */ - if (newp != new_stack) - --newp; - meta_stack_tracker_restack_windows (stack->screen->stack_tracker, - newp, new_end - newp); - } - } - - /* Push hidden X windows to the bottom of the stack under the guard window */ - meta_stack_tracker_lower (stack->screen->stack_tracker, - stack->screen->guard_window); - meta_stack_tracker_restack_windows (stack->screen->stack_tracker, - (guint64 *)x11_hidden_stack_ids->data, - x11_hidden_stack_ids->len); - g_array_free (x11_hidden_stack_ids, TRUE); - - meta_error_trap_pop (stack->screen->display); - /* on error, a window was destroyed; it should eventually - * get removed from the stacking list when we unmanage it - * and we'll fix stacking at that time. - */ + meta_stack_tracker_restack_managed (stack->screen->stack_tracker, + (guint64 *)all_root_children_stacked->data, + all_root_children_stacked->len); + meta_stack_tracker_restack_at_bottom (stack->screen->stack_tracker, + (guint64 *)x11_hidden_stack_ids->data, + x11_hidden_stack_ids->len); /* Sync _NET_CLIENT_LIST and _NET_CLIENT_LIST_STACKING */ @@ -1373,8 +1135,6 @@ stack_sync_to_xserver (MetaStack *stack) g_array_free (x11_stacked, TRUE); g_array_free (all_root_children_stacked, TRUE); - - /* That was scary... */ } MetaWindow*