Commit 07c10039 authored by Tomas Frydrych's avatar Tomas Frydrych
Browse files

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).
parent 273d2135
......@@ -112,6 +112,8 @@ metacity_SOURCES += \
compositor/mutter/mutter-plugin-manager.h \
compositor/mutter/tidy/tidy-texture-frame.c \
compositor/mutter/tidy/tidy-texture-frame.h \
compositor/mutter/tidy/tidy-grid.c \
compositor/mutter/tidy/tidy-grid.h \
include/mutter-plugin.h
endif
......
......@@ -78,6 +78,9 @@ struct _MetaCompositor
MetaWorkspace *from,
MetaWorkspace *to,
MetaMotionDirection direction);
void (*ensure_stack_order) (MetaCompositor *compositor,
MetaScreen *screen);
};
#endif
......@@ -242,3 +242,12 @@ meta_compositor_switch_workspace (MetaCompositor *compositor,
#endif
}
void
meta_compositor_ensure_stack_order (MetaCompositor *compositor,
MetaScreen *screen)
{
#ifdef HAVE_COMPOSITE_EXTENSIONS
if (compositor && compositor->ensure_stack_order)
compositor->ensure_stack_order (compositor, screen);
#endif
}
This diff is collapsed.
......@@ -884,3 +884,13 @@ mutter_plugin_set_stage_input_area (MutterPlugin *plugin,
XFixesSetWindowShapeRegion (xdpy, xstage, ShapeInput, 0, 0, region);
XFixesSetWindowShapeRegion (xdpy, xoverlay, ShapeInput, 0, 0, region);
}
GList *
mutter_plugin_get_windows (MutterPlugin *plugin)
{
MutterPluginPrivate *priv = plugin->manager_private;
MutterPluginManager *plugin_mgr = priv->self;
return mutter_get_windows (plugin_mgr->screen);
}
......@@ -33,6 +33,12 @@
#include <gmodule.h>
#include <string.h>
#include "../tidy/tidy-grid.h"
/* For debugging only */
#include "../../../core/window-private.h"
#include "compositor-mutter.h"
#define DESTROY_TIMEOUT 250
#define MINIMIZE_TIMEOUT 250
#define MAXIMIZE_TIMEOUT 250
......@@ -43,6 +49,10 @@
#define PANEL_SLIDE_THRESHOLD 2
#define PANEL_HEIGHT 40
#define ACTOR_DATA_KEY "MCCP-scratch-actor-data"
#define SWITCHER_CELL_WIDTH 200
#define SWITCHER_CELL_HEIGHT 200
static GQuark actor_data_quark = 0;
typedef struct PluginPrivate PluginPrivate;
......@@ -119,6 +129,8 @@ struct PluginPrivate
ClutterActor *d_overlay ; /* arrow indicator */
ClutterActor *panel;
ClutterActor *switcher;
gboolean debug_mode : 1;
gboolean panel_out : 1;
gboolean panel_out_in_progress : 1;
......@@ -131,6 +143,8 @@ struct PluginPrivate
struct ActorPrivate
{
ClutterActor *orig_parent;
gint orig_x;
gint orig_y;
ClutterTimeline *tml_minimize;
ClutterTimeline *tml_maximize;
......@@ -800,36 +814,225 @@ g_module_check_init (GModule *module)
}
#endif
static void switcher_clone_weak_notify (gpointer data, GObject *object);
static void
switcher_origin_weak_notify (gpointer data, GObject *object)
{
ClutterActor *clone = data;
/*
* The original MutterWindow destroyed; remove the weak reference the
* we added to the clone referencing the original window, then
* destroy the clone.
*/
g_object_weak_unref (G_OBJECT (clone), switcher_clone_weak_notify, object);
clutter_actor_destroy (clone);
}
static void
switcher_clone_weak_notify (gpointer data, GObject *object)
{
ClutterActor *origin = data;
/*
* Clone destroyed -- this function gets only called whent the clone
* is destroyed while the original MutterWindow still exists, so remove
* the weak reference we added on the origin for sake of the clone.
*/
g_object_weak_unref (G_OBJECT (origin), switcher_origin_weak_notify, object);
}
static gboolean
switcher_clone_input_cb (ClutterActor *clone,
ClutterEvent *event,
gpointer data)
{
MutterWindow *mw = data;
MetaWindow *window;
MetaWorkspace *workspace;
printf ("Actor %p (%s) clicked\n",
clone, clutter_actor_get_name (clone));
window = mutter_window_get_meta_window (mw);
workspace = meta_window_get_workspace (window);
meta_workspace_activate_with_focus (workspace, window, event->any.time);
return FALSE;
}
/*
* This is a simple example of how a switcher might access the windows.
*
* Note that we use ClutterCloneTexture hooked up to the texture *inside*
* MutterWindow (with FBO support, we could clone the entire MutterWindow,
* although for the switcher purposes that is probably not what is wanted
* anyway).
*/
static void
hide_switcher (void)
{
MutterPlugin *plugin = get_plugin ();
PluginPrivate *priv = plugin->plugin_private;
if (!priv->switcher)
return;
clutter_actor_destroy (priv->switcher);
priv->switcher = NULL;
}
static void
show_switcher (void)
{
MutterPlugin *plugin = get_plugin ();
PluginPrivate *priv = plugin->plugin_private;
ClutterActor *stage;
GList *l;
ClutterActor *switcher;
TidyGrid *grid;
guint panel_height;
gint panel_y;
switcher = tidy_grid_new ();
grid = TIDY_GRID (switcher);
tidy_grid_set_homogenous_rows (grid, TRUE);
tidy_grid_set_homogenous_columns (grid, TRUE);
tidy_grid_set_column_major (grid, TRUE);
tidy_grid_set_row_gap (grid, CLUTTER_UNITS_FROM_INT (10));
tidy_grid_set_column_gap (grid, CLUTTER_UNITS_FROM_INT (10));
l = mutter_plugin_get_windows (plugin);
while (l)
{
MutterWindow *mw = l->data;
MetaCompWindowType type = mutter_window_get_window_type (mw);
ClutterActor *a = CLUTTER_ACTOR (mw);
ClutterActor *texture;
ClutterActor *clone;
guint w, h;
gdouble s_x, s_y, s;
/*
* Only show regular windows.
*/
if (mutter_window_is_override_redirect (mw) ||
type != META_COMP_WINDOW_NORMAL)
{
l = l->next;
continue;
}
#if 0
printf ("Adding %p:%s\n",
mw,
mutter_window_get_meta_window (mw) ?
mutter_window_get_meta_window (mw)->desc : "unknown");
#endif
texture = mutter_window_get_texture (mw);
clone = clutter_clone_texture_new (CLUTTER_TEXTURE (texture));
clutter_actor_set_name (clone, mutter_window_get_meta_window (mw)->desc);
g_signal_connect (clone,
"button-press-event",
G_CALLBACK (switcher_clone_input_cb), mw);
g_object_weak_ref (G_OBJECT (mw), switcher_origin_weak_notify, clone);
g_object_weak_ref (G_OBJECT (clone), switcher_clone_weak_notify, mw);
/*
* Scale clone to fit the predefined size of the grid cell
*/
clutter_actor_get_size (a, &w, &h);
s_x = (gdouble) SWITCHER_CELL_WIDTH / (gdouble) w;
s_y = (gdouble) SWITCHER_CELL_HEIGHT / (gdouble) h;
s = s_x < s_y ? s_x : s_y;
if (s_x < s_y)
clutter_actor_set_size (clone,
(guint)((gdouble)w * s_x),
(guint)((gdouble)h * s_x));
else
clutter_actor_set_size (clone,
(guint)((gdouble)w * s_y),
(guint)((gdouble)h * s_y));
clutter_actor_set_reactive (clone, TRUE);
clutter_container_add_actor (CLUTTER_CONTAINER (grid), clone);
l = l->next;
}
if (priv->switcher)
hide_switcher ();
priv->switcher = switcher;
panel_height = clutter_actor_get_height (priv->panel);
panel_y = clutter_actor_get_y (priv->panel);
clutter_actor_set_position (switcher, 10, panel_height + panel_y);
stage = mutter_plugin_get_stage (plugin);
clutter_container_add_actor (CLUTTER_CONTAINER (stage), switcher);
}
static void
toggle_switcher ()
{
MutterPlugin *plugin = get_plugin ();
PluginPrivate *priv = plugin->plugin_private;
if (priv->switcher)
hide_switcher ();
else
show_switcher ();
}
static gboolean
stage_input_cb (ClutterActor *stage, ClutterEvent *event, gpointer data)
{
if (event->type == CLUTTER_MOTION)
gboolean capture = GPOINTER_TO_INT (data);
if ((capture && event->type == CLUTTER_MOTION) ||
(!capture && event->type == CLUTTER_BUTTON_PRESS))
{
ClutterMotionEvent *mev = (ClutterMotionEvent *) event;
gint event_y;
MutterPlugin *plugin = get_plugin ();
PluginPrivate *priv = plugin->plugin_private;
if (event->type == CLUTTER_MOTION)
event_y = ((ClutterMotionEvent*)event)->y;
else
event_y = ((ClutterButtonEvent*)event)->y;
if (priv->panel_out_in_progress || priv->panel_back_in_progress)
return FALSE;
if (priv->panel_out)
if (priv->panel_out &&
(event->type == CLUTTER_BUTTON_PRESS || !priv->switcher))
{
guint height = clutter_actor_get_height (priv->panel);
gint x = clutter_actor_get_x (priv->panel);
if (mev->y > (gint)height)
if (event_y > (gint)height)
{
priv->panel_back_in_progress = TRUE;
clutter_effect_move (priv->panel_slide_effect,
priv->panel, x, -height,
on_panel_effect_complete,
GINT_TO_POINTER (FALSE));
priv->panel_out = FALSE;
}
return FALSE;
}
else if (mev->y < PANEL_SLIDE_THRESHOLD)
else if (event_y < PANEL_SLIDE_THRESHOLD)
{
gint x = clutter_actor_get_x (priv->panel);
......@@ -840,11 +1043,20 @@ stage_input_cb (ClutterActor *stage, ClutterEvent *event, gpointer data)
GINT_TO_POINTER (TRUE));
priv->panel_out = TRUE;
return FALSE;
}
}
else if (event->type == CLUTTER_KEY_RELEASE)
{
ClutterKeyEvent *kev = (ClutterKeyEvent *) event;
return FALSE;
g_print ("*** key press event (key:%c) ***\n",
clutter_key_event_symbol (kev));
}
if (!capture && (event->type == CLUTTER_BUTTON_PRESS))
{
toggle_switcher ();
}
return FALSE;
......@@ -964,7 +1176,12 @@ do_init (const char *params)
* children and do not interfere with their event processing.
*/
g_signal_connect (mutter_plugin_get_stage (plugin),
"captured-event", G_CALLBACK (stage_input_cb), NULL);
"captured-event", G_CALLBACK (stage_input_cb),
GINT_TO_POINTER (TRUE));
g_signal_connect (mutter_plugin_get_stage (plugin),
"button-press-event", G_CALLBACK (stage_input_cb),
GINT_TO_POINTER (FALSE));
mutter_plugin_set_stage_input_area (plugin, 0, 0, screen_width, 1);
......
This diff is collapsed.
/*
* Clutter.
*
* An OpenGL based 'interactive canvas' library.
*
* Authored By Matthew Allum <mallum@openedhand.com>
*
* Copyright (C) 2008 OpenedHand
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifndef __TIDY_GRID_H__
#define __TIDY_GRID_H__
#include <clutter/clutter-actor.h>
G_BEGIN_DECLS
#define TIDY_TYPE_GRID (tidy_grid_get_type())
#define TIDY_GRID(obj) \
(G_TYPE_CHECK_INSTANCE_CAST ((obj), \
TIDY_TYPE_GRID, \
TidyGrid))
#define TIDY_GRID_CLASS(klass) \
(G_TYPE_CHECK_CLASS_CAST ((klass), \
TIDY_TYPE_GRID, \
TidyGridClass))
#define TIDY_IS_GRID(obj) \
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
TIDY_TYPE_GRID))
#define TIDY_IS_GRID_CLASS(klass) \
(G_TYPE_CHECK_CLASS_TYPE ((klass), \
TIDY_TYPE_GRID))
#define TIDY_GRID_GET_CLASS(obj) \
(G_TYPE_INSTANCE_GET_CLASS ((obj), \
TIDY_TYPE_GRID, \
TidyGridClass))
typedef struct _TidyGrid TidyGrid;
typedef struct _TidyGridClass TidyGridClass;
typedef struct _TidyGridPrivate TidyGridPrivate;
struct _TidyGridClass
{
ClutterActorClass parent_class;
};
struct _TidyGrid
{
ClutterActor parent;
TidyGridPrivate *priv;
};
GType tidy_grid_get_type (void) G_GNUC_CONST;
ClutterActor *tidy_grid_new (void);
void tidy_grid_set_end_align (TidyGrid *self,
gboolean value);
gboolean tidy_grid_get_end_align (TidyGrid *self);
void tidy_grid_set_homogenous_rows (TidyGrid *self,
gboolean value);
gboolean tidy_grid_get_homogenous_rows (TidyGrid *self);
void tidy_grid_set_homogenous_columns (TidyGrid *self,
gboolean value);
gboolean tidy_grid_get_homogenous_columns (TidyGrid *self);
void tidy_grid_set_column_major (TidyGrid *self,
gboolean value);
gboolean tidy_grid_get_column_major (TidyGrid *self);
void tidy_grid_set_row_gap (TidyGrid *self,
ClutterUnit value);
ClutterUnit tidy_grid_get_row_gap (TidyGrid *self);
void tidy_grid_set_column_gap (TidyGrid *self,
ClutterUnit value);
ClutterUnit tidy_grid_get_column_gap (TidyGrid *self);
void tidy_grid_set_valign (TidyGrid *self,
gdouble value);
gdouble tidy_grid_get_valign (TidyGrid *self);
void tidy_grid_set_halign (TidyGrid *self,
gdouble value);
gdouble tidy_grid_get_halign (TidyGrid *self);
G_END_DECLS
#endif /* __TIDY_GRID_H__ */
......@@ -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)
......
......@@ -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);
......
......@@ -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
......
......@@ -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);