diff --git a/src/display.h b/src/display.h index 553360d93..4421bd2e9 100644 --- a/src/display.h +++ b/src/display.h @@ -30,6 +30,7 @@ typedef struct _MetaDisplay MetaDisplay; typedef struct _MetaFrame MetaFrame; typedef struct _MetaScreen MetaScreen; +typedef struct _MetaStack MetaStack; typedef struct _MetaUISlave MetaUISlave; typedef struct _MetaWindow MetaWindow; typedef struct _MetaWorkspace MetaWorkspace; diff --git a/src/stack.c b/src/stack.c new file mode 100644 index 000000000..bf5a7236e --- /dev/null +++ b/src/stack.c @@ -0,0 +1,471 @@ +/* Metacity Window Stack */ + +/* + * Copyright (C) 2001 Havoc Pennington + * + * 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" + +struct _MetaStackOp +{ + guint raised : 1; + guint lowered : 1; + guint update_transient : 1; + guint update_layer : 1; + guint removed : 1; + MetaWindow *window; + Window xwindow; /* needed for remove, since window will be NULL */ + int add_order; /* sequence number of add since last sync */ +}; + +static void meta_stack_sync_to_server (MetaStack *stack); + + +MetaStack* +meta_stack_new (MetaScreen *screen) +{ + MetaStack *stack; + int i; + + stack = g_new (MetaStack, 1); + + stack->screen = screen; + stack->windows = g_array_new (FALSE, FALSE, sizeof (Window)); + + i = 0; + while (i < META_LAYER_LAST) + { + stack->layers[i] = NULL; + ++i; + } + stack->pending = NULL; + stack->freeze_count = 0; + + return stack; +} + +void +meta_stack_free (MetaStack *stack) +{ + GList *tmp; + + g_array_free (stack->windows, TRUE); + + i = 0; + while (i < META_LAYER_LAST) + { + g_list_free (stack->layers[i]); + ++i; + } + + tmp = stack->pending; + while (tmp != NULL) + { + MetaStackOp *op; + + op = tmp->data; + if (op->window) + op->window->stack_op = NULL; + g_free (op); + + tmp = tmp->next; + } + + g_list_free (stack->pending); +} + +static MetaStackOp* +ensure_op (MetaStack *stack, + MetaWindow *window) +{ + if (window->stack_op == NULL) + { + /* init all flags to 0 */ + window->stack_op = g_new0 (MetaStackOp, 1); + window->stack_op->window = window; + window->stack_op->xwindow = window->xwindow; + window->stack_op->add_order = -1; /* indicates never added */ + } + else + { + /* Need to move to front of list */ + stack->pending = g_list_remove (stack->pending, window->stack_op); + } + + stack->pending = g_list_prepend (stack->pending, window->stack_op); + + return window->stack_op; +} + +void +meta_stack_add (MetaStack *stack, + MetaWindow *window) +{ + MetaStackOp *op; + + op = ensure_op (stack, window); + + if (op->add_order >= 0) + meta_bug ("Window %s added to stack twice\n", window->desc); + + op->add_order = stack->n_added; + stack->n_added += 1; + + if (op->removed) + meta_bug ("Remove op was left associated with window %s\n", + window->desc); + + /* We automatically need to update all new windows */ + op->update_layer = TRUE; + op->update_transient = TRUE; + + meta_stack_sync_to_server (stack); +} + +void +meta_stack_remove (MetaStack *stack, + MetaWindow *window) +{ + MetaStackOp *op; + + op = ensure_op (stack, window); + + if (op->add_order >= 0) + { + /* All we have to do is cancel the add */ + stack->pending = g_list_remove (stack->pending, op); + window->stack_op = NULL; + g_free (op); + return; + } + + /* op was other than an add, save a remove op */ + + op->window = NULL; /* can't touch this anymore. */ + op->removed = TRUE; + op->add_order = -1; + + /* Need to immediately remove from layer lists */ + stack->layers[window->layer] = + g_list_remove (stack->layers[window->layer], window); + + meta_stack_sync_to_server (stack); +} + +void +meta_stack_update_layer (MetaStack *stack, + MetaWindow *window) +{ + MetaStackOp *op; + + op = ensure_op (stack, window); + op->update_layer = TRUE; + + meta_stack_sync_to_server (stack); +} + +void +meta_stack_update_transient (MetaStack *stack, + MetaWindow *window) +{ + MetaStackOp *op; + + op = ensure_op (stack, window); + op->update_transient = TRUE; + + meta_stack_sync_to_server (stack); +} + +/* raise/lower within a layer */ +void +meta_stack_raise (MetaStack *stack, + MetaWindow *window) +{ + MetaStackOp *op; + + op = ensure_op (stack, window); + op->raised = TRUE; + op->lowered = FALSE; + + meta_stack_sync_to_server (stack); +} + +void +meta_stack_lower (MetaStack *stack, + MetaWindow *window) +{ + MetaStackOp *op; + + op = ensure_op (stack, window); + op->raised = FALSE; + op->lowered = TRUE; + + 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 void +compute_layer (MetaWindow *window) +{ + switch (window->type) + { + case META_WINDOW_DESKTOP: + window->layer = META_LAYER_DESKTOP; + break; + + case META_WINDOW_DOCK: + window->layer = META_WINDOW_DOCK; + break; + + default: + window->layer = META_LAYER_NORMAL; + break; + } +} + +static void +meta_stack_sync_to_server (MetaStack *stack) +{ + gboolean needs_sort[META_LAYER_LAST] = { + FALSE, FALSE, FALSE, FALSE, FALSE + }; + GList *tmp; + Window *added; + Window *scratch; + int n_actually_added; + int i, j; + int old_size; + + /* Bail out if frozen */ + if (stack->freeze_count > 0) + return; + + /* Here comes the fun - figure out all the stacking. + * We make no pretense of efficiency. + * a) replay all the pending operations + * b) do a stable sort within each layer with a comparison + * function that uses our constraints such as TRANSIENT_FOR + * c) sync to server + */ + + /* n_added is the number of meta_stack_add() calls, not the number + * of windows that eventually get added here. meta_stack_remove() + * may have cancelled some add operations. + */ + if (stack->n_added > 0) + added = g_new0 (Window, stack->n_added); + else + added = NULL; + + n_actually_added = 0; + tmp = stack->pending; + while (tmp != NULL) + { + MetaStackOp *op; + + op = tmp->data; + + if (op->add_order >= 0) + { + added[op->add_order] = op->window->xwindow; + ++n_actually_added; + } + + tmp = tmp->next; + } + + old_size = stack->window->len; + g_array_set_size (stack->windows, + old_size + n_actually_added); + + scratch = &g_array_index (stack->windows, Window, old_size); + + i = 0; + j = 0; + while (i < stack->n_added) + { + if (added[i] != None) + { + scratch[j] = added[i]; + ++j; + } + + ++i; + } + + g_assert (j == n_actually_added); + g_assert (i == stack->n_added); + + g_free (added); + + /* Now remove windows that need removing; + * they were already removed from the layer lists + * in meta_stack_remove() + */ + tmp = stack->pending; + while (tmp != NULL) + { + MetaStackOp *op; + + op = tmp->data; + + if (op->removed) + { + /* Go from the end, on the principle that more recent + * windows are more likely to be removed, and also that we + * can remove without changing what we're iterating over. + */ + 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, or even + * added/removed/added/removed/added again, etc. + */ + if (op->xwindow == g_array_index (stack->windows, Window, i)) + { + g_array_remove_index (stack->windows, i); + goto next; + } + } + } + + next: + tmp = tmp->next; + } + + /* With all the adding/removing sorted out, actually do our + * operations + */ + + tmp = stack->pending; + while (tmp != NULL) + { + MetaStackOp *op; + + op = tmp->data; + + if (!op->removed) + { + MetaStackLayer old_layer; + + old_layer = op->window->layer; + + if (op->update_layer) + { + compute_layer (op->window); + + if (op->window->layer != old_layer) + { + /* don't resort old layer, it's + * assumed that removing a window + * makes no difference. + */ + needs_sort[op->window->layer] = TRUE; + + stack->layers[old_layer] = + g_list_remove (stack->layers[old_layer], op->window); + stack->layers[op->window->layer] = + g_list_prepend (stack->layers[op->window->layer], op->window); + } + } + + if (op->update_transient) + { + /* need to resort our layer */ + needs_sort[op->window->layer] = TRUE; + } + + /* We assume that ordering between changing layers + * and raise/lower is irrelevant; if you raise, then + * the layer turns out to be different, you still + * raise inside the new layer + */ + if (op->raised) + { + /* "top" is the front of the list */ + + stack->layers[op->window->layer] = + g_list_remove (stack->layers[op->window->layer], + op->window); + + stack->layers[op->window->layer] = + g_list_prepend (stack->layers[op->window->layer], + op->window); + } + else if (op->lowered) + { + stack->layers[op->window->layer] = + g_list_remove (stack->layers[op->window->layer], + op->window); + + stack->layers[op->window->layer] = + g_list_append (stack->layers[op->window->layer], + op->window); + } + } + + g_free (op); + + tmp = tmp->next; + } + + g_list_free (stack->pending); + stack->pending = NULL; + stack->n_added = 0; + + + /* Sort the layer lists */ + i = 0; + while (i < META_LAYER_LAST) + { + if (needs_sort[i]) + { + + + } + + ++i; + } + + /* Create stacked xwindow array */ + + /* Sync to server */ + + /* Sync _NET_CLIENT_LIST and _NET_CLIENT_LIST_STACKING */ + +} + diff --git a/src/window.c b/src/window.c index 7abdf8431..3da2ba06a 100644 --- a/src/window.c +++ b/src/window.c @@ -189,6 +189,9 @@ meta_window_new (MetaDisplay *display, Window xwindow) window->type = META_WINDOW_NORMAL; window->type_atom = None; + + window->layer = META_LAYER_NORMAL; + window->stack_op = NULL; meta_display_register_x_window (display, &window->xwindow, window); diff --git a/src/window.h b/src/window.h index 237b2c1fd..8c7ed0708 100644 --- a/src/window.h +++ b/src/window.h @@ -134,6 +134,10 @@ struct _MetaWindow int border_width; /* x/y/w/h here get filled with ConfigureRequest values */ XSizeHints size_hints; + + /* Managed by stack.c */ + MetaStackLayer layer; + MetaStackOp *stack_op; }; MetaWindow* meta_window_new (MetaDisplay *display,