diff --git a/src/Makefile.am b/src/Makefile.am index f95c98095..1d3924857 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -70,6 +70,8 @@ mutter_SOURCES= \ core/session.h \ core/stack.c \ core/stack.h \ + core/stack-tracker.c \ + core/stack-tracker.h \ core/util.c \ include/util.h \ core/window-props.c \ diff --git a/src/core/display.c b/src/core/display.c index 9b22dfe8d..d1abfb88b 100644 --- a/src/core/display.c +++ b/src/core/display.c @@ -2019,9 +2019,27 @@ event_callback (XEvent *event, case VisibilityNotify: break; case CreateNotify: + { + MetaScreen *screen; + + screen = meta_display_screen_for_root (display, + event->xcreatewindow.parent); + if (screen) + meta_stack_tracker_create_event (screen->stack_tracker, + &event->xcreatewindow); + } break; case DestroyNotify: + { + MetaScreen *screen; + + screen = meta_display_screen_for_root (display, + event->xdestroywindow.event); + if (screen) + meta_stack_tracker_destroy_event (screen->stack_tracker, + &event->xdestroywindow); + } if (window) { /* FIXME: It sucks that DestroyNotify events don't come with @@ -2135,8 +2153,30 @@ event_callback (XEvent *event, } break; case ReparentNotify: + { + MetaScreen *screen; + + screen = meta_display_screen_for_root (display, + event->xconfigure.event); + if (screen) + { + if (screen) + meta_stack_tracker_reparent_event (screen->stack_tracker, + &event->xreparent); + } + } break; case ConfigureNotify: + if (event->xconfigure.event != event->xconfigure.window) + { + MetaScreen *screen; + + screen = meta_display_screen_for_root (display, + event->xconfigure.event); + if (screen) + meta_stack_tracker_configure_event (screen->stack_tracker, + &event->xconfigure); + } if (window && window->override_redirect) meta_window_configure_notify (window, &event->xconfigure); else diff --git a/src/core/frame.c b/src/core/frame.c index 373382ace..12d84bee9 100644 --- a/src/core/frame.c +++ b/src/core/frame.c @@ -48,6 +48,7 @@ meta_window_ensure_frame (MetaWindow *window) MetaFrame *frame; XSetWindowAttributes attrs; Visual *visual; + gulong create_serial; if (window->frame) return; @@ -105,7 +106,11 @@ meta_window_ensure_frame (MetaWindow *window) frame->rect.y, frame->rect.width, frame->rect.height, - frame->window->screen->number); + frame->window->screen->number, + &create_serial); + meta_stack_tracker_record_add (window->screen->stack_tracker, + frame->xwindow, + create_serial); meta_verbose ("Frame for %s is 0x%lx\n", frame->window->desc, frame->xwindow); attrs.event_mask = EVENT_MASK; @@ -141,6 +146,9 @@ meta_window_ensure_frame (MetaWindow *window) window->rect.x = 0; window->rect.y = 0; + meta_stack_tracker_record_remove (window->screen->stack_tracker, + window->xwindow, + XNextRequest (window->display->xdisplay)); XReparentWindow (window->display->xdisplay, window->xwindow, frame->xwindow, @@ -199,6 +207,9 @@ meta_window_destroy_frame (MetaWindow *window) "Incrementing unmaps_pending on %s for reparent back to root\n", window->desc); window->unmaps_pending += 1; } + meta_stack_tracker_record_add (window->screen->stack_tracker, + window->xwindow, + XNextRequest (window->display->xdisplay)); XReparentWindow (window->display->xdisplay, window->xwindow, window->screen->xroot, diff --git a/src/core/screen-private.h b/src/core/screen-private.h index 81770dad9..101b462e2 100644 --- a/src/core/screen-private.h +++ b/src/core/screen-private.h @@ -36,6 +36,7 @@ #include "display-private.h" #include "screen.h" #include +#include "stack-tracker.h" #include "alttabhandler.h" #include "ui.h" @@ -94,6 +95,7 @@ struct _MetaScreen GList *workspaces; MetaStack *stack; + MetaStackTracker *stack_tracker; MetaCursor current_cursor; diff --git a/src/core/screen.c b/src/core/screen.c index 78938651c..a72a21d2d 100644 --- a/src/core/screen.c +++ b/src/core/screen.c @@ -439,11 +439,15 @@ create_guard_window (Display *xdisplay, MetaScreen *screen) { XSetWindowAttributes attributes; Window guard_window; + gulong create_serial; attributes.event_mask = NoEventMask; attributes.override_redirect = True; attributes.background_pixel = BlackPixel (xdisplay, screen->number); + /* We have to call record_add() after we have the new window ID, + * so save the serial for the CreateWindow request until then */ + create_serial = XNextRequest(xdisplay); guard_window = XCreateWindow (xdisplay, screen->xroot, @@ -457,6 +461,13 @@ create_guard_window (Display *xdisplay, MetaScreen *screen) CopyFromParent, /* visual */ CWEventMask|CWOverrideRedirect|CWBackPixel, &attributes); + meta_stack_tracker_record_add (screen->stack_tracker, + guard_window, + create_serial); + + meta_stack_tracker_record_lower (screen->stack_tracker, + guard_window, + XNextRequest (xdisplay)); XLowerWindow (xdisplay, guard_window); XMapWindow (xdisplay, guard_window); return guard_window; @@ -730,6 +741,7 @@ meta_screen_new (MetaDisplay *display, screen->ws_popup = NULL; screen->stack = meta_stack_new (screen); + screen->stack_tracker = meta_stack_tracker_new (screen); meta_prefs_add_listener (prefs_changed_callback, screen); @@ -807,6 +819,7 @@ meta_screen_free (MetaScreen *screen, meta_ui_free (screen->ui); meta_stack_free (screen->stack); + meta_stack_tracker_free (screen->stack_tracker); meta_error_trap_push_with_return (screen->display); XSelectInput (screen->display->xdisplay, screen->xroot, 0); diff --git a/src/core/stack-tracker.c b/src/core/stack-tracker.c new file mode 100644 index 000000000..8ea85a29a --- /dev/null +++ b/src/core/stack-tracker.c @@ -0,0 +1,630 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +/* + * 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#include + +#include + +#include "screen-private.h" +#include "stack-tracker.h" +#include "util.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) 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; + +/* 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; + } any; + struct { + MetaStackOpType type; + gulong serial; + Window window; + } add; + struct { + MetaStackOpType type; + gulong serial; + Window window; + } remove; + struct { + MetaStackOpType type; + gulong serial; + Window window; + Window sibling; + } raise_above; + struct { + MetaStackOpType type; + gulong serial; + Window window; + Window sibling; + } lower_below; +}; + +struct _MetaStackTracker +{ + MetaScreen *screen; + + /* This is the last state of the stack as based on events received + * from the X server. + */ + GArray *server_stack; + + /* This is the serial of the last request we made that was reflected + * in server_stack + */ + gulong server_serial; + + /* 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 *queued_requests; + + /* This is how we think the stack is, based on server_stack, and + * on requests we've made subsequent to server_stack + */ + GArray *predicted_stack; +}; + +static void +meta_stack_op_dump (MetaStackOp *op, + const char *prefix, + const char *suffix) +{ + switch (op->any.type) + { + case STACK_OP_ADD: + meta_topic (META_DEBUG_STACK, "%sADD(%#lx; %ld)%s", + prefix, op->add.window, op->any.serial, suffix); + break; + case STACK_OP_REMOVE: + meta_topic (META_DEBUG_STACK, "%sREMOVE(%#lx; %ld)%s", + prefix, op->add.window, op->any.serial, suffix); + break; + case STACK_OP_RAISE_ABOVE: + meta_topic (META_DEBUG_STACK, "%sRAISE_ABOVE(%#lx, %#lx; %ld)%s", + prefix, + op->raise_above.window, op->raise_above.sibling, + op->any.serial, + suffix); + break; + case STACK_OP_LOWER_BELOW: + meta_topic (META_DEBUG_STACK, "%sLOWER_BELOW(%#lx, %#lx; %ld)%s", + prefix, + op->lower_below.window, op->lower_below.sibling, + op->any.serial, + suffix); + break; + } +} + +static void +meta_stack_tracker_dump (MetaStackTracker *tracker) +{ + guint i; + GList *l; + + meta_topic (META_DEBUG_STACK, "MetaStackTracker state (screen=%d)\n", tracker->screen->number); + meta_push_no_msg_prefix (); + meta_topic (META_DEBUG_STACK, " server_serial: %ld\n", tracker->server_serial); + meta_topic (META_DEBUG_STACK, " server_stack: "); + for (i = 0; i < tracker->server_stack->len; i++) + meta_topic (META_DEBUG_STACK, " %#lx", g_array_index (tracker->server_stack, Window, i)); + if (tracker->predicted_stack) + { + meta_topic (META_DEBUG_STACK, "\n predicted_stack: "); + for (i = 0; i < tracker->predicted_stack->len; i++) + meta_topic (META_DEBUG_STACK, " %#lx", g_array_index (tracker->predicted_stack, Window, i)); + } + meta_topic (META_DEBUG_STACK, "\n queued_requests: ["); + for (l = tracker->queued_requests->head; l; l = l->next) + { + MetaStackOp *op = l->data; + meta_stack_op_dump (op, "", l->next ? ", " : ""); + } + meta_topic (META_DEBUG_STACK, "]\n"); + meta_pop_no_msg_prefix (); +} + +static void +meta_stack_op_free (MetaStackOp *op) +{ + g_slice_free (MetaStackOp, op); +} + +static int +find_window (GArray *stack, + Window window) +{ + guint i; + + for (i = 0; i < stack->len; i++) + if (g_array_index (stack, Window, i) == window) + return i; + + return -1; +} + +static void +move_window_above (GArray *stack, + Window window, + int old_pos, + int above_pos) +{ + int i; + + if (old_pos < above_pos) + { + for (i = old_pos; i < above_pos; i++) + g_array_index (stack, Window, i) = g_array_index (stack, Window, i + 1); + + g_array_index (stack, Window, above_pos) = window; + } + else if (old_pos > above_pos + 1) + { + for (i = old_pos; i > above_pos + 1; i--) + g_array_index (stack, Window, i) = g_array_index (stack, Window, i - 1); + + g_array_index (stack, Window, above_pos + 1) = window; + } +} + +static void +meta_stack_op_apply (MetaStackOp *op, + GArray *stack) +{ + switch (op->any.type) + { + case STACK_OP_ADD: + { + int old_pos = find_window (stack, op->add.window); + if (old_pos >= 0) + { + g_warning ("STACK_OP_ADD: window %#lx already in stack", + op->add.window); + return; + } + + g_array_append_val (stack, op->add.window); + break; + } + case STACK_OP_REMOVE: + { + int old_pos = find_window (stack, op->remove.window); + if (old_pos < 0) + { + g_warning ("STACK_OP_REMOVE: window %#lx not in stack", + op->remove.window); + return; + } + + g_array_remove_index (stack, old_pos); + break; + } + case STACK_OP_RAISE_ABOVE: + { + int old_pos = find_window (stack, op->raise_above.window); + int above_pos; + if (old_pos < 0) + { + g_warning ("STACK_OP_RAISE_ABOVE: window %#lx not in stack", + op->raise_above.window); + return; + } + + if (op->raise_above.sibling != None) + { + above_pos = find_window (stack, op->raise_above.sibling); + if (above_pos < 0) + { + g_warning ("STACK_OP_RAISE_ABOVE: sibling window %#lx not in stack", + op->raise_above.sibling); + return; + } + } + else + { + above_pos = -1; + } + + move_window_above (stack, op->raise_above.window, old_pos, above_pos); + break; + } + case STACK_OP_LOWER_BELOW: + { + int old_pos = find_window (stack, op->lower_below.window); + int above_pos; + if (old_pos < 0) + { + g_warning ("STACK_OP_LOWER_BELOW: window %#lx not in stack", + op->lower_below.window); + return; + } + + if (op->lower_below.sibling != None) + { + int below_pos = find_window (stack, op->lower_below.sibling); + if (below_pos < 0) + { + g_warning ("STACK_OP_LOWER_BELOW: sibling window %#lx not in stack", + op->lower_below.sibling); + return; + } + + above_pos = below_pos - 1; + } + else + { + above_pos = stack->len - 1; + } + + move_window_above (stack, op->lower_below.window, old_pos, above_pos); + break; + } + } +} + +static GArray * +copy_stack (Window *windows, + guint n_windows) +{ + GArray *stack = g_array_new (FALSE, FALSE, sizeof (Window)); + + g_array_set_size (stack, n_windows); + memcpy (stack->data, windows, sizeof (Window) * n_windows); + + return stack; +} + +MetaStackTracker * +meta_stack_tracker_new (MetaScreen *screen) +{ + MetaStackTracker *tracker; + Window ignored1, ignored2; + Window *children; + guint n_children; + + tracker = g_new0 (MetaStackTracker, 1); + tracker->screen = screen; + + tracker->server_serial = XNextRequest (screen->display->xdisplay); + + XQueryTree (screen->display->xdisplay, + screen->xroot, + &ignored1, &ignored2, &children, &n_children); + tracker->server_stack = copy_stack (children, n_children); + XFree (children); + + tracker->queued_requests = g_queue_new (); + + return tracker; +} + +void +meta_stack_tracker_free (MetaStackTracker *tracker) +{ + g_array_free (tracker->server_stack, TRUE); + if (tracker->predicted_stack) + g_array_free (tracker->predicted_stack, TRUE); + + g_queue_foreach (tracker->queued_requests, (GFunc)meta_stack_op_free, NULL); + g_queue_free (tracker->queued_requests); + tracker->queued_requests = NULL; +} + +static void +stack_tracker_queue_request (MetaStackTracker *tracker, + MetaStackOp *op) +{ + meta_stack_op_dump (op, "Queueing: ", "\n"); + g_queue_push_tail (tracker->queued_requests, op); + if (tracker->predicted_stack) + meta_stack_op_apply (op, tracker->predicted_stack); + meta_stack_tracker_dump (tracker); +} + +void +meta_stack_tracker_record_add (MetaStackTracker *tracker, + Window window, + gulong serial) +{ + MetaStackOp *op = g_slice_new (MetaStackOp); + + op->any.type = STACK_OP_ADD; + op->any.serial = serial; + op->add.window = window; + + stack_tracker_queue_request (tracker, op); +} + +void +meta_stack_tracker_record_remove (MetaStackTracker *tracker, + Window window, + gulong serial) +{ + MetaStackOp *op = g_slice_new (MetaStackOp); + + op->any.type = STACK_OP_REMOVE; + op->any.serial = serial; + op->remove.window = window; + + stack_tracker_queue_request (tracker, op); +} + +void +meta_stack_tracker_record_restack_windows (MetaStackTracker *tracker, + Window *windows, + int n_windows, + gulong serial) +{ + int i; + + /* XRestackWindows() isn't actually a X requests - it's broken down + * by XLib into a series of XConfigureWindow(StackMode=below); we + * mirror that exactly here. + * + * Aside: Having a separate StackOp for this would be possible to + * get some extra efficiency in memory allocation and in applying + * the op, at the expense of a code complexity. Implementation hint + * for that - keep op->restack_window.n_complete, and when receiving + * events with intermediate serials, set n_complete rather than + * removing the op from the queue. + */ + for (i = 0; i < n_windows - 1; i++) + meta_stack_tracker_record_lower_below (tracker, windows[i + 1], windows[i], + serial + i); +} + +void +meta_stack_tracker_record_raise_above (MetaStackTracker *tracker, + Window window, + Window sibling, + gulong serial) +{ + MetaStackOp *op = g_slice_new (MetaStackOp); + + op->any.type = STACK_OP_RAISE_ABOVE; + op->any.serial = serial; + op->raise_above.window = window; + op->raise_above.sibling = sibling; + + stack_tracker_queue_request (tracker, op); +} + +void +meta_stack_tracker_record_lower_below (MetaStackTracker *tracker, + Window window, + Window sibling, + gulong serial) +{ + MetaStackOp *op = g_slice_new (MetaStackOp); + + op->any.type = STACK_OP_LOWER_BELOW; + op->any.serial = serial; + op->lower_below.window = window; + op->lower_below.sibling = sibling; + + stack_tracker_queue_request (tracker, op); +} + +void +meta_stack_tracker_record_lower (MetaStackTracker *tracker, + Window window, + gulong serial) +{ + meta_stack_tracker_record_raise_above (tracker, window, None, serial); +} + +static void +stack_tracker_event_received (MetaStackTracker *tracker, + MetaStackOp *op) +{ + meta_stack_op_dump (op, "Stack op event received: ", "\n"); + + if (op->any.serial < tracker->server_serial) + return; + + tracker->server_serial = op->any.serial; + + meta_stack_op_apply (op, tracker->server_stack); + + while (tracker->queued_requests->head) + { + MetaStackOp *queued_op = tracker->queued_requests->head->data; + if (queued_op->any.serial > op->any.serial) + break; + + g_queue_pop_head (tracker->queued_requests); + meta_stack_op_free (queued_op); + } + + if (tracker->predicted_stack) + { + g_array_free (tracker->predicted_stack, TRUE); + tracker->predicted_stack = NULL; + } + + 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); +} + +/** + * 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 + * + * Returns 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, + Window **windows, + int *n_windows) +{ + GArray *stack; + + if (tracker->queued_requests->length == 0) + { + stack = tracker->server_stack; + } + else + { + if (tracker->predicted_stack == NULL) + { + GList *l; + + tracker->predicted_stack = copy_stack ((Window *)tracker->server_stack->data, + tracker->server_stack->len); + for (l = tracker->queued_requests->head; l; l = l->next) + { + MetaStackOp *op = l->data; + meta_stack_op_apply (op, tracker->predicted_stack); + } + } + + stack = tracker->predicted_stack; + } + + if (windows) + *windows = (Window *)stack->data; + if (n_windows) + *n_windows = stack->len; +} diff --git a/src/core/stack-tracker.h b/src/core/stack-tracker.h new file mode 100644 index 000000000..6df329977 --- /dev/null +++ b/src/core/stack-tracker.h @@ -0,0 +1,86 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +/** + * \file stack-tracker.h 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ + +#ifndef META_STACK_TRACKER_H +#define META_STACK_TRACKER_H + +#include "screen.h" + +typedef struct _MetaStackTracker MetaStackTracker; + +MetaStackTracker *meta_stack_tracker_new (MetaScreen *screen); +void meta_stack_tracker_free (MetaStackTracker *tracker); + +/* These functions are called when we make an X call that changes the + * stacking order; this allows MetaStackTracker to predict stacking + * order before it receives events back from the X server */ +void meta_stack_tracker_record_add (MetaStackTracker *tracker, + Window window, + gulong serial); +void meta_stack_tracker_record_remove (MetaStackTracker *tracker, + Window window, + gulong serial); +void meta_stack_tracker_record_restack_windows (MetaStackTracker *tracker, + Window *windows, + int n_windows, + gulong serial); +void meta_stack_tracker_record_raise_above (MetaStackTracker *tracker, + Window window, + Window sibling, + gulong serial); +void meta_stack_tracker_record_lower_below (MetaStackTracker *tracker, + Window window, + Window sibling, + gulong serial); +void meta_stack_tracker_record_lower (MetaStackTracker *tracker, + Window window, + gulong serial); + +/* These functions are used to update the stack when we get events + * reflecting changes to the stacking order */ +void meta_stack_tracker_create_event (MetaStackTracker *tracker, + XCreateWindowEvent *event); +void meta_stack_tracker_destroy_event (MetaStackTracker *tracker, + XDestroyWindowEvent *event); +void meta_stack_tracker_reparent_event (MetaStackTracker *tracker, + XReparentEvent *event); +void meta_stack_tracker_configure_event (MetaStackTracker *tracker, + XConfigureEvent *event); + +void meta_stack_tracker_get_stack (MetaStackTracker *tracker, + Window **windows, + int *n_windows); + +#endif /* META_STACK_TRACKER_H */ diff --git a/src/core/stack.c b/src/core/stack.c index 04fbe9ea8..f2f36f07f 100644 --- a/src/core/stack.c +++ b/src/core/stack.c @@ -1033,6 +1033,10 @@ raise_window_relative_to_managed_windows (MetaScreen *screen, changes.stack_mode = Above; meta_error_trap_push (screen->display); + meta_stack_tracker_record_raise_above (screen->stack_tracker, + xwindow, + children[i], + XNextRequest (screen->display->xdisplay)); XConfigureWindow (screen->display->xdisplay, xwindow, CWSibling | CWStackMode, @@ -1051,6 +1055,9 @@ raise_window_relative_to_managed_windows (MetaScreen *screen, * to be sure we're below any override redirect windows. */ meta_error_trap_push (screen->display); + meta_stack_tracker_record_lower (screen->stack_tracker, + xwindow, + XNextRequest (screen->display->xdisplay)); XLowerWindow (screen->display->xdisplay, xwindow); meta_error_trap_pop (screen->display, FALSE); @@ -1159,9 +1166,15 @@ stack_sync_to_server (MetaStack *stack) 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); + { + meta_stack_tracker_record_restack_windows (stack->screen->stack_tracker, + (Window *) root_children_stacked->data, + root_children_stacked->len, + XNextRequest (stack->screen->display->xdisplay)); + XRestackWindows (stack->screen->display->xdisplay, + (Window *) root_children_stacked->data, + root_children_stacked->len); + } } else if (root_children_stacked->len > 0) { @@ -1223,7 +1236,10 @@ stack_sync_to_server (MetaStack *stack) meta_topic (META_DEBUG_STACK, "Placing window 0x%lx below 0x%lx\n", *newp, last_window); - + + meta_stack_tracker_record_lower_below (stack->screen->stack_tracker, + *newp, last_window, + XNextRequest (stack->screen->display->xdisplay)); XConfigureWindow (stack->screen->display->xdisplay, *newp, CWSibling | CWStackMode, @@ -1246,13 +1262,23 @@ stack_sync_to_server (MetaStack *stack) */ if (newp != new_stack) --newp; + meta_stack_tracker_record_restack_windows (stack->screen->stack_tracker, + (Window *) newp, new_end - newp, + XNextRequest (stack->screen->display->xdisplay)); XRestackWindows (stack->screen->display->xdisplay, (Window *) newp, new_end - newp); } } /* Push hidden windows to the bottom of the stack under the guard window */ + meta_stack_tracker_record_lower (stack->screen->stack_tracker, + stack->screen->guard_window, + XNextRequest (stack->screen->display->xdisplay)); XLowerWindow (stack->screen->display->xdisplay, stack->screen->guard_window); + meta_stack_tracker_record_restack_windows (stack->screen->stack_tracker, + (Window *)all_hidden->data, + all_hidden->len, + XNextRequest (stack->screen->display->xdisplay)); XRestackWindows (stack->screen->display->xdisplay, (Window *)all_hidden->data, all_hidden->len); diff --git a/src/include/ui.h b/src/include/ui.h index bcfbd9fe0..c66d06f80 100644 --- a/src/include/ui.h +++ b/src/include/ui.h @@ -76,7 +76,8 @@ Window meta_ui_create_frame_window (MetaUI *ui, gint y, gint width, gint height, - gint screen_no); + gint screen_no, + gulong *create_serial); void meta_ui_destroy_frame_window (MetaUI *ui, Window xwindow); void meta_ui_move_resize_frame (MetaUI *ui, diff --git a/src/ui/ui.c b/src/ui/ui.c index f24fb244e..6955112d7 100644 --- a/src/ui/ui.c +++ b/src/ui/ui.c @@ -159,7 +159,8 @@ meta_ui_create_frame_window (MetaUI *ui, gint y, gint width, gint height, - gint screen_no) + gint screen_no, + gulong *create_serial) { GdkDisplay *display = gdk_x11_lookup_xdisplay (xdisplay); GdkScreen *screen = gdk_display_get_screen (display, screen_no); @@ -208,6 +209,12 @@ meta_ui_create_frame_window (MetaUI *ui, attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP; + /* We make an assumption that gdk_window_new() is going to call + * XCreateWindow as it's first operation; this seems to be true currently + * as long as you pass in a colormap. + */ + if (create_serial) + *create_serial = XNextRequest (xdisplay); window = gdk_window_new (gdk_screen_get_root_window(screen), &attrs, attributes_mask);