diff options
Diffstat (limited to 'src/core/stack-tracker.c')
-rw-r--r-- | src/core/stack-tracker.c | 1293 |
1 files changed, 0 insertions, 1293 deletions
diff --git a/src/core/stack-tracker.c b/src/core/stack-tracker.c deleted file mode 100644 index fbfef6465..000000000 --- a/src/core/stack-tracker.c +++ /dev/null @@ -1,1293 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/* - * SECTION:stack-tracker - * @short_description: Track stacking order for compositor - * - * #MetaStackTracker maintains the most accurate view we have at a - * given point of time of the ordering of the children of the root - * window (including override-redirect windows.) This is used to order - * the windows when the compositor draws them. - * - * By contrast, #MetaStack is responsible for keeping track of how we - * think that windows *should* be ordered. For windows we manage - * (non-override-redirect windows), the two stacking orders will be - * the same. - */ - -/* - * Copyright (C) 2009 Red Hat, Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation; either version 2 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, see <http://www.gnu.org/licenses/>. - */ - -#include "config.h" - -#include "core/stack-tracker.h" - -#include <string.h> - -#include "core/display-private.h" -#include "core/frame.h" -#include "meta/compositor.h" -#include "meta/meta-x11-errors.h" -#include "meta/util.h" -#include "x11/meta-x11-display-private.h" - -/* The complexity here comes from resolving two competing factors: - * - * - We need to have a view of the stacking order that takes into - * account everything we have done without waiting for events - * back from the X server; we don't want to draw intermediate - * partially-stacked stack states just because we haven't received - * some notification yet. - * - * - Only the X server has an accurate view of the complete stacking; - * when we make a request to restack windows, we don't know how - * it will affect override-redirect windows, because at any point - * applications may restack these windows without our involvement. - * - * The technique we use is that we keep three sets of information: - * - * - The stacking order on the server as known from the last - * event we received. - * - A queue of stacking requests that *we* made subsequent to - * that last event. - * - A predicted stacking order, derived from applying the queued - * requests to the last state from the server. - * - * When we receive a new event: a) we compare the serial in the event to - * the serial of the queued requests and remove any that are now - * no longer pending b) if necessary, drop the predicted stacking - * order to recompute it at the next opportunity. - * - * Possible optimizations: - * Keep the stacks as an array + reverse-mapping hash table to avoid - * linear lookups. - * Keep the stacks as a GList + reverse-mapping hash table to avoid - * linear lookups and to make restacking constant-time. - */ - -typedef union _MetaStackOp MetaStackOp; - -typedef enum -{ - STACK_OP_ADD, - STACK_OP_REMOVE, - STACK_OP_RAISE_ABOVE, - STACK_OP_LOWER_BELOW -} MetaStackOpType; - -typedef enum -{ - APPLY_DEFAULT = 0, - /* Only do restacking that we can do locally without changing - * the order of X windows. After we've received any stack - * events from the X server, we apply the locally cached - * ops in this mode to handle the non-X parts */ - NO_RESTACK_X_WINDOWS = 1 << 0, - /* If the stacking operation wouldn't change the order of X - * windows, ignore it. We use this when applying events received - * from X so that a spontaneous ConfigureNotify (for a move, say) - * doesn't change the stacking of X windows with respect to - * Wayland windows. */ - IGNORE_NOOP_X_RESTACK = 1 << 1 -} ApplyFlags; - -/* MetaStackOp represents a "stacking operation" - a change to - * apply to a window stack. Depending on the context, it could - * either reflect a request we have sent to the server, or a - * notification event we received from the X server. - */ -union _MetaStackOp -{ - struct { - MetaStackOpType type; - gulong serial; - guint64 window; - } any; - struct { - MetaStackOpType type; - gulong serial; - guint64 window; - } add; - struct { - MetaStackOpType type; - gulong serial; - guint64 window; - } remove; - struct { - MetaStackOpType type; - gulong serial; - guint64 window; - guint64 sibling; - } raise_above; - struct { - MetaStackOpType type; - gulong serial; - guint64 window; - guint64 sibling; - } lower_below; -}; - -struct _MetaStackTracker -{ - MetaDisplay *display; - - /* This is the serial of the last request we made that was reflected - * in xserver_stack - */ - gulong xserver_serial; - - /* A combined stack containing X and Wayland windows but without - * any unverified operations applied. */ - GArray *verified_stack; - - /* This is a queue of requests we've made to change the stacking order, - * where we haven't yet gotten a reply back from the server. - */ - GQueue *unverified_predictions; - - /* This is how we think the stack is, based on verified_stack, and - * on the unverified_predictions we've made subsequent to - * verified_stack. - */ - GArray *predicted_stack; - - /* Idle function used to sync the compositor's view of the window - * stack up with our best guess before a frame is drawn. - */ - guint sync_stack_later; -}; - -static void -meta_stack_tracker_keep_override_redirect_on_top (MetaStackTracker *tracker); - -static inline const char * -get_window_desc (MetaStackTracker *tracker, - guint64 window) -{ - return meta_display_describe_stack_id (tracker->display, window); -} - -static void -meta_stack_op_dump (MetaStackTracker *tracker, - MetaStackOp *op, - const char *prefix, - const char *suffix) -{ -#ifdef WITH_VERBOSE_MODE - const char *window_desc = get_window_desc (tracker, op->any.window); -#endif - - switch (op->any.type) - { - case STACK_OP_ADD: - meta_topic (META_DEBUG_STACK, "%sADD(%s; %ld)%s", - prefix, window_desc, op->any.serial, suffix); - break; - case STACK_OP_REMOVE: - meta_topic (META_DEBUG_STACK, "%sREMOVE(%s; %ld)%s", - prefix, window_desc, op->any.serial, suffix); - break; - case STACK_OP_RAISE_ABOVE: - { - meta_topic (META_DEBUG_STACK, "%sRAISE_ABOVE(%s, %s; %ld)%s", - prefix, - window_desc, - get_window_desc (tracker, op->raise_above.sibling), - op->any.serial, - suffix); - break; - } - case STACK_OP_LOWER_BELOW: - { - meta_topic (META_DEBUG_STACK, "%sLOWER_BELOW(%s, %s; %ld)%s", - prefix, - window_desc, - get_window_desc (tracker, op->lower_below.sibling), - op->any.serial, - suffix); - break; - } - } -} - -#ifdef WITH_VERBOSE_MODE -static void -stack_dump (MetaStackTracker *tracker, - GArray *stack) -{ - guint i; - - meta_push_no_msg_prefix (); - for (i = 0; i < stack->len; i++) - { - guint64 window = g_array_index (stack, guint64, i); - meta_topic (META_DEBUG_STACK, " %s", get_window_desc (tracker, window)); - } - meta_pop_no_msg_prefix (); -} -#endif /* WITH_VERBOSE_MODE */ - -static void -meta_stack_tracker_dump (MetaStackTracker *tracker) -{ -#ifdef WITH_VERBOSE_MODE - GList *l; - - meta_topic (META_DEBUG_STACK, "MetaStackTracker state"); - meta_push_no_msg_prefix (); - meta_topic (META_DEBUG_STACK, " xserver_serial: %ld", tracker->xserver_serial); - meta_topic (META_DEBUG_STACK, " verified_stack: "); - stack_dump (tracker, tracker->verified_stack); - meta_topic (META_DEBUG_STACK, " unverified_predictions: ["); - for (l = tracker->unverified_predictions->head; l; l = l->next) - { - MetaStackOp *op = l->data; - meta_stack_op_dump (tracker, op, "", l->next ? ", " : ""); - } - meta_topic (META_DEBUG_STACK, "]"); - if (tracker->predicted_stack) - { - meta_topic (META_DEBUG_STACK, " predicted_stack: "); - stack_dump (tracker, tracker->predicted_stack); - } - meta_pop_no_msg_prefix (); -#endif /* WITH_VERBOSE_MODE */ -} - -static void -meta_stack_op_free (MetaStackOp *op) -{ - g_free (op); -} - -static int -find_window (GArray *window_stack, - guint64 window) -{ - guint i; - - for (i = 0; i < window_stack->len; i++) - { - guint64 current = g_array_index (window_stack, guint64, i); - if (current == window) - return i; - } - - return -1; -} - -/* Returns TRUE if stack was changed */ -static gboolean -move_window_above (GArray *stack, - guint64 window, - int old_pos, - int above_pos, - ApplyFlags apply_flags) -{ - int i; - gboolean can_restack_this_window = - (apply_flags & NO_RESTACK_X_WINDOWS) == 0 || !META_STACK_ID_IS_X11 (window); - - if (old_pos < above_pos) - { - if ((apply_flags & IGNORE_NOOP_X_RESTACK) != 0) - { - gboolean found_x_window = FALSE; - for (i = old_pos + 1; i <= above_pos; i++) - if (META_STACK_ID_IS_X11 (g_array_index (stack, guint64, i))) - found_x_window = TRUE; - - if (!found_x_window) - return FALSE; - } - - for (i = old_pos; i < above_pos; i++) - { - if (!can_restack_this_window && - META_STACK_ID_IS_X11 (g_array_index (stack, guint64, i + 1))) - break; - - g_array_index (stack, guint64, i) = - g_array_index (stack, guint64, i + 1); - } - - g_array_index (stack, guint64, i) = window; - - return i != old_pos; - } - else if (old_pos > above_pos + 1) - { - if ((apply_flags & IGNORE_NOOP_X_RESTACK) != 0) - { - gboolean found_x_window = FALSE; - for (i = above_pos + 1; i < old_pos; i++) - if (META_STACK_ID_IS_X11 (g_array_index (stack, guint64, i))) - found_x_window = TRUE; - - if (!found_x_window) - return FALSE; - } - - for (i = old_pos; i > above_pos + 1; i--) - { - if (!can_restack_this_window && - META_STACK_ID_IS_X11 (g_array_index (stack, guint64, i - 1))) - break; - - g_array_index (stack, guint64, i) = - g_array_index (stack, guint64, i - 1); - } - - g_array_index (stack, guint64, i) = window; - - return i != old_pos; - } - else - return FALSE; -} - -/* Returns TRUE if stack was changed */ -static gboolean -meta_stack_op_apply (MetaStackTracker *tracker, - MetaStackOp *op, - GArray *stack, - ApplyFlags apply_flags) -{ - switch (op->any.type) - { - case STACK_OP_ADD: - { - int old_pos; - - if (META_STACK_ID_IS_X11 (op->add.window) && - (apply_flags & NO_RESTACK_X_WINDOWS) != 0) - return FALSE; - - old_pos = find_window (stack, op->add.window); - if (old_pos >= 0) - { - meta_topic (META_DEBUG_STACK, - "STACK_OP_ADD: window %s already in stack", - get_window_desc (tracker, op->add.window)); - return FALSE; - } - - g_array_append_val (stack, op->add.window); - return TRUE; - } - case STACK_OP_REMOVE: - { - int old_pos; - - if (META_STACK_ID_IS_X11 (op->remove.window) && - (apply_flags & NO_RESTACK_X_WINDOWS) != 0) - return FALSE; - - old_pos = find_window (stack, op->remove.window); - if (old_pos < 0) - { - meta_topic (META_DEBUG_STACK, - "STACK_OP_REMOVE: window %s not in stack", - get_window_desc (tracker, op->remove.window)); - return FALSE; - } - - g_array_remove_index (stack, old_pos); - return TRUE; - } - case STACK_OP_RAISE_ABOVE: - { - int old_pos; - int above_pos; - - old_pos = find_window (stack, op->raise_above.window); - if (old_pos < 0) - { - meta_topic (META_DEBUG_STACK, - "STACK_OP_RAISE_ABOVE: window %s not in stack", - get_window_desc (tracker, op->raise_above.window)); - return FALSE; - } - - if (op->raise_above.sibling) - { - above_pos = find_window (stack, op->raise_above.sibling); - if (above_pos < 0) - { - meta_topic (META_DEBUG_STACK, - "STACK_OP_RAISE_ABOVE: sibling window %s not in stack", - get_window_desc (tracker, op->raise_above.sibling)); - return FALSE; - } - } - else - { - above_pos = -1; - } - - return move_window_above (stack, op->raise_above.window, old_pos, above_pos, - apply_flags); - } - case STACK_OP_LOWER_BELOW: - { - int old_pos; - int above_pos; - - old_pos = find_window (stack, op->raise_above.window); - if (old_pos < 0) - { - meta_topic (META_DEBUG_STACK, - "STACK_OP_LOWER_BELOW: window %s not in stack", - get_window_desc (tracker, op->lower_below.window)); - return FALSE; - } - - if (op->lower_below.sibling) - { - int below_pos; - - below_pos = find_window (stack, op->lower_below.sibling); - if (below_pos < 0) - { - meta_topic (META_DEBUG_STACK, - "STACK_OP_LOWER_BELOW: sibling window %s not in stack", - get_window_desc (tracker, op->lower_below.sibling)); - return FALSE; - } - - above_pos = below_pos - 1; - } - else - { - above_pos = stack->len - 1; - } - - return move_window_above (stack, op->lower_below.window, old_pos, above_pos, - apply_flags); - } - } - - g_assert_not_reached (); - return FALSE; -} - -static GArray * -copy_stack (GArray *stack) -{ - GArray *copy = g_array_sized_new (FALSE, FALSE, sizeof (guint64), stack->len); - - g_array_set_size (copy, stack->len); - - memcpy (copy->data, stack->data, sizeof (guint64) * stack->len); - - return copy; -} - -static void -query_xserver_stack (MetaDisplay *display, - MetaStackTracker *tracker) -{ - MetaX11Display *x11_display = display->x11_display; - Window ignored1, ignored2; - Window *children; - guint n_children; - guint i, old_len; - - tracker->xserver_serial = XNextRequest (x11_display->xdisplay); - - XQueryTree (x11_display->xdisplay, - x11_display->xroot, - &ignored1, &ignored2, &children, &n_children); - - old_len = tracker->verified_stack->len; - - g_array_set_size (tracker->verified_stack, old_len + n_children); - - for (i = 0; i < n_children; i++) - g_array_index (tracker->verified_stack, guint64, old_len + i) = children[i]; - - XFree (children); -} - -static void -drop_x11_windows (MetaDisplay *display, - MetaStackTracker *tracker) -{ - GArray *new_stack; - GList *l; - int i; - - tracker->xserver_serial = 0; - - new_stack = g_array_new (FALSE, FALSE, sizeof (guint64)); - - for (i = 0; i < tracker->verified_stack->len; i++) - { - guint64 window = g_array_index (tracker->verified_stack, guint64, i); - - if (!META_STACK_ID_IS_X11 (window)) - g_array_append_val (new_stack, window); - } - - g_array_unref (tracker->verified_stack); - tracker->verified_stack = new_stack; - l = tracker->unverified_predictions->head; - - while (l) - { - MetaStackOp *op = l->data; - GList *next = l->next; - - if (META_STACK_ID_IS_X11 (op->any.window)) - g_queue_remove (tracker->unverified_predictions, op); - - l = next; - } -} - -MetaStackTracker * -meta_stack_tracker_new (MetaDisplay *display) -{ - MetaStackTracker *tracker; - - tracker = g_new0 (MetaStackTracker, 1); - tracker->display = display; - - tracker->verified_stack = g_array_new (FALSE, FALSE, sizeof (guint64)); - tracker->unverified_predictions = g_queue_new (); - - g_signal_connect (display, - "x11-display-setup", - G_CALLBACK (query_xserver_stack), - tracker); - g_signal_connect (display, - "x11-display-closing", - G_CALLBACK (drop_x11_windows), - tracker); - - meta_stack_tracker_dump (tracker); - - return tracker; -} - -void -meta_stack_tracker_free (MetaStackTracker *tracker) -{ - if (tracker->sync_stack_later) - meta_later_remove (tracker->sync_stack_later); - - g_array_free (tracker->verified_stack, TRUE); - if (tracker->predicted_stack) - g_array_free (tracker->predicted_stack, TRUE); - - g_queue_foreach (tracker->unverified_predictions, (GFunc)meta_stack_op_free, NULL); - g_queue_free (tracker->unverified_predictions); - tracker->unverified_predictions = NULL; - - g_signal_handlers_disconnect_by_func (tracker->display, - (gpointer)query_xserver_stack, - tracker); - g_signal_handlers_disconnect_by_func (tracker->display, - drop_x11_windows, - tracker); - - g_free (tracker); -} - -static void -stack_tracker_apply_prediction (MetaStackTracker *tracker, - MetaStackOp *op) -{ - gboolean free_at_end = FALSE; - - /* If this operation doesn't involve restacking X windows then it's - * implicitly verified. We can apply it immediately unless there - * are outstanding X restacks that haven't yet been confirmed. - */ - if (op->any.serial == 0 && - tracker->unverified_predictions->length == 0) - { - if (meta_stack_op_apply (tracker, op, tracker->verified_stack, APPLY_DEFAULT)) - meta_stack_tracker_queue_sync_stack (tracker); - - free_at_end = TRUE; - } - else - { - meta_stack_op_dump (tracker, op, "Predicting: ", ""); - g_queue_push_tail (tracker->unverified_predictions, op); - } - - if (!tracker->predicted_stack || - meta_stack_op_apply (tracker, op, tracker->predicted_stack, APPLY_DEFAULT)) - meta_stack_tracker_queue_sync_stack (tracker); - - if (free_at_end) - meta_stack_op_free (op); - - meta_stack_tracker_dump (tracker); -} - -void -meta_stack_tracker_record_add (MetaStackTracker *tracker, - guint64 window, - gulong serial) -{ - MetaStackOp *op = g_new0 (MetaStackOp, 1); - - op->any.type = STACK_OP_ADD; - op->any.serial = serial; - op->any.window = window; - - stack_tracker_apply_prediction (tracker, op); -} - -void -meta_stack_tracker_record_remove (MetaStackTracker *tracker, - guint64 window, - gulong serial) -{ - MetaStackOp *op = g_new0 (MetaStackOp, 1); - - op->any.type = STACK_OP_REMOVE; - op->any.serial = serial; - op->any.window = window; - - stack_tracker_apply_prediction (tracker, op); -} - -static void -meta_stack_tracker_record_raise_above (MetaStackTracker *tracker, - guint64 window, - guint64 sibling, - gulong serial) -{ - MetaStackOp *op = g_new0 (MetaStackOp, 1); - - op->any.type = STACK_OP_RAISE_ABOVE; - op->any.serial = serial; - op->any.window = window; - op->raise_above.sibling = sibling; - - stack_tracker_apply_prediction (tracker, op); -} - -static void -meta_stack_tracker_record_lower_below (MetaStackTracker *tracker, - guint64 window, - guint64 sibling, - gulong serial) -{ - MetaStackOp *op = g_new0 (MetaStackOp, 1); - - op->any.type = STACK_OP_LOWER_BELOW; - op->any.serial = serial; - op->any.window = window; - op->lower_below.sibling = sibling; - - stack_tracker_apply_prediction (tracker, op); -} - -static void -stack_tracker_event_received (MetaStackTracker *tracker, - MetaStackOp *op) -{ - gboolean need_sync = FALSE; - - /* If the event is older than our initial query, then it's - * already included in our tree. Just ignore it. */ - if (op->any.serial < tracker->xserver_serial) - return; - - meta_stack_op_dump (tracker, op, "Stack op event received: ", ""); - - /* First we apply any operations that we have queued up that depended - * on X operations *older* than what we received .. those operations - * must have been ignored by the X server, so we just apply the - * operations we have as best as possible while not moving windows. - */ - while (tracker->unverified_predictions->head) - { - MetaStackOp *queued_op = tracker->unverified_predictions->head->data; - - if (queued_op->any.serial >= op->any.serial) - break; - - meta_stack_op_apply (tracker, queued_op, tracker->verified_stack, - NO_RESTACK_X_WINDOWS); - - g_queue_pop_head (tracker->unverified_predictions); - meta_stack_op_free (queued_op); - need_sync = TRUE; - } - - /* Then we apply the received event. If it's a spontaneous event - * based on stacking we didn't trigger, this is the only handling. If we - * triggered it, we do the X restacking here, and then any residual - * local-only Wayland stacking below. - */ - if (meta_stack_op_apply (tracker, op, tracker->verified_stack, - IGNORE_NOOP_X_RESTACK)) - need_sync = TRUE; - - /* What is left to process is the prediction corresponding to the event - * (if any), and then any subsequent Wayland-only events we can just - * go ahead and do now. - */ - while (tracker->unverified_predictions->head) - { - MetaStackOp *queued_op = tracker->unverified_predictions->head->data; - - if (queued_op->any.serial > op->any.serial) - break; - - meta_stack_op_apply (tracker, queued_op, tracker->verified_stack, - NO_RESTACK_X_WINDOWS); - - g_queue_pop_head (tracker->unverified_predictions); - meta_stack_op_free (queued_op); - need_sync = TRUE; - } - - if (need_sync) - { - if (tracker->predicted_stack) - { - g_array_free (tracker->predicted_stack, TRUE); - tracker->predicted_stack = NULL; - } - - meta_stack_tracker_queue_sync_stack (tracker); - } - - meta_stack_tracker_dump (tracker); -} - -void -meta_stack_tracker_create_event (MetaStackTracker *tracker, - XCreateWindowEvent *event) -{ - MetaStackOp op; - - op.any.type = STACK_OP_ADD; - op.any.serial = event->serial; - op.add.window = event->window; - - stack_tracker_event_received (tracker, &op); -} - -void -meta_stack_tracker_destroy_event (MetaStackTracker *tracker, - XDestroyWindowEvent *event) -{ - MetaStackOp op; - - op.any.type = STACK_OP_REMOVE; - op.any.serial = event->serial; - op.remove.window = event->window; - - stack_tracker_event_received (tracker, &op); -} - -void -meta_stack_tracker_reparent_event (MetaStackTracker *tracker, - XReparentEvent *event) -{ - if (event->parent == event->event) - { - MetaStackOp op; - - op.any.type = STACK_OP_ADD; - op.any.serial = event->serial; - op.add.window = event->window; - - stack_tracker_event_received (tracker, &op); - } - else - { - MetaStackOp op; - - op.any.type = STACK_OP_REMOVE; - op.any.serial = event->serial; - op.remove.window = event->window; - - stack_tracker_event_received (tracker, &op); - } -} - -void -meta_stack_tracker_configure_event (MetaStackTracker *tracker, - XConfigureEvent *event) -{ - MetaStackOp op; - - op.any.type = STACK_OP_RAISE_ABOVE; - op.any.serial = event->serial; - op.raise_above.window = event->window; - op.raise_above.sibling = event->above; - - stack_tracker_event_received (tracker, &op); -} - -static gboolean -meta_stack_tracker_is_guard_window (MetaStackTracker *tracker, - uint64_t stack_id) -{ - MetaX11Display *x11_display = tracker->display->x11_display; - - if (!x11_display) - return FALSE; - - return stack_id == x11_display->guard_window; -} - -/** - * meta_stack_tracker_get_stack: - * @tracker: a #MetaStackTracker - * @windows: location to store list of windows, or %NULL - * @n_windows: location to store count of windows, or %NULL - * - * @windows will contain the most current view we have of the stacking order - * of the children of the root window. The returned array contains - * everything: InputOnly windows, override-redirect windows, - * hidden windows, etc. Some of these will correspond to MetaWindow - * objects, others won't. - * - * Assuming that no other clients have made requests that change - * the stacking order since we last received a notification, the - * returned list of windows is exactly that you'd get as the - * children when calling XQueryTree() on the root window. - */ -void -meta_stack_tracker_get_stack (MetaStackTracker *tracker, - guint64 **windows, - int *n_windows) -{ - GArray *stack; - - if (tracker->unverified_predictions->length == 0) - { - stack = tracker->verified_stack; - } - else - { - if (tracker->predicted_stack == NULL) - { - GList *l; - - tracker->predicted_stack = copy_stack (tracker->verified_stack); - for (l = tracker->unverified_predictions->head; l; l = l->next) - { - MetaStackOp *op = l->data; - meta_stack_op_apply (tracker, op, tracker->predicted_stack, APPLY_DEFAULT); - } - } - - stack = tracker->predicted_stack; - } - - if (windows) - *windows = (guint64 *)stack->data; - if (n_windows) - *n_windows = stack->len; -} - -/** - * meta_stack_tracker_sync_stack: - * @tracker: a #MetaStackTracker - * - * Informs the compositor of the current stacking order of windows, - * based on the predicted view maintained by the #MetaStackTracker. - */ -void -meta_stack_tracker_sync_stack (MetaStackTracker *tracker) -{ - guint64 *windows; - GList *meta_windows; - int n_windows; - int i; - - if (tracker->sync_stack_later) - { - meta_later_remove (tracker->sync_stack_later); - tracker->sync_stack_later = 0; - } - - meta_stack_tracker_keep_override_redirect_on_top (tracker); - - meta_stack_tracker_get_stack (tracker, &windows, &n_windows); - - meta_windows = NULL; - for (i = 0; i < n_windows; i++) - { - guint64 window = windows[i]; - - if (META_STACK_ID_IS_X11 (window)) - { - MetaX11Display *x11_display = tracker->display->x11_display; - MetaWindow *meta_window = NULL; - - if (x11_display) - meta_window = meta_x11_display_lookup_x_window (x11_display, (Window) window); - - /* When mapping back from xwindow to MetaWindow we have to be a bit careful; - * children of the root could include unmapped windows created by toolkits - * for internal purposes, including ones that we have registered in our - * XID => window table. (Wine uses a toplevel for _NET_WM_USER_TIME_WINDOW; - * see window-prop.c:reload_net_wm_user_time_window() for registration.) - */ - if (meta_window && - ((Window)window == meta_window->xwindow || - (meta_window->frame && (Window)window == meta_window->frame->xwindow))) - meta_windows = g_list_prepend (meta_windows, meta_window); - } - else - meta_windows = g_list_prepend (meta_windows, - meta_display_lookup_stamp (tracker->display, window)); - } - - meta_compositor_sync_stack (tracker->display->compositor, - meta_windows); - g_list_free (meta_windows); - - meta_display_restacked (tracker->display); -} - -static gboolean -stack_tracker_sync_stack_later (gpointer data) -{ - meta_stack_tracker_sync_stack (data); - - return FALSE; -} - -/** - * meta_stack_tracker_queue_sync_stack: - * @tracker: a #MetaStackTracker - * - * Queue informing the compositor of the new stacking order before the - * next redraw. (See meta_stack_tracker_sync_stack()). This is called - * internally when the stack of X windows changes, but also needs be - * called directly when we an undecorated window is first shown or - * withdrawn since the compositor's stacking order (which contains only - * the windows that have a corresponding MetaWindow) will change without - * any change to the stacking order of the X windows, if we are creating - * or destroying MetaWindows. - */ -void -meta_stack_tracker_queue_sync_stack (MetaStackTracker *tracker) -{ - if (tracker->sync_stack_later == 0) - { - tracker->sync_stack_later = meta_later_add (META_LATER_SYNC_STACK, - stack_tracker_sync_stack_later, - tracker, NULL); - } -} - -/* When moving an X window we sometimes need an X based sibling. - * - * If the given sibling is X based this function returns it back - * otherwise it searches downwards looking for the nearest X window. - * - * If no X based sibling could be found return NULL. */ -static Window -find_x11_sibling_downwards (MetaStackTracker *tracker, - guint64 sibling) -{ - guint64 *windows; - int n_windows; - int i; - - if (META_STACK_ID_IS_X11 (sibling)) - return (Window)sibling; - - meta_stack_tracker_get_stack (tracker, - &windows, &n_windows); - - /* NB: Children are in order from bottom to top and we - * want to search downwards for the nearest X window. - */ - - for (i = n_windows - 1; i >= 0; i--) - if (windows[i] == sibling) - break; - - for (; i >= 0; i--) - { - if (META_STACK_ID_IS_X11 (windows[i])) - return (Window)windows[i]; - } - - return None; -} - -static Window -find_x11_sibling_upwards (MetaStackTracker *tracker, - guint64 sibling) -{ - guint64 *windows; - int n_windows; - int i; - - if (META_STACK_ID_IS_X11 (sibling)) - return (Window)sibling; - - meta_stack_tracker_get_stack (tracker, - &windows, &n_windows); - - for (i = 0; i < n_windows; i++) - if (windows[i] == sibling) - break; - - for (; i < n_windows; i++) - { - if (META_STACK_ID_IS_X11 (windows[i])) - return (Window)windows[i]; - } - - return None; -} - -static void -meta_stack_tracker_lower_below (MetaStackTracker *tracker, - guint64 window, - guint64 sibling) -{ - gulong serial = 0; - MetaX11Display *x11_display = tracker->display->x11_display; - - if (META_STACK_ID_IS_X11 (window)) - { - XWindowChanges changes; - changes.sibling = sibling ? find_x11_sibling_upwards (tracker, sibling) : None; - - if (changes.sibling != find_x11_sibling_upwards (tracker, window)) - { - serial = XNextRequest (x11_display->xdisplay); - - meta_x11_error_trap_push (x11_display); - - changes.stack_mode = changes.sibling ? Below : Above; - - XConfigureWindow (x11_display->xdisplay, - window, - (changes.sibling ? CWSibling : 0) | CWStackMode, - &changes); - - meta_x11_error_trap_pop (x11_display); - } - } - - meta_stack_tracker_record_lower_below (tracker, - window, sibling, - serial); -} - -static void -meta_stack_tracker_raise_above (MetaStackTracker *tracker, - guint64 window, - guint64 sibling) -{ - gulong serial = 0; - MetaX11Display *x11_display = tracker->display->x11_display; - - if (META_STACK_ID_IS_X11 (window)) - { - XWindowChanges changes; - changes.sibling = sibling ? find_x11_sibling_downwards (tracker, sibling) : None; - - if (changes.sibling != find_x11_sibling_downwards (tracker, window)) - { - serial = XNextRequest (x11_display->xdisplay); - - meta_x11_error_trap_push (x11_display); - - changes.stack_mode = changes.sibling ? Above : Below; - - XConfigureWindow (x11_display->xdisplay, - (Window)window, - (changes.sibling ? CWSibling : 0) | CWStackMode, - &changes); - - meta_x11_error_trap_pop (x11_display); - } - } - - meta_stack_tracker_record_raise_above (tracker, window, - sibling, serial); -} - -void -meta_stack_tracker_lower (MetaStackTracker *tracker, - guint64 window) -{ - meta_stack_tracker_raise_above (tracker, window, None); -} - -static void -meta_stack_tracker_keep_override_redirect_on_top (MetaStackTracker *tracker) -{ - guint64 *stack; - int n_windows, i; - int topmost_non_or; - - meta_stack_tracker_get_stack (tracker, &stack, &n_windows); - - for (i = n_windows - 1; i >= 0; i--) - { - MetaWindow *window; - - window = meta_display_lookup_stack_id (tracker->display, stack[i]); - if (window && window->layer != META_LAYER_OVERRIDE_REDIRECT) - break; - } - - topmost_non_or = i; - - for (i -= 1; i >= 0; i--) - { - MetaWindow *window; - - if (meta_stack_tracker_is_guard_window (tracker, stack[i])) - break; - - window = meta_display_lookup_stack_id (tracker->display, stack[i]); - if (window && window->layer == META_LAYER_OVERRIDE_REDIRECT) - { - meta_stack_tracker_raise_above (tracker, stack[i], stack[topmost_non_or]); - meta_stack_tracker_get_stack (tracker, &stack, &n_windows); - topmost_non_or -= 1; - } - } -} - -void -meta_stack_tracker_restack_managed (MetaStackTracker *tracker, - const guint64 *managed, - int n_managed) -{ - guint64 *windows; - int n_windows; - int old_pos, new_pos; - - COGL_TRACE_BEGIN_SCOPED (StackTrackerRestackManaged, - "StackTracker: Restack Managed"); - if (n_managed == 0) - return; - - COGL_TRACE_BEGIN (StackTrackerRestackManagedGet, - "StackTracker: Restack Managed (get)"); - 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.) - */ - 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->display, windows[old_pos]); - if ((old_window && !old_window->override_redirect && !old_window->unmanaging) || - meta_stack_tracker_is_guard_window (tracker, windows[old_pos])) - break; - } - g_assert (old_pos >= 0); - COGL_TRACE_END (StackTrackerRestackManagedGet); - - COGL_TRACE_BEGIN (StackTrackerRestackManagedRaise, - "StackTracker: Restack Managed (raise)"); - 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 */ - } - COGL_TRACE_END (StackTrackerRestackManagedRaise); - - old_pos--; - new_pos--; - - COGL_TRACE_BEGIN (StackTrackerRestackManagedRestack, - "StackTracker: Restack Managed (restack)"); - while (old_pos >= 0 && new_pos >= 0) - { - if (meta_stack_tracker_is_guard_window (tracker, windows[old_pos])) - break; - - if (windows[old_pos] == managed[new_pos]) - { - old_pos--; - new_pos--; - continue; - } - - MetaWindow *old_window = meta_display_lookup_stack_id (tracker->display, windows[old_pos]); - if (!old_window || old_window->override_redirect || old_window->unmanaging) - { - 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--; - } - COGL_TRACE_END (StackTrackerRestackManagedRestack); - - COGL_TRACE_BEGIN (StackTrackerRestackManagedLower, - "StackTracker: Restack Managed (lower)"); - while (new_pos > 0) - { - meta_stack_tracker_lower_below (tracker, managed[new_pos], managed[new_pos - 1]); - new_pos--; - } - COGL_TRACE_END (StackTrackerRestackManagedLower); -} - -void -meta_stack_tracker_restack_at_bottom (MetaStackTracker *tracker, - const guint64 *new_order, - int n_new_order) -{ - guint64 *windows; - int n_windows; - int pos; - - COGL_TRACE_BEGIN_SCOPED (StackTrackerRestackAtBottom, - "Stack tracker: Restack at bottom"); - 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); - } - } -} |