/* Metacity Window Stack */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2002 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, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "stack.h" #include "window.h" #include "errors.h" #include "frame.h" #include "group.h" #include "prefs.h" #include "workspace.h" #include static void meta_stack_sync_to_server (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); meta_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 (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 */ stack->removed = g_list_prepend (stack->removed, (void*) window->xwindow); if (window->frame) stack->removed = g_list_prepend (stack->removed, (void*) window->frame->xwindow); meta_stack_sync_to_server (stack); } void meta_stack_update_layer (MetaStack *stack, MetaWindow *window) { stack->need_relayer = TRUE; meta_stack_sync_to_server (stack); } void meta_stack_update_transient (MetaStack *stack, MetaWindow *window) { stack->need_constrain = TRUE; meta_stack_sync_to_server (stack); } /* raise/lower within a layer */ void meta_stack_raise (MetaStack *stack, MetaWindow *window) { meta_window_set_stack_position (window, stack->n_positions - 1); meta_stack_sync_to_server (stack); } void meta_stack_lower (MetaStack *stack, MetaWindow *window) { meta_window_set_stack_position (window, 0); meta_stack_sync_to_server (stack); } /* Prevent syncing to server until thaw */ 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; meta_stack_sync_to_server (stack); } static gboolean window_is_fullscreen_size (MetaWindow *window) { int i; if (window->rect.x <= 0 && window->rect.y <= 0 && window->rect.width >= window->screen->width && window->rect.height >= window->screen->height) return TRUE; i = 0; while (i < window->screen->n_xinerama_infos) { if (window->rect.x == window->screen->xinerama_infos[i].x_origin && window->rect.y == window->screen->xinerama_infos[i].y_origin && window->rect.width >= window->screen->xinerama_infos[i].width && window->rect.height >= window->screen->xinerama_infos[i].height) return TRUE; ++i; } return FALSE; } /* Get layer ignoring any transient or group relationships */ static MetaStackLayer get_standalone_layer (MetaWindow *window) { MetaStackLayer layer; 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; case META_WINDOW_SPLASHSCREEN: layer = META_LAYER_SPLASH; break; default: #if 0 if (window->has_focus && meta_prefs_get_focus_mode () == META_FOCUS_MODE_CLICK) layer = META_LAYER_FOCUSED_WINDOW; #endif if (window->has_focus && (window->fullscreen || window_is_fullscreen_size (window))) layer = META_LAYER_FULLSCREEN; else if (window->wm_state_above) layer = META_LAYER_DOCK; else layer = META_LAYER_NORMAL; break; } return layer; } static MetaStackLayer get_maximum_layer_of_ancestor (MetaWindow *window) { MetaWindow *w; MetaStackLayer max; MetaStackLayer layer; max = get_standalone_layer (window); w = window; while (w != NULL) { if (w->xtransient_for == None || w->transient_parent_is_root_window) break; w = meta_display_lookup_x_window (w->display, w->xtransient_for); if (w == window) break; /* Cute, someone thought they'd make a transient_for cycle */ /* w may be null... */ if (w != NULL) { layer = get_standalone_layer (w); if (layer > max) max = layer; } } return max; } /* 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->type == META_WINDOW_DIALOG || window->type == META_WINDOW_MODAL_DIALOG || window->type == META_WINDOW_UTILITY || window->type == META_WINDOW_MENU || window->type == META_WINDOW_TOOLBAR) { if (window->xtransient_for != None && !window->transient_parent_is_root_window) { MetaStackLayer ancestor_max; ancestor_max = get_maximum_layer_of_ancestor (window); if (ancestor_max > window->layer) { meta_topic (META_DEBUG_STACK, "Promoting window %s from layer %d to %d due to transiency\n", window->desc, window->layer, ancestor_max); window->layer = ancestor_max; } } else { /* 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 %d to %d due to group membership\n", window->desc, window->layer, group_max); window->layer = group_max; } } } meta_topic (META_DEBUG_STACK, "Window %s on layer %d type = %d 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 */ } static void ensure_above (MetaWindow *above, MetaWindow *below) { if (above->stack_position < below->stack_position) { /* move above to below->stack_position bumping below down the stack */ meta_window_set_stack_position (above, below->stack_position); g_assert (below->stack_position + 1 == above->stack_position); } meta_topic (META_DEBUG_STACK, "Above pos %d > below pos %d\n", above->stack_position, below->stack_position); } #define WINDOW_TRANSIENT_FOR_WHOLE_GROUP(w) \ ((w->xtransient_for == None || \ w->transient_parent_is_root_window) && \ (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)) static void apply_constraints (GList *list) { GList *tmp; /* This algorithm could stand to be a bit less * quadratic */ tmp = list; while (tmp != NULL) { MetaWindow *w = tmp->data; 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 (!(meta_window_is_ancestor_of_transient (w, group_window)) && !WINDOW_TRANSIENT_FOR_WHOLE_GROUP (group_window)) { meta_topic (META_DEBUG_STACK, "Stacking %s above %s as it's a dialog transient for its group\n", w->desc, group_window->desc); ensure_above (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) { meta_topic (META_DEBUG_STACK, "Stacking %s above %s due to transiency\n", w->desc, parent->desc); ensure_above (w, parent); } } tmp = tmp->next; } } static void meta_stack_ensure_sorted (MetaStack *stack) { GList *tmp; int i; int n_added; /* Note that the additions, relayers, reconstrains * may all set need_resort to TRUE */ /* Do removals before adds, with paranoid idea that we might re-add * the same window IDs. */ tmp = stack->removed; while (tmp != NULL) { Window xwindow; xwindow = (unsigned long) 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; 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 */ if (stack->need_relayer) { 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 %d to %d\n", w->desc, old_layer, w->layer); stack->need_resort = 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 to reflect transiency constraints */ if (stack->need_constrain) { meta_topic (META_DEBUG_STACK, "Reapplying constraints\n"); apply_constraints (stack->sorted); stack->need_constrain = FALSE; } /* Sort stack->sorted with layers having priority over stack_position */ if (stack->need_resort) { meta_topic (META_DEBUG_STACK, "Sorting stack list\n"); stack->sorted = g_list_sort (stack->sorted, (GCompareFunc) compare_window_position); stack->need_resort = FALSE; } } static void raise_window_relative_to_managed_windows (MetaScreen *screen, Window xwindow) { /* 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) */ Window ignored1, ignored2; Window *children; 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 (screen->display); XQueryTree (screen->display->xdisplay, screen->xroot, &ignored1, &ignored2, &children, &n_children); if (meta_error_trap_pop (screen->display)) { 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); 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); } if (children) XFree (children); } static void meta_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"); meta_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, "%d:%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 ("%d windows stacked, %d windows exist in stack\n", stacked->len, stack->windows->len); /* Sync to server */ meta_topic (META_DEBUG_STACK, "Restacking %d 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); /* 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, 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, 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) { meta_stack_ensure_sorted (stack); if (stack->sorted) return stack->sorted->data; else return NULL; } MetaWindow* meta_stack_get_bottom (MetaStack *stack) { GList *link; meta_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; meta_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; meta_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; } MetaWindow* meta_stack_get_default_focus_window (MetaStack *stack, MetaWorkspace *workspace, MetaWindow *not_this_one) { /* 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; meta_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 && (workspace == NULL || meta_window_visible_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) transient_parent = window; if (topmost_in_group == NULL && not_this_one_group != NULL && not_this_one_group == meta_window_get_group (window)) 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) 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; } GList* meta_stack_list_windows (MetaStack *stack, MetaWorkspace *workspace) { GList *workspace_windows = NULL; GList *link; meta_stack_ensure_sorted (stack); /* do adds/removes */ link = stack->sorted; while (link) { MetaWindow *window = link->data; if (window && (workspace == NULL || meta_window_visible_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 */ meta_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 */ } void meta_window_set_stack_position (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); }