From 0058271aaa5ec3dfeb1512faefe72f1915282e8e Mon Sep 17 00:00:00 2001 From: Robert Bragg Date: Mon, 3 Nov 2008 14:50:22 +0000 Subject: [PATCH] Re-works the approach to supporting live preview to handle stacking. We can't easily use a metacity layer to hide windows as that means we loose our original stacking position. (Metacity's stack positions are only valid within a single layer) We now have a "guard window" per screen that is a fullscreen override redirect that is lowered to the bottom of the stack. Hidden windows now remain in their original layer so the stacking position remains valid, but all hidden windows get XRestacked under the guard window. A new compositor hook is also added to inform it when a window becomes hidden/unhidded, this lets us map/unmap the corresponding actor. missing files in preview commit (TODO: rebase -i and squash this later) --- src/compositor/compositor-private.h | 6 ++ src/compositor/compositor.c | 15 +++- src/compositor/mutter/compositor-mutter.c | 101 ++++++++++++++++++---- src/core/screen-private.h | 5 ++ src/core/screen.c | 35 ++++++++ src/core/stack.c | 52 ++++++++--- src/core/window-private.h | 3 - src/core/window.c | 64 +++++++------- src/include/compositor.h | 1 + 9 files changed, 217 insertions(+), 65 deletions(-) diff --git a/src/compositor/compositor-private.h b/src/compositor/compositor-private.h index 6d0eda278..566598e87 100644 --- a/src/compositor/compositor-private.h +++ b/src/compositor/compositor-private.h @@ -80,7 +80,13 @@ struct _MetaCompositor MetaMotionDirection direction); void (*sync_stack) (MetaCompositor *compositor, + MetaScreen *screen, GList *stack); + + void (*set_window_hidden) (MetaCompositor *compositor, + MetaScreen *screen, + MetaWindow *window, + gboolean hidden); }; #endif diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c index 9817fc315..87692f4e1 100644 --- a/src/compositor/compositor.c +++ b/src/compositor/compositor.c @@ -244,11 +244,24 @@ meta_compositor_switch_workspace (MetaCompositor *compositor, void meta_compositor_sync_stack (MetaCompositor *compositor, + MetaScreen *screen, GList *stack) { #ifdef HAVE_COMPOSITE_EXTENSIONS if (compositor && compositor->sync_stack) - compositor->sync_stack (compositor, stack); + compositor->sync_stack (compositor, screen, stack); +#endif +} + +void +meta_compositor_set_window_hidden (MetaCompositor *compositor, + MetaScreen *screen, + MetaWindow *window, + gboolean hidden) +{ +#ifdef HAVE_COMPOSITE_EXTENSIONS + if (compositor && compositor->set_window_hidden) + compositor->set_window_hidden (compositor, screen, window, hidden); #endif } diff --git a/src/compositor/mutter/compositor-mutter.c b/src/compositor/mutter/compositor-mutter.c index c79459e3d..46b33a616 100644 --- a/src/compositor/mutter/compositor-mutter.c +++ b/src/compositor/mutter/compositor-mutter.c @@ -783,7 +783,7 @@ mutter_window_get_workspace (MutterWindow *mcw) return meta_workspace_index (workspace); } - +gboolean mutter_window_showing_on_its_workspace (MutterWindow *mcw) { if (!mcw) @@ -796,7 +796,7 @@ mutter_window_showing_on_its_workspace (MutterWindow *mcw) return meta_window_showing_on_its_workspace (mcw->priv->window); } -tatic void repair_win (MutterWindow *cw); +static void repair_win (MutterWindow *cw); static void map_win (MutterWindow *cw); static void unmap_win (MutterWindow *cw); @@ -834,7 +834,8 @@ mutter_finish_workspace_switch (MetaCompScreen *info) l = l->prev; } } - + +#if 0 /* * Fix up stacking order in case the plugin messed it up. */ @@ -874,6 +875,7 @@ mutter_finish_workspace_switch (MetaCompScreen *info) l = l->prev; } +#endif /* printf ("... FINISHED DESKTOP SWITCH\n"); */ @@ -1222,7 +1224,22 @@ add_win (MetaScreen *screen, MetaWindow *window, Window xwindow) Display *xdisplay = meta_display_get_xdisplay (display); XWindowAttributes attrs; gulong events_needed; + + /* Note: this blacklist internal windows is copied from screen.c + * Ideally add_win shouldn't be driven by CreateNotify events and + * should instead be an event directly from metacity core. */ + if (xwindow == screen->no_focus_window || + xwindow == screen->flash_window || +#ifdef HAVE_COMPOSITE_EXTENSIONS + xwindow == screen->wm_cm_selection_window || + xwindow == screen->guard_window || +#endif + xwindow == screen->wm_sn_selection_window) { + meta_verbose ("Not managing our own windows\n"); + return; + } + g_printerr ("window =%p\n", window); if (info == NULL) return; @@ -1932,6 +1949,8 @@ clutter_cmp_manage_screen (MetaCompositor *compositor, KeyPressMask | KeyReleaseMask); info->window_group = clutter_group_new (); + g_object_set_property (G_OBJECT (info->window_group), + "show-on-set-parent", FALSE); info->overlay_group = clutter_group_new (); clutter_container_add (CLUTTER_CONTAINER (info->stage), @@ -2285,9 +2304,11 @@ clutter_cmp_switch_workspace (MetaCompositor *compositor, info = meta_screen_get_compositor_data (screen); to_indx = meta_workspace_index (to); from_indx = meta_workspace_index (from); - + if (!meta_prefs_get_live_hidden_windows ()) { + GList *l; + /* * We are in the traditional mode where hidden windows get unmapped, * we need to pre-calculate the map status of each window so that once @@ -2295,14 +2316,12 @@ clutter_cmp_switch_workspace (MetaCompositor *compositor, * (we need to ignore the map notifications during the effect so that * actors do not just disappear while the effect is running). */ - GList *l = info->windows; - - while (l) + for (l = info->windows; l != NULL; l = l->next) { MutterWindow *cw = l->data; - MetaWindow *mw = cw->priv->window; - gboolean sticky; - gint workspace = -1; + MetaWindow *mw = cw->priv->window; + gboolean sticky; + gint workspace = -1; sticky = (!mw || meta_window_is_on_all_workspaces (mw)); @@ -2327,8 +2346,6 @@ clutter_cmp_switch_workspace (MetaCompositor *compositor, cw->priv->needs_unmap = FALSE; } } - - l = l->next; } } @@ -2355,14 +2372,23 @@ clutter_cmp_switch_workspace (MetaCompositor *compositor, static void clutter_cmp_sync_stack (MetaCompositor *compositor, + MetaScreen *screen, GList *stack) { GList *tmp; + MetaCompScreen *info = meta_screen_get_compositor_data (screen); + g_printerr ("----------------------------------------\n"); for (tmp = stack; tmp != NULL; tmp = tmp->next) { MetaWindow *window = tmp->data; MutterWindow *cw = window->compositor_private; + GList *link; + + /* FIXME -debug */ + g_printerr ("sync DEBUG window = %p stack_position=%d\n", + window, window->stack_position); + if (!cw) { meta_verbose ("Failed to find corresponding MutterWindow " @@ -2371,15 +2397,55 @@ clutter_cmp_sync_stack (MetaCompositor *compositor, } #if 0 - /* FIXME: There should be a seperate composite manager hook - * for hiding/unhiding the actor when the window becomes - * hidden or not */ - if (meta_window_is_hidden (window)) + /* This is a failsafe, it shouldn't be needed if everything is + * well behaved, but if some plugin accidentally shows a + * hidden window, this may help. */ + if (window->hidden) clutter_actor_hide (CLUTTER_ACTOR (cw)); #endif clutter_actor_lower_bottom (CLUTTER_ACTOR (cw)); + + /* Also maintain the order of info->windows */ + info->windows = g_list_remove (info->windows, (gconstpointer)cw); + info->windows = g_list_prepend (info->windows, cw); } + +#if 0 + /* FIXME debug */ + { + g_printerr ("----------------------------------------\n"); + MetaWindow *window = stack->data; + MutterWindow *cw = window->compositor_private; + ClutterActor *parent = clutter_actor_get_parent (cw); + for (tmp = clutter_container_get_children (parent); + tmp != NULL; + tmp = tmp->next) + { + g_printerr ("sync DEBUG: %p\n", tmp->data); + } + } +#endif +} + +static void +clutter_cmp_set_window_hidden (MetaCompositor *compositor, + MetaScreen *screen, + MetaWindow *window, + gboolean hidden) +{ + MutterWindow *cw = window->compositor_private; + + g_return_if_fail (cw); + + if (hidden) + { + /* FIXME: There needs to be a way to queue this if there is an effect + * in progress for this window */ + clutter_actor_hide (CLUTTER_ACTOR (cw)); + } + else + clutter_actor_show (CLUTTER_ACTOR (cw)); } static MetaCompositor comp_info = { @@ -2398,7 +2464,8 @@ static MetaCompositor comp_info = { clutter_cmp_unmaximize_window, clutter_cmp_update_workspace_geometry, clutter_cmp_switch_workspace, - clutter_cmp_sync_stack + clutter_cmp_sync_stack, + clutter_cmp_set_window_hidden }; MetaCompositor * diff --git a/src/core/screen-private.h b/src/core/screen-private.h index b622c9ce9..f4aeed17c 100644 --- a/src/core/screen-private.h +++ b/src/core/screen-private.h @@ -132,6 +132,11 @@ struct _MetaScreen /* Managed by compositor.c */ gpointer compositor_data; + + /* Instead of unmapping withdrawn windows we can leave them mapped + * and restack them below a guard window. When using a compositor + * this allows us to provide live previews of unmapped windows */ + Window guard_window; }; MetaScreen* meta_screen_new (MetaDisplay *display, diff --git a/src/core/screen.c b/src/core/screen.c index aeb500500..5d929c4b4 100644 --- a/src/core/screen.c +++ b/src/core/screen.c @@ -311,6 +311,38 @@ reload_xinerama_infos (MetaScreen *screen) g_assert (screen->xinerama_infos != NULL); } +/* The guard window allows us to leave minimized windows mapped so + * that compositor code may provide live previews of them. + * Instead of being unmapped/withdrawn, they get pushed underneath + * the guard window. */ +static Window +create_guard_window (Display *xdisplay, MetaScreen *screen) +{ + XSetWindowAttributes attributes; + Window guard_window; + + attributes.event_mask = NoEventMask; + attributes.override_redirect = True; + attributes.background_pixel = BlackPixel (xdisplay, screen->number); + + guard_window = + XCreateWindow (xdisplay, + screen->xroot, + 0, /* x */ + 0, /* y */ + screen->rect.width, + screen->rect.height, + 0, /* border width */ + CopyFromParent, /* depth */ + CopyFromParent, /* class */ + CopyFromParent, /* visual */ + CWEventMask|CWOverrideRedirect|CWBackPixel, + &attributes); + XLowerWindow (xdisplay, guard_window); + XMapWindow (xdisplay, guard_window); + return guard_window; +} + MetaScreen* meta_screen_new (MetaDisplay *display, int number, @@ -490,6 +522,8 @@ meta_screen_new (MetaDisplay *display, screen->vertical_workspaces = FALSE; screen->starting_corner = META_SCREEN_TOPLEFT; screen->compositor_data = NULL; + + screen->guard_window = create_guard_window (xdisplay, screen); { XFontStruct *font_info; @@ -760,6 +794,7 @@ meta_screen_manage_all_windows (MetaScreen *screen) info->xwindow == screen->flash_window || #ifdef HAVE_COMPOSITE_EXTENSIONS info->xwindow == screen->wm_cm_selection_window || + info->xwindow == screen->guard_window || #endif info->xwindow == screen->wm_sn_selection_window) { meta_verbose ("Not managing our own windows\n"); diff --git a/src/core/stack.c b/src/core/stack.c index 1c1371621..c7b55ab0c 100644 --- a/src/core/stack.c +++ b/src/core/stack.c @@ -35,6 +35,10 @@ #include "prefs.h" #include "workspace.h" +#ifdef HAVE_COMPOSITE_EXTENSIONS +#include "compositor.h" +#endif + #include #define WINDOW_HAS_TRANSIENT_TYPE(w) \ @@ -1057,6 +1061,7 @@ stack_sync_to_server (MetaStack *stack) GArray *stacked; GArray *root_children_stacked; GList *tmp; + GArray *all_hidden; /* Bail out if frozen */ if (stack->freeze_count > 0) @@ -1067,6 +1072,7 @@ stack_sync_to_server (MetaStack *stack) stack_ensure_sorted (stack); meta_compositor_sync_stack (stack->screen->display->compositor, + stack->screen, stack->sorted); /* Create stacked xwindow arrays. @@ -1076,29 +1082,42 @@ stack_sync_to_server (MetaStack *stack) */ stacked = g_array_new (FALSE, FALSE, sizeof (Window)); root_children_stacked = g_array_new (FALSE, FALSE, sizeof (Window)); + all_hidden = g_array_new (FALSE, FALSE, sizeof (Window)); + + /* The screen guard window sits above all hidden windows and acts as + * a barrier to input reaching these windows. */ + g_array_append_val (all_hidden, stack->screen->guard_window); meta_topic (META_DEBUG_STACK, "Top to bottom: "); meta_push_no_msg_prefix (); - - tmp = stack->sorted; - while (tmp != NULL) + + for (tmp = stack->sorted; tmp != NULL; tmp = tmp->next) { - MetaWindow *w; - - w = tmp->data; + MetaWindow *w = tmp->data; + Window top_level_window; + meta_topic (META_DEBUG_STACK, "%u:%d - %s ", + w->layer, w->stack_position, w->desc); + /* 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); + top_level_window = 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; + top_level_window = w->xwindow; + + /* We don't restack hidden windows along with the rest, though they are + * reflected in the _NET hints. Hidden windows all get pushed below + * the screens fullscreen guard_window. */ + if (w->hidden) + { + g_array_append_val (all_hidden, top_level_window); + continue; + } + + /* build XRestackWindows() array from top to bottom */ + g_array_append_val (root_children_stacked, top_level_window); } meta_topic (META_DEBUG_STACK, "\n"); @@ -1216,6 +1235,13 @@ stack_sync_to_server (MetaStack *stack) } } + /* Push hidden windows to the bottom of the stack under the guard window */ + XLowerWindow (stack->screen->display->xdisplay, stack->screen->guard_window); + XRestackWindows (stack->screen->display->xdisplay, + (Window *)all_hidden->data, + all_hidden->len); + g_array_free (all_hidden, TRUE); + 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 diff --git a/src/core/window-private.h b/src/core/window-private.h index 67774bca1..08ba4abdf 100644 --- a/src/core/window-private.h +++ b/src/core/window-private.h @@ -428,9 +428,6 @@ void meta_window_resize_with_gravity (MetaWindow *window, int gravity); -/* Return whether the window would be showing if we were on its workspace */ -gboolean meta_window_showing_on_its_workspace (MetaWindow *window); - /* Return whether the window should be currently mapped */ gboolean meta_window_should_be_showing (MetaWindow *window); diff --git a/src/core/window.c b/src/core/window.c index c0e8fc94f..2169611cc 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -2250,34 +2250,37 @@ meta_window_show (MetaWindow *window) } else if (meta_prefs_get_live_hidden_windows ()) { - if (window->hidden && window->type != META_WINDOW_DESKTOP) + if (window->hidden) { - window->hidden = FALSE; meta_stack_freeze (window->screen->stack); - meta_window_update_layer (window); - meta_window_raise (window); + window->hidden = FALSE; meta_stack_thaw (window->screen->stack); + /* Inform the compositor that the window isn't hidden */ + meta_compositor_set_window_hidden (window->display->compositor, + window->screen, + window, + window->hidden); did_show = TRUE; } } if (did_show && window->was_minimized) - { - MetaRectangle window_rect; - MetaRectangle icon_rect; - - window->was_minimized = FALSE; - - if (meta_window_get_icon_geometry (window, &icon_rect)) - { - meta_window_get_outer_rect (window, &window_rect); - - meta_effect_run_unminimize (window, - &window_rect, - &icon_rect, - NULL, NULL); - } - } + { + MetaRectangle window_rect; + MetaRectangle icon_rect; + + window->was_minimized = FALSE; + + if (meta_window_get_icon_geometry (window, &icon_rect)) + { + meta_window_get_outer_rect (window, &window_rect); + + meta_effect_run_unminimize (window, + &window_rect, + &icon_rect, + NULL, NULL); + } + } if (window->iconic) { @@ -2333,23 +2336,22 @@ meta_window_hide (MetaWindow *window) if (meta_prefs_get_live_hidden_windows ()) { - gboolean was_mapped; - if (window->hidden) return; - was_mapped = window->mapped; - - if (!was_mapped) + if (!window->mapped) meta_window_show (window); - window->hidden = TRUE; - did_hide = TRUE; - meta_stack_freeze (window->screen->stack); - meta_window_update_layer (window); - meta_window_lower (window); + window->hidden = TRUE; meta_stack_thaw (window->screen->stack); + /* Tell the compositor this window is now hidden */ + meta_compositor_set_window_hidden (window->display->compositor, + window->screen, + window, + window->hidden); + + did_hide = TRUE; } else { @@ -8013,7 +8015,7 @@ meta_window_update_layer (MetaWindow *window) meta_stack_freeze (window->screen->stack); group = meta_window_get_group (window); - if (!window->hidden && group) + if (group) meta_group_update_layers (group); else meta_stack_update_layer (window->screen->stack, window); diff --git a/src/include/compositor.h b/src/include/compositor.h index 9687b884d..582ca18ae 100644 --- a/src/include/compositor.h +++ b/src/include/compositor.h @@ -130,6 +130,7 @@ meta_compositor_switch_workspace (MetaCompositor *compositor, void meta_compositor_sync_stack (MetaCompositor *compositor, + MetaScreen *screen, GList *stack); #endif