Live previews for hidden windows.

ClutterActors for hidden windows (such windows on different than active
workspaces and windows that are minimized) are available, and reflect the
actual state of the window. This is intended for use in task-switchers etc.

This feature is disabled by default (due to increased demand on resources),
and can be enabled through the metacity/general/live_hidden_windows gconf key.

A trivial sample window switcher is included in the scratch plugin (activated
by clicking on the slide out panel).
This commit is contained in:
Tomas Frydrych
2008-10-24 10:07:24 +01:00
parent 273d213509
commit 07c1003905
22 changed files with 2017 additions and 221 deletions

View File

@ -68,6 +68,8 @@
#define KEY_CLUTTER_PLUGINS "/apps/metacity/general/clutter_plugins"
#endif
#define KEY_LIVE_HIDDEN_WINDOWS "/apps/metacity/general/live_hidden_windows"
#ifdef HAVE_GCONF
static GConfClient *default_client = NULL;
static GList *changes = NULL;
@ -114,6 +116,8 @@ static gboolean clutter_disabled = FALSE;
static GSList *clutter_plugins = NULL;
#endif
static gboolean live_hidden_windows = FALSE;
#ifdef HAVE_GCONF
static gboolean handle_preference_update_enum (const gchar *key, GConfValue *value);
@ -423,6 +427,11 @@ static MetaBoolPreference preferences_bool[] =
FALSE,
},
#endif
{ "/apps/metacity/general/live_hidden_windows",
META_PREF_LIVE_HIDDEN_WINDOWS,
&live_hidden_windows,
FALSE,
},
{ NULL, 0, NULL, FALSE },
};
@ -1827,6 +1836,8 @@ meta_preference_to_string (MetaPreference pref)
case META_PREF_CLUTTER_PLUGINS:
return "CLUTTER_PLUGINS";
#endif
case META_PREF_LIVE_HIDDEN_WINDOWS:
return "LIVE_HIDDEN_WINDOWS";
}
return "(unknown)";
@ -2962,6 +2973,34 @@ meta_prefs_set_clutter_plugins (GSList *list)
}
#endif
gboolean
meta_prefs_get_live_hidden_windows (void)
{
return live_hidden_windows;
}
void
meta_prefs_set_live_hidden_windows (gboolean whether)
{
#ifdef HAVE_GCONF
GError *err = NULL;
gconf_client_set_bool (default_client,
KEY_LIVE_HIDDEN_WINDOWS,
whether,
&err);
if (err)
{
meta_warning (_("Error setting live hidden windows status status: %s\n"),
err->message);
g_error_free (err);
}
#else
live_hidden_windows = whether;
#endif
}
#ifndef HAVE_GCONF
static void
init_button_layout(void)

View File

@ -772,6 +772,17 @@ meta_screen_manage_all_windows (MetaScreen *screen)
}
meta_stack_thaw (screen->stack);
/*
* Because the windows have already been created/mapped/etc, if the compositor
* maintains a separate stack based on ConfigureNotify restack messages, it
* will not necessarily get what it needs; we explicitely notify the
* compositor to fix up its stacking order.
*
* For more on this issue, see comments in meta_window_hide().
*/
if (screen->display->compositor)
meta_compositor_ensure_stack_order (screen->display->compositor, screen);
g_list_foreach (windows, (GFunc)g_free, NULL);
g_list_free (windows);

View File

