diff options
Diffstat (limited to 'src/core/stack.c')
-rw-r--r-- | src/core/stack.c | 1661 |
1 files changed, 0 insertions, 1661 deletions
diff --git a/src/core/stack.c b/src/core/stack.c deleted file mode 100644 index cf770bca..00000000 --- a/src/core/stack.c +++ /dev/null @@ -1,1661 +0,0 @@ -/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ - -/** - * \file stack.c Which windows cover which other windows - */ - -/* - * Copyright (C) 2001 Havoc Pennington - * Copyright (C) 2002, 2003 Red Hat, Inc. - * Copyright (C) 2004 Rob Adams - * Copyright (C) 2004, 2005 Elijah Newren - * - * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - * 02111-1307, USA. - */ - -#include <config.h> -#include "stack.h" -#include "window-private.h" -#include "errors.h" -#include "frame-private.h" -#include "group.h" -#include "prefs.h" -#include "workspace.h" - -#include <X11/Xatom.h> - -#define WINDOW_HAS_TRANSIENT_TYPE(w) \ - (w->type == META_WINDOW_DIALOG || \ - w->type == META_WINDOW_MODAL_DIALOG || \ - w->type == META_WINDOW_TOOLBAR || \ - w->type == META_WINDOW_MENU || \ - w->type == META_WINDOW_UTILITY) - -#define WINDOW_TRANSIENT_FOR_WHOLE_GROUP(w) \ - ((w->xtransient_for == None || \ - w->transient_parent_is_root_window) && \ - WINDOW_HAS_TRANSIENT_TYPE (w)) - -#define WINDOW_IN_STACK(w) (w->stack_position >= 0) - -static void stack_sync_to_server (MetaStack *stack); -static void meta_window_set_stack_position_no_sync (MetaWindow *window, - int position); -static void stack_do_window_deletions (MetaStack *stack); -static void stack_do_window_additions (MetaStack *stack); -static void stack_do_relayer (MetaStack *stack); -static void stack_do_constrain (MetaStack *stack); -static void stack_do_resort (MetaStack *stack); - -static void stack_ensure_sorted (MetaStack *stack); - -MetaStack* -meta_stack_new (MetaScreen *screen) -{ - MetaStack *stack; - - stack = g_new (MetaStack, 1); - - stack->screen = screen; - stack->windows = g_array_new (FALSE, FALSE, sizeof (Window)); - - stack->sorted = NULL; - stack->added = NULL; - stack->removed = NULL; - - stack->freeze_count = 0; - stack->last_root_children_stacked = NULL; - - stack->n_positions = 0; - - stack->need_resort = FALSE; - stack->need_relayer = FALSE; - stack->need_constrain = FALSE; - - return stack; -} - -void -meta_stack_free (MetaStack *stack) -{ - g_array_free (stack->windows, TRUE); - - g_list_free (stack->sorted); - g_list_free (stack->added); - g_list_free (stack->removed); - - if (stack->last_root_children_stacked) - g_array_free (stack->last_root_children_stacked, TRUE); - - g_free (stack); -} - -void -meta_stack_add (MetaStack *stack, - MetaWindow *window) -{ - meta_topic (META_DEBUG_STACK, "Adding window %s to the stack\n", window->desc); - - if (window->stack_position >= 0) - meta_bug ("Window %s had stack position already\n", window->desc); - - stack->added = g_list_prepend (stack->added, window); - - window->stack_position = stack->n_positions; - stack->n_positions += 1; - meta_topic (META_DEBUG_STACK, - "Window %s has stack_position initialized to %d\n", - window->desc, window->stack_position); - - stack_sync_to_server (stack); -} - -void -meta_stack_remove (MetaStack *stack, - MetaWindow *window) -{ - meta_topic (META_DEBUG_STACK, "Removing window %s from the stack\n", window->desc); - - if (window->stack_position < 0) - meta_bug ("Window %s removed from stack but had no stack position\n", - window->desc); - - /* Set window to top position, so removing it will not leave gaps - * in the set of positions - */ - meta_window_set_stack_position_no_sync (window, - stack->n_positions - 1); - window->stack_position = -1; - stack->n_positions -= 1; - - /* We don't know if it's been moved from "added" to "stack" yet */ - stack->added = g_list_remove (stack->added, window); - stack->sorted = g_list_remove (stack->sorted, window); - - /* Remember the window ID to remove it from the stack array. - * The macro is safe to use: Window is guaranteed to be 32 bits, and - * GUINT_TO_POINTER says it only works on 32 bits. - */ - stack->removed = g_list_prepend (stack->removed, - GUINT_TO_POINTER (window->xwindow)); - if (window->frame) - stack->removed = g_list_prepend (stack->removed, - GUINT_TO_POINTER (window->frame->xwindow)); - - stack_sync_to_server (stack); -} - -void -meta_stack_update_layer (MetaStack *stack, - MetaWindow *window) -{ - stack->need_relayer = TRUE; - - stack_sync_to_server (stack); -} - -void -meta_stack_update_transient (MetaStack *stack, - MetaWindow *window) -{ - stack->need_constrain = TRUE; - - stack_sync_to_server (stack); -} - -/* raise/lower within a layer */ -void -meta_stack_raise (MetaStack *stack, - MetaWindow *window) -{ - meta_window_set_stack_position_no_sync (window, - stack->n_positions - 1); - - stack_sync_to_server (stack); -} - -void -meta_stack_lower (MetaStack *stack, - MetaWindow *window) -{ - meta_window_set_stack_position_no_sync (window, 0); - - stack_sync_to_server (stack); -} - -void -meta_stack_freeze (MetaStack *stack) -{ - stack->freeze_count += 1; -} - -void -meta_stack_thaw (MetaStack *stack) -{ - g_return_if_fail (stack->freeze_count > 0); - - stack->freeze_count -= 1; - stack_sync_to_server (stack); -} - -static gboolean -is_focused_foreach (MetaWindow *window, - void *data) -{ - if (window == window->display->expected_focus_window) - { - *((gboolean*) data) = TRUE; - return FALSE; - } - return TRUE; -} - -static gboolean -windows_on_different_xinerama (MetaWindow *a, - MetaWindow *b) -{ - if (a->screen != b->screen) - return TRUE; - - return meta_screen_get_xinerama_for_window (a->screen, a) != - meta_screen_get_xinerama_for_window (b->screen, b); -} - -/* Get layer ignoring any transient or group relationships */ -static MetaStackLayer -get_standalone_layer (MetaWindow *window) -{ - MetaStackLayer layer; - gboolean focused_transient = FALSE; - - switch (window->type) - { - case META_WINDOW_DESKTOP: - layer = META_LAYER_DESKTOP; - break; - - case META_WINDOW_DOCK: - /* still experimenting here */ - if (window->wm_state_below) - layer = META_LAYER_BOTTOM; - else - layer = META_LAYER_DOCK; - break; - - default: - meta_window_foreach_transient (window, - is_focused_foreach, - &focused_transient); - - if (window->wm_state_below) - layer = META_LAYER_BOTTOM; - else if (window->fullscreen && - (focused_transient || - window == window->display->expected_focus_window || - window->display->expected_focus_window == NULL || - (window->display->expected_focus_window != NULL && - windows_on_different_xinerama (window, - window->display->expected_focus_window)))) - layer = META_LAYER_FULLSCREEN; - else if (window->wm_state_above) - layer = META_LAYER_TOP; - else - layer = META_LAYER_NORMAL; - break; - } - - return layer; -} - -/* Note that this function can never use window->layer only - * get_standalone_layer, or we'd have issues. - */ -static MetaStackLayer -get_maximum_layer_in_group (MetaWindow *window) -{ - GSList *members; - MetaGroup *group; - GSList *tmp; - MetaStackLayer max; - MetaStackLayer layer; - - max = META_LAYER_DESKTOP; - - group = meta_window_get_group (window); - - if (group != NULL) - members = meta_group_list_windows (group); - else - members = NULL; - - tmp = members; - while (tmp != NULL) - { - MetaWindow *w = tmp->data; - - layer = get_standalone_layer (w); - if (layer > max) - max = layer; - - tmp = tmp->next; - } - - g_slist_free (members); - - return max; -} - -static void -compute_layer (MetaWindow *window) -{ - window->layer = get_standalone_layer (window); - - /* We can only do promotion-due-to-group for dialogs and other - * transients, or weird stuff happens like the desktop window and - * nautilus windows getting in the same layer, or all gnome-terminal - * windows getting in fullscreen layer if any terminal is - * fullscreen. - */ - if (WINDOW_HAS_TRANSIENT_TYPE(window) && - (window->xtransient_for == None || - window->transient_parent_is_root_window)) - { - /* We only do the group thing if the dialog is NOT transient for - * a particular window. Imagine a group with a normal window, a dock, - * and a dialog transient for the normal window; you don't want the dialog - * above the dock if it wouldn't normally be. - */ - - MetaStackLayer group_max; - - group_max = get_maximum_layer_in_group (window); - - if (group_max > window->layer) - { - meta_topic (META_DEBUG_STACK, - "Promoting window %s from layer %u to %u due to group membership\n", - window->desc, window->layer, group_max); - window->layer = group_max; - } - } - - meta_topic (META_DEBUG_STACK, "Window %s on layer %u type = %u has_focus = %d\n", - window->desc, window->layer, - window->type, window->has_focus); -} - -/* Front of the layer list is the topmost window, - * so the lower stack position is later in the list - */ -static int -compare_window_position (void *a, - void *b) -{ - MetaWindow *window_a = a; - MetaWindow *window_b = b; - - /* Go by layer, then stack_position */ - if (window_a->layer < window_b->layer) - return 1; /* move window_a later in list */ - else if (window_a->layer > window_b->layer) - return -1; - else if (window_a->stack_position < window_b->stack_position) - return 1; /* move window_a later in list */ - else if (window_a->stack_position > window_b->stack_position) - return -1; - else - return 0; /* not reached */ -} - -/* - * Stacking constraints - * - * Assume constraints of the form "AB" meaning "window A must be - * below window B" - * - * If we have windows stacked from bottom to top - * "ABC" then raise A we get "BCA". Say C is - * transient for B is transient for A. So - * we have constraints AB and BC. - * - * After raising A, we need to reapply the constraints. - * If we do this by raising one window at a time - - * - * start: BCA - * apply AB: CAB - * apply BC: ABC - * - * but apply constraints in the wrong order and it breaks: - * - * start: BCA - * apply BC: BCA - * apply AB: CAB - * - * We make a directed graph of the constraints by linking - * from "above windows" to "below windows as follows: - * - * AB -> BC -> CD - * \ - * CE - * - * If we then walk that graph and apply the constraints in the order - * that they appear, we will apply them correctly. Note that the - * graph MAY have cycles, so we have to guard against that. - * - */ - -typedef struct Constraint Constraint; - -struct Constraint -{ - MetaWindow *above; - MetaWindow *below; - - /* used to keep the constraint in the - * list of constraints for window "below" - */ - Constraint *next; - - /* used to create the graph. */ - GSList *next_nodes; - - /* constraint has been applied, used - * to detect cycles. - */ - unsigned int applied : 1; - - /* constraint has a previous node in the graph, - * used to find places to start in the graph. - * (I think this also has the side effect - * of preventing cycles, since cycles will - * have no starting point - so maybe - * the "applied" flag isn't needed.) - */ - unsigned int has_prev : 1; -}; - -/* We index the array of constraints by window - * stack positions, just because the stack - * positions are a convenient index. - */ -static void -add_constraint (Constraint **constraints, - MetaWindow *above, - MetaWindow *below) -{ - Constraint *c; - - g_assert (above->screen == below->screen); - - /* check if constraint is a duplicate */ - c = constraints[below->stack_position]; - while (c != NULL) - { - if (c->above == above) - return; - c = c->next; - } - - /* if not, add the constraint */ - c = g_new (Constraint, 1); - c->above = above; - c->below = below; - c->next = constraints[below->stack_position]; - c->next_nodes = NULL; - c->applied = FALSE; - c->has_prev = FALSE; - - constraints[below->stack_position] = c; -} - -static void -create_constraints (Constraint **constraints, - GList *windows) -{ - GList *tmp; - - tmp = windows; - while (tmp != NULL) - { - MetaWindow *w = tmp->data; - - if (!WINDOW_IN_STACK (w)) - { - meta_topic (META_DEBUG_STACK, "Window %s not in the stack, not constraining it\n", - w->desc); - tmp = tmp->next; - continue; - } - - if (WINDOW_TRANSIENT_FOR_WHOLE_GROUP (w)) - { - GSList *group_windows; - GSList *tmp2; - MetaGroup *group; - - group = meta_window_get_group (w); - - if (group != NULL) - group_windows = meta_group_list_windows (group); - else - group_windows = NULL; - - tmp2 = group_windows; - - while (tmp2 != NULL) - { - MetaWindow *group_window = tmp2->data; - - if (!WINDOW_IN_STACK (group_window) || - w->screen != group_window->screen) - { - tmp2 = tmp2->next; - continue; - } - -#if 0 - /* old way of doing it */ - if (!(meta_window_is_ancestor_of_transient (w, group_window)) && - !WINDOW_TRANSIENT_FOR_WHOLE_GROUP (group_window)) /* note */;/*note*/ -#else - /* better way I think, so transient-for-group are constrained - * only above non-transient-type windows in their group - */ - if (!WINDOW_HAS_TRANSIENT_TYPE (group_window)) -#endif - { - meta_topic (META_DEBUG_STACK, "Constraining %s above %s as it's transient for its group\n", - w->desc, group_window->desc); - add_constraint (constraints, w, group_window); - } - - tmp2 = tmp2->next; - } - - g_slist_free (group_windows); - } - else if (w->xtransient_for != None && - !w->transient_parent_is_root_window) - { - MetaWindow *parent; - - parent = - meta_display_lookup_x_window (w->display, w->xtransient_for); - - if (parent && WINDOW_IN_STACK (parent) && - parent->screen == w->screen) - { - meta_topic (META_DEBUG_STACK, "Constraining %s above %s due to transiency\n", - w->desc, parent->desc); - add_constraint (constraints, w, parent); - } - } - - tmp = tmp->next; - } -} - -static void -graph_constraints (Constraint **constraints, - int n_constraints) -{ - int i; - - i = 0; - while (i < n_constraints) - { - Constraint *c; - - /* If we have "A below B" and "B below C" then AB -> BC so we - * add BC to next_nodes in AB. - */ - - c = constraints[i]; - while (c != NULL) - { - Constraint *n; - - g_assert (c->below->stack_position == i); - - /* Constraints where ->above is below are our - * next_nodes and we are their previous - */ - n = constraints[c->above->stack_position]; - while (n != NULL) - { - c->next_nodes = g_slist_prepend (c->next_nodes, - n); - /* c is a previous node of n */ - n->has_prev = TRUE; - - n = n->next; - } - - c = c->next; - } - - ++i; - } -} - -static void -free_constraints (Constraint **constraints, - int n_constraints) -{ - int i; - - i = 0; - while (i < n_constraints) - { - Constraint *c; - - c = constraints[i]; - while (c != NULL) - { - Constraint *next = c->next; - - g_slist_free (c->next_nodes); - - g_free (c); - - c = next; - } - - ++i; - } -} - -static void -ensure_above (MetaWindow *above, - MetaWindow *below) -{ - if (WINDOW_HAS_TRANSIENT_TYPE(above) && - above->layer < below->layer) - { - meta_topic (META_DEBUG_STACK, - "Promoting window %s from layer %u to %u due to contraint\n", - above->desc, above->layer, below->layer); - above->layer = below->layer; - } - - if (above->stack_position < below->stack_position) - { - /* move above to below->stack_position bumping below down the stack */ - meta_window_set_stack_position_no_sync (above, below->stack_position); - g_assert (below->stack_position + 1 == above->stack_position); - } - meta_topic (META_DEBUG_STACK, "%s above at %d > %s below at %d\n", - above->desc, above->stack_position, - below->desc, below->stack_position); -} - -static void -traverse_constraint (Constraint *c) -{ - GSList *tmp; - - if (c->applied) - return; - - ensure_above (c->above, c->below); - c->applied = TRUE; - - tmp = c->next_nodes; - while (tmp != NULL) - { - traverse_constraint (tmp->data); - - tmp = tmp->next; - } -} - -static void -apply_constraints (Constraint **constraints, - int n_constraints) -{ - GSList *heads; - GSList *tmp; - int i; - - /* List all heads in an ordered constraint chain */ - heads = NULL; - i = 0; - while (i < n_constraints) - { - Constraint *c; - - c = constraints[i]; - while (c != NULL) - { - if (!c->has_prev) - heads = g_slist_prepend (heads, c); - - c = c->next; - } - - ++i; - } - - /* Now traverse the chain and apply constraints */ - tmp = heads; - while (tmp != NULL) - { - Constraint *c = tmp->data; - - traverse_constraint (c); - - tmp = tmp->next; - } - - g_slist_free (heads); -} - -/** - * Go through "deleted" and take the matching windows - * out of "windows". - */ -static void -stack_do_window_deletions (MetaStack *stack) -{ - /* Do removals before adds, with paranoid idea that we might re-add - * the same window IDs. - */ - GList *tmp; - int i; - - tmp = stack->removed; - while (tmp != NULL) - { - Window xwindow; - xwindow = GPOINTER_TO_UINT (tmp->data); - - /* We go from the end figuring removals are more - * likely to be recent. - */ - i = stack->windows->len; - while (i > 0) - { - --i; - - /* there's no guarantee we'll actually find windows to - * remove, e.g. the same xwindow could have been - * added/removed before we ever synced, and we put - * both the window->xwindow and window->frame->xwindow - * in the removal list. - */ - if (xwindow == g_array_index (stack->windows, Window, i)) - { - g_array_remove_index (stack->windows, i); - goto next; - } - } - - next: - tmp = tmp->next; - } - - g_list_free (stack->removed); - stack->removed = NULL; -} - -static void -stack_do_window_additions (MetaStack *stack) -{ - GList *tmp; - gint i, n_added; - - n_added = g_list_length (stack->added); - if (n_added > 0) - { - Window *end; - int old_size; - - meta_topic (META_DEBUG_STACK, - "Adding %d windows to sorted list\n", - n_added); - - old_size = stack->windows->len; - g_array_set_size (stack->windows, old_size + n_added); - - end = &g_array_index (stack->windows, Window, old_size); - - /* stack->added has the most recent additions at the - * front of the list, so we need to reverse it - */ - stack->added = g_list_reverse (stack->added); - - i = 0; - tmp = stack->added; - while (tmp != NULL) - { - MetaWindow *w; - - w = tmp->data; - - end[i] = w->xwindow; - - /* add to the main list */ - stack->sorted = g_list_prepend (stack->sorted, w); - - ++i; - tmp = tmp->next; - } - - stack->need_resort = TRUE; /* may not be needed as we add to top */ - stack->need_constrain = TRUE; - stack->need_relayer = TRUE; - } - - g_list_free (stack->added); - stack->added = NULL; -} - -/** - * Update the layers that windows are in - */ -static void -stack_do_relayer (MetaStack *stack) -{ - GList *tmp; - - if (!stack->need_relayer) - return; - - meta_topic (META_DEBUG_STACK, - "Recomputing layers\n"); - - tmp = stack->sorted; - - while (tmp != NULL) - { - MetaWindow *w; - MetaStackLayer old_layer; - - w = tmp->data; - old_layer = w->layer; - - compute_layer (w); - - if (w->layer != old_layer) - { - meta_topic (META_DEBUG_STACK, - "Window %s moved from layer %u to %u\n", - w->desc, old_layer, w->layer); - - stack->need_resort = TRUE; - stack->need_constrain = TRUE; - /* don't need to constrain as constraining - * purely operates in terms of stack_position - * not layer - */ - } - - tmp = tmp->next; - } - - stack->need_relayer = FALSE; -} - -/** - * Update stack_position and layer to reflect transiency - * constraints - */ -static void -stack_do_constrain (MetaStack *stack) -{ - Constraint **constraints; - - /* It'd be nice if this were all faster, probably */ - - if (!stack->need_constrain) - return; - - meta_topic (META_DEBUG_STACK, - "Reapplying constraints\n"); - - constraints = g_new0 (Constraint*, - stack->n_positions); - - create_constraints (constraints, stack->sorted); - - graph_constraints (constraints, stack->n_positions); - - apply_constraints (constraints, stack->n_positions); - - free_constraints (constraints, stack->n_positions); - g_free (constraints); - - stack->need_constrain = FALSE; -} - -/** - * Sort stack->sorted with layers having priority over stack_position. - */ -static void -stack_do_resort (MetaStack *stack) -{ - if (!stack->need_resort) - return; - - meta_topic (META_DEBUG_STACK, - "Sorting stack list\n"); - - stack->sorted = g_list_sort (stack->sorted, - (GCompareFunc) compare_window_position); - - stack->need_resort = FALSE; -} - -/** - * Puts the stack into canonical form. - * - * Honour the removed and added lists of the stack, and then recalculate - * all the layers (if the flag is set), re-run all the constraint calculations - * (if the flag is set), and finally re-sort the stack (if the flag is set, - * and if it wasn't already it might have become so during all the previous - * activity). - */ -static void -stack_ensure_sorted (MetaStack *stack) -{ - stack_do_window_deletions (stack); - stack_do_window_additions (stack); - stack_do_relayer (stack); - stack_do_constrain (stack); - stack_do_resort (stack); -} - -/** - * This function is used to avoid raising a window above popup - * menus and other such things. - * - * FIXME This is sort of an expensive function, should probably - * do something to avoid it. One 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, - Window xwindow) -{ - - Window ignored1, ignored2; - Window *children; - unsigned int n_children; - int i; - - /* Normally XQueryTree() means "must grab server" but here - * we don't, since we know we won't manage any new windows - * or restack any windows before using the XQueryTree results. - */ - - meta_error_trap_push_with_return (screen->display); - - XQueryTree (screen->display->xdisplay, - screen->xroot, - &ignored1, &ignored2, &children, &n_children); - - if (meta_error_trap_pop_with_return (screen->display, TRUE) != Success) - { - meta_topic (META_DEBUG_STACK, - "Error querying root children to raise window 0x%lx\n", - xwindow); - return; - } - - /* Children are in order from bottom to top. We want to - * find the topmost managed child, then configure - * our window to be above it. - */ - i = n_children - 1; - while (i >= 0) - { - if (children[i] == xwindow) - { - /* 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_display_lookup_x_window (screen->display, - children[i]) != NULL) - { - XWindowChanges changes; - - /* children[i] is the topmost managed child */ - meta_topic (META_DEBUG_STACK, - "Moving 0x%lx above topmost managed child window 0x%lx\n", - xwindow, children[i]); - - changes.sibling = children[i]; - changes.stack_mode = Above; - - meta_error_trap_push (screen->display); - XConfigureWindow (screen->display->xdisplay, - xwindow, - CWSibling | CWStackMode, - &changes); - meta_error_trap_pop (screen->display, FALSE); - - break; - } - - --i; - } - - if (i < 0) - { - /* No sibling to use, just lower ourselves to the bottom - * to be sure we're below any override redirect windows. - */ - meta_error_trap_push (screen->display); - XLowerWindow (screen->display->xdisplay, - xwindow); - meta_error_trap_pop (screen->display, FALSE); - } - - if (children) - XFree (children); -} - -/** - * Order the windows on the X server to be the same as in our structure. - * We do this using XRestackWindows if we don't know the previous order, - * or XConfigureWindow on a few particular windows if we do and can figure - * out the minimum set of changes. After that, we set __NET_CLIENT_LIST - * and __NET_CLIENT_LIST_STACKING. - */ -static void -stack_sync_to_server (MetaStack *stack) -{ - GArray *stacked; - GArray *root_children_stacked; - GList *tmp; - - /* Bail out if frozen */ - if (stack->freeze_count > 0) - return; - - meta_topic (META_DEBUG_STACK, "Syncing window stack to server\n"); - - 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() - */ - stacked = g_array_new (FALSE, FALSE, sizeof (Window)); - root_children_stacked = g_array_new (FALSE, FALSE, sizeof (Window)); - - meta_topic (META_DEBUG_STACK, "Top to bottom: "); - meta_push_no_msg_prefix (); - - tmp = stack->sorted; - while (tmp != NULL) - { - MetaWindow *w; - - w = tmp->data; - - /* remember, stacked is in reverse order (bottom to top) */ - g_array_prepend_val (stacked, w->xwindow); - - /* build XRestackWindows() array from top to bottom */ - if (w->frame) - g_array_append_val (root_children_stacked, w->frame->xwindow); - else - g_array_append_val (root_children_stacked, w->xwindow); - - meta_topic (META_DEBUG_STACK, "%u:%d - %s ", w->layer, w->stack_position, w->desc); - - tmp = tmp->next; - } - - meta_topic (META_DEBUG_STACK, "\n"); - meta_pop_no_msg_prefix (); - - /* All windows should be in some stacking order */ - if (stacked->len != stack->windows->len) - meta_bug ("%u windows stacked, %u windows exist in stack\n", - stacked->len, stack->windows->len); - - /* Sync to server */ - - meta_topic (META_DEBUG_STACK, "Restacking %u windows\n", - root_children_stacked->len); - - meta_error_trap_push (stack->screen->display); - - if (stack->last_root_children_stacked == NULL) - { - /* Just impose our stack, we don't know the previous state. - * This involves a ton of circulate requests and may flicker. - */ - meta_topic (META_DEBUG_STACK, "Don't know last stack state, restacking everything\n"); - - if (root_children_stacked->len > 0) - XRestackWindows (stack->screen->display->xdisplay, - (Window *) root_children_stacked->data, - root_children_stacked->len); - } - else if (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... - */ - const Window *old_stack = (Window *) stack->last_root_children_stacked->data; - const Window *new_stack = (Window *) root_children_stacked->data; - const int old_len = stack->last_root_children_stacked->len; - const int new_len = root_children_stacked->len; - const Window *oldp = old_stack; - const Window *newp = new_stack; - const Window *old_end = old_stack + old_len; - const Window *new_end = new_stack + new_len; - Window last_window = None; - - while (oldp != old_end && - newp != new_end) - { - if (*oldp == *newp) - { - /* Stacks are the same here, move on */ - ++oldp; - last_window = *newp; - ++newp; - } - else if (meta_display_lookup_x_window (stack->screen->display, - *oldp) == NULL) - { - /* *oldp is no longer known to us (probably destroyed), - * so we can just skip it - */ - ++oldp; - } - else - { - /* Move *newp below last_window */ - if (last_window == None) - { - meta_topic (META_DEBUG_STACK, "Using window 0x%lx as topmost (but leaving it in-place)\n", *newp); - - raise_window_relative_to_managed_windows (stack->screen, - *newp); - } - else - { - /* This means that if last_window is dead, but not - * *newp, then we fail to restack *newp; but on - * unmanaging last_window, we'll fix it up. - */ - - XWindowChanges changes; - - changes.sibling = last_window; - changes.stack_mode = Below; - - meta_topic (META_DEBUG_STACK, "Placing window 0x%lx below 0x%lx\n", - *newp, last_window); - - XConfigureWindow (stack->screen->display->xdisplay, - *newp, - CWSibling | CWStackMode, - &changes); - } - - 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; - XRestackWindows (stack->screen->display->xdisplay, - (Window *) newp, new_end - newp); - } - } - - meta_error_trap_pop (stack->screen->display, FALSE); - /* 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. - */ - - /* Sync _NET_CLIENT_LIST and _NET_CLIENT_LIST_STACKING */ - - XChangeProperty (stack->screen->display->xdisplay, - stack->screen->xroot, - stack->screen->display->atom__NET_CLIENT_LIST, - XA_WINDOW, - 32, PropModeReplace, - (unsigned char *)stack->windows->data, - stack->windows->len); - XChangeProperty (stack->screen->display->xdisplay, - stack->screen->xroot, - stack->screen->display->atom__NET_CLIENT_LIST_STACKING, - XA_WINDOW, - 32, PropModeReplace, - (unsigned char *)stacked->data, - stacked->len); - - g_array_free (stacked, TRUE); - - if (stack->last_root_children_stacked) - g_array_free (stack->last_root_children_stacked, TRUE); - stack->last_root_children_stacked = root_children_stacked; - - /* That was scary... */ -} - -MetaWindow* -meta_stack_get_top (MetaStack *stack) -{ - stack_ensure_sorted (stack); - - if (stack->sorted) - return stack->sorted->data; - else - return NULL; -} - -MetaWindow* -meta_stack_get_bottom (MetaStack *stack) -{ - GList *link; - - stack_ensure_sorted (stack); - - link = g_list_last (stack->sorted); - if (link != NULL) - return link->data; - else - return NULL; -} - -MetaWindow* -meta_stack_get_above (MetaStack *stack, - MetaWindow *window, - gboolean only_within_layer) -{ - GList *link; - MetaWindow *above; - - stack_ensure_sorted (stack); - - link = g_list_find (stack->sorted, window); - if (link == NULL) - return NULL; - if (link->prev == NULL) - return NULL; - - above = link->prev->data; - - if (only_within_layer && - above->layer != window->layer) - return NULL; - else - return above; -} - -MetaWindow* -meta_stack_get_below (MetaStack *stack, - MetaWindow *window, - gboolean only_within_layer) -{ - GList *link; - MetaWindow *below; - - stack_ensure_sorted (stack); - - link = g_list_find (stack->sorted, window); - - if (link == NULL) - return NULL; - if (link->next == NULL) - return NULL; - - below = link->next->data; - - if (only_within_layer && - below->layer != window->layer) - return NULL; - else - return below; -} - -static gboolean -window_contains_point (MetaWindow *window, - int root_x, - int root_y) -{ - MetaRectangle rect; - - meta_window_get_outer_rect (window, &rect); - - return POINT_IN_RECT (root_x, root_y, rect); -} - -static MetaWindow* -get_default_focus_window (MetaStack *stack, - MetaWorkspace *workspace, - MetaWindow *not_this_one, - gboolean must_be_at_point, - int root_x, - int root_y) -{ - /* Find the topmost, focusable, mapped, window. - * not_this_one is being unfocused or going away, so exclude it. - * Also, prefer to focus transient parent of not_this_one, - * or top window in same group as not_this_one. - */ - - MetaWindow *topmost_dock; - MetaWindow *transient_parent; - MetaWindow *topmost_in_group; - MetaWindow *topmost_overall; - MetaGroup *not_this_one_group; - GList *link; - - topmost_dock = NULL; - transient_parent = NULL; - topmost_in_group = NULL; - topmost_overall = NULL; - if (not_this_one) - not_this_one_group = meta_window_get_group (not_this_one); - else - not_this_one_group = NULL; - - stack_ensure_sorted (stack); - - /* top of this layer is at the front of the list */ - link = stack->sorted; - - while (link) - { - MetaWindow *window = link->data; - - if (window && - window != not_this_one && - (window->unmaps_pending == 0) && - !window->minimized && - (window->input || window->take_focus) && - (workspace == NULL || - meta_window_located_on_workspace (window, workspace))) - { - if (topmost_dock == NULL && - window->type == META_WINDOW_DOCK) - topmost_dock = window; - - if (not_this_one != NULL) - { - if (transient_parent == NULL && - not_this_one->xtransient_for != None && - not_this_one->xtransient_for == window->xwindow && - (!must_be_at_point || - window_contains_point (window, root_x, root_y))) - transient_parent = window; - - if (topmost_in_group == NULL && - not_this_one_group != NULL && - not_this_one_group == meta_window_get_group (window) && - (!must_be_at_point || - window_contains_point (window, root_x, root_y))) - topmost_in_group = window; - } - - /* Note that DESKTOP windows can be topmost_overall so - * we prefer focusing desktop or other windows over - * focusing dock, even though docks are stacked higher. - */ - if (topmost_overall == NULL && - window->type != META_WINDOW_DOCK && - (!must_be_at_point || - window_contains_point (window, root_x, root_y))) - topmost_overall = window; - - /* We could try to bail out early here for efficiency in - * some cases, but it's just not worth the code. - */ - } - - link = link->next; - } - - if (transient_parent) - return transient_parent; - else if (topmost_in_group) - return topmost_in_group; - else if (topmost_overall) - return topmost_overall; - else - return topmost_dock; -} - -MetaWindow* -meta_stack_get_default_focus_window_at_point (MetaStack *stack, - MetaWorkspace *workspace, - MetaWindow *not_this_one, - int root_x, - int root_y) -{ - return get_default_focus_window (stack, workspace, not_this_one, - TRUE, root_x, root_y); -} - -MetaWindow* -meta_stack_get_default_focus_window (MetaStack *stack, - MetaWorkspace *workspace, - MetaWindow *not_this_one) -{ - return get_default_focus_window (stack, workspace, not_this_one, - FALSE, 0, 0); -} - -GList* -meta_stack_list_windows (MetaStack *stack, - MetaWorkspace *workspace) -{ - GList *workspace_windows = NULL; - GList *link; - - stack_ensure_sorted (stack); /* do adds/removes */ - - link = stack->sorted; - - while (link) - { - MetaWindow *window = link->data; - - if (window && - (workspace == NULL || meta_window_located_on_workspace (window, workspace))) - { - workspace_windows = g_list_prepend (workspace_windows, - window); - } - - link = link->next; - } - - return workspace_windows; -} - -int -meta_stack_windows_cmp (MetaStack *stack, - MetaWindow *window_a, - MetaWindow *window_b) -{ - g_return_val_if_fail (window_a->screen == window_b->screen, 0); - - /* -1 means a below b */ - - stack_ensure_sorted (stack); /* update constraints, layers */ - - if (window_a->layer < window_b->layer) - return -1; - else if (window_a->layer > window_b->layer) - return 1; - else if (window_a->stack_position < window_b->stack_position) - return -1; - else if (window_a->stack_position > window_b->stack_position) - return 1; - else - return 0; /* not reached */ -} - -static int -compare_just_window_stack_position (void *a, - void *b) -{ - MetaWindow *window_a = a; - MetaWindow *window_b = b; - - if (window_a->stack_position < window_b->stack_position) - return -1; /* move window_a earlier in list */ - else if (window_a->stack_position > window_b->stack_position) - return 1; - else - return 0; /* not reached */ -} - -GList* -meta_stack_get_positions (MetaStack *stack) -{ - GList *tmp; - - /* Make sure to handle any adds or removes */ - stack_ensure_sorted (stack); - - tmp = g_list_copy (stack->sorted); - tmp = g_list_sort (tmp, (GCompareFunc) compare_just_window_stack_position); - - return tmp; -} - -static gint -compare_pointers (gconstpointer a, - gconstpointer b) -{ - if (a > b) - return 1; - else if (a < b) - return -1; - else - return 0; -} - -static gboolean -lists_contain_same_windows (GList *a, - GList *b) -{ - GList *copy1, *copy2; - GList *tmp1, *tmp2; - - if (g_list_length (a) != g_list_length (b)) - return FALSE; - - tmp1 = copy1 = g_list_sort (g_list_copy (a), compare_pointers); - tmp2 = copy2 = g_list_sort (g_list_copy (b), compare_pointers); - - while (tmp1 && tmp1->data == tmp2->data) /* tmp2 is non-NULL if tmp1 is */ - { - tmp1 = tmp1->next; - tmp2 = tmp2->next; - } - - g_list_free (copy1); - g_list_free (copy2); - - return (tmp1 == NULL); /* tmp2 is non-NULL if tmp1 is */ -} - -void -meta_stack_set_positions (MetaStack *stack, - GList *windows) -{ - int i; - GList *tmp; - - /* Make sure any adds or removes aren't in limbo -- is this needed? */ - stack_ensure_sorted (stack); - - if (!lists_contain_same_windows (windows, stack->sorted)) - { - meta_warning ("This list of windows has somehow changed; not resetting " - "positions of the windows.\n"); - return; - } - - g_list_free (stack->sorted); - stack->sorted = g_list_copy (windows); - - stack->need_resort = TRUE; - stack->need_constrain = TRUE; - - i = 0; - tmp = windows; - while (tmp != NULL) - { - MetaWindow *w = tmp->data; - w->stack_position = i++; - tmp = tmp->next; - } - - meta_topic (META_DEBUG_STACK, - "Reset the stack positions of (nearly) all windows\n"); - - stack_sync_to_server (stack); -} - -void -meta_window_set_stack_position_no_sync (MetaWindow *window, - int position) -{ - int low, high, delta; - GList *tmp; - - g_return_if_fail (window->screen->stack != NULL); - g_return_if_fail (window->stack_position >= 0); - g_return_if_fail (position >= 0); - g_return_if_fail (position < window->screen->stack->n_positions); - - if (position == window->stack_position) - { - meta_topic (META_DEBUG_STACK, "Window %s already has position %d\n", - window->desc, position); - return; - } - - window->screen->stack->need_resort = TRUE; - window->screen->stack->need_constrain = TRUE; - - if (position < window->stack_position) - { - low = position; - high = window->stack_position - 1; - delta = 1; - } - else - { - low = window->stack_position + 1; - high = position; - delta = -1; - } - - tmp = window->screen->stack->sorted; - while (tmp != NULL) - { - MetaWindow *w = tmp->data; - - if (w->stack_position >= low && - w->stack_position <= high) - w->stack_position += delta; - - tmp = tmp->next; - } - - window->stack_position = position; - - meta_topic (META_DEBUG_STACK, - "Window %s had stack_position set to %d\n", - window->desc, window->stack_position); -} - -void -meta_window_set_stack_position (MetaWindow *window, - int position) -{ - meta_window_set_stack_position_no_sync (window, position); - stack_sync_to_server (window->screen->stack); -} |