@ -240,7 +240,13 @@ get_standalone_layer (MetaWindow *window)
{
MetaStackLayer layer;
gboolean focused_transient = FALSE;
if (window->hidden)
{
layer = META_LAYER_DESKTOP;
return layer;
}
switch (window->type)
{
case META_WINDOW_DESKTOP:
@ -329,7 +335,8 @@ compute_layer (MetaWindow *window)
* windows getting in fullscreen layer if any terminal is
* fullscreen.
*/
if (WINDOW_HAS_TRANSIENT_TYPE(window) &&
if (window->layer != META_LAYER_DESKTOP &&
WINDOW_HAS_TRANSIENT_TYPE(window) &&
(window->xtransient_for == None ||
window->transient_parent_is_root_window))
{
@ -854,7 +861,6 @@ stack_do_relayer (MetaStack *stack)
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

View File

@ -150,6 +150,11 @@ struct _MetaWindow
*/
guint mapped : 1;
/* Whether window has been hidden from view by lowering it to the bottom
* of window stack.
*/
guint hidden : 1;
/* Iconic is the state in WM_STATE; happens for workspaces/shading
* in addition to minimize
*/
@ -393,11 +398,6 @@ void meta_window_change_workspace (MetaWindow *window,
void meta_window_stick (MetaWindow *window);
void meta_window_unstick (MetaWindow *window);
void meta_window_activate (MetaWindow *window,
guint32 current_time);
void meta_window_activate_with_workspace (MetaWindow *window,
guint32 current_time,
MetaWorkspace *workspace);
void meta_window_make_fullscreen_internal (MetaWindow *window);
void meta_window_make_fullscreen (MetaWindow *window);
void meta_window_unmake_fullscreen (MetaWindow *window);

View File

@ -472,6 +472,7 @@ meta_window_new_with_attrs (MetaDisplay *display,
window->tab_unminimized = FALSE;
window->iconic = FALSE;
window->mapped = attrs->map_state != IsUnmapped;
window->hidden = 0;
/* if already mapped, no need to worry about focus-on-first-time-showing */
window->showing_for_first_time = !window->mapped;
/* if already mapped we don't want to do the placement thing */
@ -2243,8 +2244,22 @@ meta_window_show (MetaWindow *window)
XMapWindow (window->display->xdisplay, window->xwindow);
meta_error_trap_pop (window->display, FALSE);
did_show = TRUE;
if (window->was_minimized)
window->hidden = FALSE;
}
else if (meta_prefs_get_live_hidden_windows ())
{
if (window->hidden && window->type != META_WINDOW_DESKTOP)
{
window->hidden = FALSE;
meta_stack_freeze (window->screen->stack);
meta_window_update_layer (window);
meta_window_raise (window);
meta_stack_thaw (window->screen->stack);
did_show = TRUE;
}
}
if (did_show && window->was_minimized)
{
MetaRectangle window_rect;
MetaRectangle icon_rect;
@ -2261,7 +2276,6 @@ meta_window_show (MetaWindow *window)
NULL, NULL);
}
}
}
if (window->iconic)
{
@ -2309,33 +2323,83 @@ static void
meta_window_hide (MetaWindow *window)
{
gboolean did_hide;
meta_topic (META_DEBUG_WINDOW_STATE,
"Hiding window %s\n", window->desc);
did_hide = FALSE;
if (window->frame && window->frame->mapped)
{
meta_topic (META_DEBUG_WINDOW_STATE, "Frame actually needs unmap\n");
window->frame->mapped = FALSE;
meta_ui_unmap_frame (window->screen->ui, window->frame->xwindow);
did_hide = TRUE;
}
if (window->mapped)
if (meta_prefs_get_live_hidden_windows ())
{
meta_topic (META_DEBUG_WINDOW_STATE,
"%s actually needs unmap\n", window->desc);
meta_topic (META_DEBUG_WINDOW_STATE,
"Incrementing unmaps_pending on %s for hide\n",
window->desc);
window->mapped = FALSE;
window->unmaps_pending += 1;
meta_error_trap_push (window->display);
XUnmapWindow (window->display->xdisplay, window->xwindow);
meta_error_trap_pop (window->display, FALSE);
gboolean was_mapped;
if (window->hidden)
return;
was_mapped = window->mapped;
if (!was_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);
meta_stack_thaw (window->screen->stack);
/*
* The X server does not implement lower-below semantics for restacking
* windows, only raise-above; consequently each single lower-bottom call
* gets translated to a bunch of raise-above moves, and often there will
* be no ConfigureNotify at all for the window we are lowering (only for
* its siblings). If we mix the lower-bottom sequence of calls with
* mapping of windows, the batch of ConfigureNotify events that is
* generated does not correctly reflect the stack order, and if the
* Compositor relies on these for its own internal stack, it will
* invariably end up with wrong stacking order.
*
* I have not been able to find a way to get this just work so that the
* resulting ConfigureNotify messages would reflect the actual state of
* the stack, so in the special case we map a window while hiding it, we
* explitely notify the compositor that it should ensure its stacking
* matches the cannonical stack of the WM.
*
* NB: this is uncommon, and generally only happens on the WM start up,
* when we are taking over pre-existing windows, so this brute-force
* fix is OK performance wise.
*/
if (!was_mapped && window->display->compositor)
{
meta_compositor_ensure_stack_order (window->display->compositor,
window->screen);
}
}
else
{
if (window->frame && window->frame->mapped)
{
meta_topic (META_DEBUG_WINDOW_STATE, "Frame actually needs unmap\n");
window->frame->mapped = FALSE;
meta_ui_unmap_frame (window->screen->ui, window->frame->xwindow);
did_hide = TRUE;
}
if (window->mapped)
{
meta_topic (META_DEBUG_WINDOW_STATE,
"%s actually needs unmap\n", window->desc);
meta_topic (META_DEBUG_WINDOW_STATE,
"Incrementing unmaps_pending on %s for hide\n",
window->desc);
window->mapped = FALSE;
window->unmaps_pending += 1;
meta_error_trap_push (window->display);
XUnmapWindow (window->display->xdisplay, window->xwindow);
meta_error_trap_pop (window->display, FALSE);
did_hide = TRUE;
}
}
if (!window->iconic)
@ -7974,7 +8038,7 @@ meta_window_update_layer (MetaWindow *window)
meta_stack_freeze (window->screen->stack);
group = meta_window_get_group (window);
if (group)
if (!window->hidden && group)
meta_group_update_layers (group);
else
meta_stack_update_layer (window->screen->stack, window);
@ -8223,3 +8287,9 @@ meta_window_is_on_all_workspaces (MetaWindow *window)
return window->on_all_workspaces;
}
gboolean
meta_window_is_hidden (MetaWindow *window)
{
return window->hidden;
}

View File

@ -65,11 +65,6 @@ void meta_workspace_remove_window (MetaWorkspace *workspace,
MetaWindow *window);
void meta_workspace_relocate_windows (MetaWorkspace *workspace,
MetaWorkspace *new_home);
void meta_workspace_activate_with_focus (MetaWorkspace *workspace,
MetaWindow *focus_this,
guint32 timestamp);
void meta_workspace_activate (MetaWorkspace *workspace,
guint32 timestamp);
GList* meta_workspace_list_windows (MetaWorkspace *workspace);
void meta_workspace_invalidate_work_area (MetaWorkspace *workspace);

View File

@ -306,7 +306,19 @@ meta_workspace_queue_calc_showing (MetaWorkspace *workspace)
tmp = workspace->windows;
while (tmp != NULL)
{
meta_window_queue (tmp->data, META_QUEUE_CALC_SHOWING);
if (meta_prefs_get_live_hidden_windows ())
{
/*
* When we hide rather than unmap windows, we need the show/hide
* status of the window to be recalculated *before* we call the
* compositor switch_workspace hook.
*/
meta_window_calc_showing (tmp->data);
}
else
{
meta_window_queue (tmp->data, META_QUEUE_CALC_SHOWING);
}
tmp = tmp->next;
}