Basic plugin infastructure and a sample simple plugin.

This commit is contained in:
Tomas Frydrych 2008-09-18 16:09:11 +01:00
parent d509097967
commit 9b3a0d1ad8
34 changed files with 3023 additions and 309 deletions

60
.gitignore vendored Normal file
View File

@ -0,0 +1,60 @@
Makefile
Makefile.in
Makefile.in.in
aclocal.m4
autom4te.cache
compile
config.guess
config.h
config.h.in
config.log
config.status
config.sub
configure
depcomp
install-sh
libtool
ltmain.sh
missing
.deps
src/metacity-wm.desktop
*.o
*.a
*.lo
*.la
.libs
*.swp
tidy-enum-types.[ch]
tidy-marshal.[ch]
stamp-tidy-enum-types.h
stamp-tidy-marshal.h
stamp-h1
*.gmo
*.make
*~
stamp-it
.intltool-merge-cache
POTFILES
50-metacity-desktop-key.xml
50-metacity-key.xml
inlinepixbufs.h
libmetacity-private.pc
metacity
metacity-dialog
metacity-theme-viewer
metacity.desktop
metacity.schemas
testasyncgetprop
testboxes
testgradient
metacity-grayscale
metacity-mag
metacity-message
metacity-window-demo
focus-window
test-gravity
test-resizing
test-size-hints
wm-tester
INSTALL
mkinstalldirs

View File

@ -8,6 +8,9 @@ m4_define([metacity_micro_version], [2])
m4_define([metacity_version], m4_define([metacity_version],
[metacity_major_version.metacity_minor_version.metacity_micro_version]) [metacity_major_version.metacity_minor_version.metacity_micro_version])
m4_define([metacity_clutter_plugin_api_version], [1])
AC_INIT([metacity], [metacity_version], AC_INIT([metacity], [metacity_version],
[http://bugzilla.gnome.org/enter_bug.cgi?product=metacity]) [http://bugzilla.gnome.org/enter_bug.cgi?product=metacity])
@ -17,6 +20,15 @@ AC_CONFIG_HEADERS(config.h)
AM_INIT_AUTOMAKE AM_INIT_AUTOMAKE
AM_MAINTAINER_MODE AM_MAINTAINER_MODE
METACITY_MAJOR_VERSION=metacity_major_version
METACITY_MINOR_VERSION=metacity_minor_version
METACITY_MICRO_VERSION=metacity_micro_version
METACITY_CLUTTER_PLUGIN_API_VERSION=metacity_clutter_plugin_api_version
AC_SUBST(METACITY_MAJOR_VERSION)
AC_SUBST(METACITY_MINOR_VERSION)
AC_SUBST(METACITY_MICRO_VERSION)
AC_SUBST(METACITY_CLUTTER_PLUGIN_API_VERSION)
# Honor aclocal flags # Honor aclocal flags
AC_SUBST(ACLOCAL_AMFLAGS, "\${ACLOCAL_FLAGS}") AC_SUBST(ACLOCAL_AMFLAGS, "\${ACLOCAL_FLAGS}")
@ -290,6 +302,7 @@ fi
if test x$have_clutter = xyes; then if test x$have_clutter = xyes; then
CLUTTER_PACKAGE=clutter-0.8 CLUTTER_PACKAGE=clutter-0.8
METACITY_PC_MODULES="$METACITY_PC_MODULES $CLUTTER_PACKAGE " METACITY_PC_MODULES="$METACITY_PC_MODULES $CLUTTER_PACKAGE "
PKG_CHECK_MODULES(CLUTTER, $CLUTTER_PACKAGE)
AC_DEFINE(WITH_CLUTTER, , [Building with Clutter compositor]) AC_DEFINE(WITH_CLUTTER, , [Building with Clutter compositor])
fi fi
@ -509,6 +522,7 @@ src/wm-tester/Makefile
src/libmetacity-private.pc src/libmetacity-private.pc
src/tools/Makefile src/tools/Makefile
src/themes/Makefile src/themes/Makefile
src/compositor/clutter-plugins/Makefile
po/Makefile.in po/Makefile.in
]) ])

View File

@ -2,7 +2,11 @@ lib_LTLIBRARIES = libmetacity-private.la
SUBDIRS=wm-tester tools themes SUBDIRS=wm-tester tools themes
INCLUDES=@METACITY_CFLAGS@ -I $(srcdir)/include -DMETACITY_LIBEXECDIR=\"$(libexecdir)\" -DHOST_ALIAS=\"@HOST_ALIAS@\" -DMETACITY_LOCALEDIR=\"$(prefix)/@DATADIRNAME@/locale\" -DMETACITY_PKGDATADIR=\"$(pkgdatadir)\" -DMETACITY_DATADIR=\"$(datadir)\" -DG_LOG_DOMAIN=\"metacity\" -DSN_API_NOT_YET_FROZEN=1 if WITH_CLUTTER
SUBDIRS += compositor/clutter-plugins
endif
INCLUDES=@METACITY_CFLAGS@ -I $(srcdir)/include -DMETACITY_LIBEXECDIR=\"$(libexecdir)\" -DHOST_ALIAS=\"@HOST_ALIAS@\" -DMETACITY_LOCALEDIR=\"$(prefix)/@DATADIRNAME@/locale\" -DMETACITY_PKGDATADIR=\"$(pkgdatadir)\" -DMETACITY_DATADIR=\"$(datadir)\" -DG_LOG_DOMAIN=\"metacity\" -DSN_API_NOT_YET_FROZEN=1 -DMETACITY_MAJOR_VERSION=$(METACITY_MAJOR_VERSION) -DMETACITY_MINOR_VERSION=$(METACITY_MINOR_VERSION) -DMETACITY_MICRO_VERSION=$(METACITY_MICRO_VERSION) -DMETACITY_CLUTTER_PLUGIN_API_VERSION=$(METACITY_CLUTTER_PLUGIN_API_VERSION) -DMETACITY_PKGLIBDIR=\"$(pkglibdir)\"
metacity_SOURCES= \ metacity_SOURCES= \
core/async-getprop.c \ core/async-getprop.c \
@ -100,7 +104,10 @@ metacity_SOURCES= \
if WITH_CLUTTER if WITH_CLUTTER
metacity_SOURCES += compositor/compositor-clutter.c \ metacity_SOURCES += compositor/compositor-clutter.c \
compositor/compositor-clutter.h compositor/compositor-clutter.h \
include/compositor-clutter-plugin.h \
compositor/compositor-clutter-plugin-manager.c \
compositor/compositor-clutter-plugin-manager.h
endif endif
# by setting libmetacity_private_la_CFLAGS, the files shared with # by setting libmetacity_private_la_CFLAGS, the files shared with

View File

@ -0,0 +1,21 @@
pkglibdir=$(libdir)/@PACKAGE@/plugins/clutter
if WITH_CLUTTER
INCLUDES=@METACITY_CFLAGS@ -I $(top_srcdir)/src/include -DMETACITY_LIBEXECDIR=\"$(libexecdir)\" -DHOST_ALIAS=\"@HOST_ALIAS@\" -DMETACITY_LOCALEDIR=\"$(prefix)/@DATADIRNAME@/locale\" -DMETACITY_PKGDATADIR=\"$(pkgdatadir)\" -DMETACITY_DATADIR=\"$(datadir)\" -DG_LOG_DOMAIN=\"metacity\" -DSN_API_NOT_YET_FROZEN=1 -DMETACITY_MAJOR_VERSION=$(METACITY_MAJOR_VERSION) -DMETACITY_MINOR_VERSION=$(METACITY_MINOR_VERSION) -DMETACITY_MICRO_VERSION=$(METACITY_MICRO_VERSION) -DMETACITY_CLUTTER_PLUGIN_API_VERSION=$(METACITY_CLUTTER_PLUGIN_API_VERSION) -DMETACITY_PKGLIBDIR=\"$(pkglibdir)\"
simple_la_CFLAGS = -fPIC
simple_la_SOURCES = simple.c
simple_la_LDFLAGS = -module -avoid-version
simple_la_LIBADD = @CLUTTER_LIBS@
pkglib_LTLIBRARIES = simple.la
# post-install hook to remove the .la and .a files we are not interested in
# (There is no way to stop libtool generating static libs locally, and we
# cannot do this globally because of libmetacity-private.so).
install-exec-hook:
rm $(pkglibdir)/*.a
rm $(pkglibdir)/*.la
endif

View File

@ -0,0 +1,41 @@
Plugins implement effects associated with WM events, such as window map,
minimizing, maximizing, unmaximizing, destruction and workspace switching. The
plugin API is documented in src/include/compositor-clutter-plugin.h; in
addition the simple plugin can be used as a reference implementation.
The API is intended to be generic, exposing no implementation details of the WM
to the plugins; this will facilitate reuse without modification with another WM
(there are plans to use the same plugin API with Matchbox 2).
Multiple plugins can implement the same effect and be loaded at the same time;
however, stacking arbitrary effects in this way might not work as expected;
this is particularly true of more complex effects, such as those for workspace
switching.
Plugins are installed in ${prefix}/lib/metacity/plugins/clutter; from there the
WM will load plugins listed in the clutter_plugins key in the Metacity gconf
general preferences group. Each entry in preferences has the format
'name: optional parameters'
where 'name' is the name of the library without the .so suffix.
As noted above, additional parameters can be passed to the plugin via the
preference key. In such case, the plugin name is immediately followed by a
colon, separating it from the parameters. Two common parameters should be
handled by all plugins:
'debug' indicates that the plugin is run in a debug mode (what exactly that
means is left to the plugin to determine).
'disable' parameter indicates which effects within the plugin should be
disabled; the format of the disable parameter is
'disable: effect1[, effect2];'
where effect1, etc., matches the effects listed in the
compositor-clutter-plugin.h file (currently one of 'map', 'destroy',
'maximize', 'unmaximize', 'switch-workspace'). Example 'disable:
minimize, maximize;'.

View File

@ -0,0 +1,811 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (c) 2008 Intel Corp.
*
* Author: Tomas Frydrych <tf@linux.intel.com>
*
* 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.
*/
#define META_COMPOSITOR_CLUTTER_BUILDING_PLUGIN 1
#include "compositor-clutter-plugin.h"
#include <libintl.h>
#define _(x) dgettext (GETTEXT_PACKAGE, x)
#define N_(x) x
#include <clutter/clutter.h>
#include <gmodule.h>
#include <string.h>
#define DESTROY_TIMEOUT 600
#define MINIMIZE_TIMEOUT 600
#define MAXIMIZE_TIMEOUT 600
#define MAP_TIMEOUT 600
#define SWITCH_TIMEOUT 1000
#define ACTOR_DATA_KEY "MCCP-Simple-actor-data"
typedef struct PluginPrivate PluginPrivate;
typedef struct ActorPrivate ActorPrivate;
static void minimize (ClutterActor *actor, MetaCompWindowType type,
gint workspace);
static void map (ClutterActor *actor, MetaCompWindowType type,
gint workspace);
static void destroy (ClutterActor *actor, MetaCompWindowType type,
gint workspace);
static void maximize (ClutterActor *actor, MetaCompWindowType type,
gint workspace,
gint x, gint y, gint width, gint height);
static void unmaximize (ClutterActor *actor, MetaCompWindowType type,
gint workspace,
gint x, gint y, gint width, gint height);
static void switch_workspace (const GList **actors, gint from, gint to);
static void kill_effect (ClutterActor *actor, gulong event);
static gboolean reload (void);
/*
* First we create the header struct and initialize its static members.
* Any dynamically allocated data should be initialized in the
* init () function below.
*/
MetaCompositorClutterPlugin META_COMPOSITOR_CLUTTER_PLUGIN_STRUCT =
{
/*
* These are predefined values; do not modify.
*/
.version_major = METACITY_MAJOR_VERSION,
.version_minor = METACITY_MINOR_VERSION,
.version_micro = METACITY_MICRO_VERSION,
.version_api = METACITY_CLUTTER_PLUGIN_API_VERSION,
/* Human readable name (for use in UI) */
.name = "Simple Effects",
/* Which types of events this plugin supports */
.features = META_COMPOSITOR_CLUTTER_PLUGIN_MINIMIZE |
META_COMPOSITOR_CLUTTER_PLUGIN_DESTROY |
META_COMPOSITOR_CLUTTER_PLUGIN_MAP |
META_COMPOSITOR_CLUTTER_PLUGIN_MAXIMIZE |
META_COMPOSITOR_CLUTTER_PLUGIN_UNMAXIMIZE |
META_COMPOSITOR_CLUTTER_PLUGIN_SWITCH_WORKSPACE,
/* And the corresponding handlers */
.minimize = minimize,
.destroy = destroy,
.map = map,
.maximize = maximize,
.unmaximize = unmaximize,
.switch_workspace = switch_workspace,
.kill_effect = kill_effect,
/* The reload handler */
.reload = reload
};
/*
* Plugin private data that we store in the .plugin_private member.
*/
struct PluginPrivate
{
ClutterEffectTemplate *destroy_effect;
ClutterEffectTemplate *minimize_effect;
ClutterEffectTemplate *maximize_effect;
ClutterEffectTemplate *map_effect;
ClutterEffectTemplate *switch_workspace_effect;
/* Valid only when switch_workspace effect is in progress */
ClutterTimeline *tml_switch_workspace1;
ClutterTimeline *tml_switch_workspace2;
GList **actors;
ClutterActor *desktop1;
ClutterActor *desktop2;
gboolean debug_mode : 1;
};
/*
* Per actor private data we attach to each actor.
*/
struct ActorPrivate
{
ClutterActor *orig_parent;
gint workspace;
ClutterTimeline *tml_minimize;
ClutterTimeline *tml_maximize;
ClutterTimeline *tml_destroy;
ClutterTimeline *tml_map;
gboolean is_minimized : 1;
gboolean is_maximized : 1;
};
/*
* Actor private data accessor
*/
static ActorPrivate *
get_actor_private (ClutterActor *actor)
{
ActorPrivate * priv = g_object_get_data (G_OBJECT (actor), ACTOR_DATA_KEY);
if (!priv)
{
priv = g_new0 (ActorPrivate, 1);
g_object_set_data_full (G_OBJECT (actor), ACTOR_DATA_KEY, priv, g_free);
}
return priv;
}
static void
on_switch_workspace_effect_complete (ClutterActor *group, gpointer data)
{
MetaCompositorClutterPlugin *plugin = &META_COMPOSITOR_CLUTTER_PLUGIN_STRUCT;
PluginPrivate *ppriv = plugin->plugin_private;
GList *l = *((GList**)data);
ClutterActor *actor_for_cb = l->data;
while (l)
{
ClutterActor *a = l->data;
ActorPrivate *priv = get_actor_private (a);
if (priv->orig_parent)
{
clutter_actor_reparent (a, priv->orig_parent);
priv->orig_parent = NULL;
}
l = l->next;
}
clutter_actor_destroy (ppriv->desktop1);
clutter_actor_destroy (ppriv->desktop2);
ppriv->actors = NULL;
ppriv->tml_switch_workspace1 = NULL;
ppriv->tml_switch_workspace2 = NULL;
ppriv->desktop1 = NULL;
ppriv->desktop2 = NULL;
if (plugin->completed)
plugin->completed (plugin, actor_for_cb,
META_COMPOSITOR_CLUTTER_PLUGIN_SWITCH_WORKSPACE);
}
static void
switch_workspace (const GList **actors, gint from, gint to)
{
MetaCompositorClutterPlugin *plugin = &META_COMPOSITOR_CLUTTER_PLUGIN_STRUCT;
PluginPrivate *ppriv = plugin->plugin_private;
GList *l;
gint n_workspaces;
ClutterActor *group1 = clutter_group_new ();
ClutterActor *group2 = clutter_group_new ();
ClutterActor *stage = plugin->stage;
#if 1
clutter_actor_set_anchor_point (group2,
plugin->screen_width,
plugin->screen_height);
clutter_actor_set_position (group2,
plugin->screen_width,
plugin->screen_height);
#endif
clutter_actor_set_scale (group2, 0.0, 0.0);
clutter_container_add_actor (CLUTTER_CONTAINER (stage), group2);
clutter_container_add_actor (CLUTTER_CONTAINER (stage), group1);
if (from == to)
{
if (plugin->completed)
plugin->completed (plugin, NULL,
META_COMPOSITOR_CLUTTER_PLUGIN_SWITCH_WORKSPACE);
return;
}
n_workspaces = g_list_length (plugin->work_areas);
l = g_list_last (*((GList**) actors));
while (l)
{
ClutterActor *a = l->data;
ActorPrivate *priv = get_actor_private (a);
gint workspace;
workspace = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (a),
META_COMPOSITOR_CLUTTER_PLUGIN_WORKSPACE_KEY));
priv->workspace = workspace;
if (workspace == to || workspace == from)
{
gint x, y;
guint w, h;
clutter_actor_get_position (a, &x, &y);
clutter_actor_get_size (a, &w, &h);
priv->orig_parent = clutter_actor_get_parent (a);
clutter_actor_reparent (a, workspace == to ? group2 : group1);
clutter_actor_show_all (a);
clutter_actor_raise_top (a);
}
else if (workspace < 0)
{
/* Sticky window */
priv->orig_parent = NULL;
}
else
{
/* Window on some other desktop */
clutter_actor_hide (a);
priv->orig_parent = NULL;
}
l = l->prev;
}
ppriv->actors = (GList **)actors;
ppriv->desktop1 = group1;
ppriv->desktop2 = group2;
ppriv->tml_switch_workspace2 = clutter_effect_scale (
ppriv->switch_workspace_effect,
group2, 1.0, 1.0,
on_switch_workspace_effect_complete,
(gpointer)actors);
ppriv->tml_switch_workspace1 = clutter_effect_scale (
ppriv->switch_workspace_effect,
group1, 0.0, 0.0,
NULL, NULL);
}
/*
* Minimize effect completion callback; this function restores actor state, and
* calls the manager callback function.
*/
static void
on_minimize_effect_complete (ClutterActor *actor, gpointer data)
{
/*
* Must reverse the effect of the effect; must hide it first to ensure
* that the restoration will not be visible.
*/
MetaCompositorClutterPlugin *plugin = &META_COMPOSITOR_CLUTTER_PLUGIN_STRUCT;
ActorPrivate *apriv = get_actor_private (actor);
apriv->tml_minimize = NULL;
clutter_actor_hide (actor);
clutter_actor_set_scale (actor, 1.0, 1.0);
clutter_actor_move_anchor_point_from_gravity (actor,
CLUTTER_GRAVITY_NORTH_WEST);
/* Decrease the running effect counter */
plugin->running--;
/* Now notify the manager that we are done with this effect */
if (plugin->completed)
plugin->completed (plugin, actor, META_COMPOSITOR_CLUTTER_PLUGIN_MINIMIZE);
}
/*
* Simple minimize handler: it applies a scale effect (which must be reversed on
* completion).
*/
static void
minimize (ClutterActor *actor, MetaCompWindowType type, gint workspace)
{
MetaCompositorClutterPlugin *plugin = &META_COMPOSITOR_CLUTTER_PLUGIN_STRUCT;
PluginPrivate *priv = plugin->plugin_private;
if (type == META_COMP_WINDOW_NORMAL)
{
ActorPrivate *apriv = get_actor_private (actor);
apriv->is_minimized = TRUE;
clutter_actor_move_anchor_point_from_gravity (actor,
CLUTTER_GRAVITY_CENTER);
META_COMPOSITOR_CLUTTER_PLUGIN_STRUCT.running++;
apriv->tml_minimize = clutter_effect_scale (priv->minimize_effect,
actor,
0.0,
0.0,
(ClutterEffectCompleteFunc)
on_minimize_effect_complete,
NULL);
}
else if (plugin->completed)
plugin->completed (plugin, actor, META_COMPOSITOR_CLUTTER_PLUGIN_MINIMIZE);
}
/*
* Minimize effect completion callback; this function restores actor state, and
* calls the manager callback function.
*/
static void
on_maximize_effect_complete (ClutterActor *actor, gpointer data)
{
/*
* Must reverse the effect of the effect.
*/
MetaCompositorClutterPlugin *plugin = &META_COMPOSITOR_CLUTTER_PLUGIN_STRUCT;
ActorPrivate *apriv = get_actor_private (actor);
apriv->tml_maximize = NULL;
clutter_actor_set_scale (actor, 1.0, 1.0);
clutter_actor_move_anchor_point_from_gravity (actor,
CLUTTER_GRAVITY_NORTH_WEST);
/* Decrease the running effect counter */
plugin->running--;
/* Now notify the manager that we are done with this effect */
if (plugin->completed)
plugin->completed (plugin, actor, META_COMPOSITOR_CLUTTER_PLUGIN_MAXIMIZE);
}
/*
* The Nature of Maximize operation is such that it is difficult to do a visual
* effect that would work well. Scaling, the obvious effect, does not work that
* well, because at the end of the effect we end up with window content bigger
* and differently laid out than in the real window; this is a proof concept.
*
* (Something like a sound would be more appropriate.)
*/
static void
maximize (ClutterActor *actor, MetaCompWindowType type, gint workspace,
gint end_x, gint end_y, gint end_width, gint end_height)
{
MetaCompositorClutterPlugin *plugin = &META_COMPOSITOR_CLUTTER_PLUGIN_STRUCT;
PluginPrivate *priv = plugin->plugin_private;
gdouble scale_x = 1.0;
gdouble scale_y = 1.0;
gint anchor_x = 0;
gint anchor_y = 0;
if (type == META_COMP_WINDOW_NORMAL)
{
ActorPrivate *apriv = get_actor_private (actor);
guint width, height;
gint x, y;
apriv->is_maximized = TRUE;
clutter_actor_get_size (actor, &width, &height);
clutter_actor_get_position (actor, &x, &y);
/*
* Work out the scale and anchor point so that the window is expanding
* smoothly into the target size.
*/
scale_x = (gdouble)end_width / (gdouble) width;
scale_y = (gdouble)end_height / (gdouble) height;
anchor_x = (gdouble)(x - end_x)*(gdouble)width /
((gdouble)(end_width - width));
anchor_y = (gdouble)(y - end_y)*(gdouble)height /
((gdouble)(end_height - height));
clutter_actor_move_anchor_point (actor, anchor_x, anchor_y);
apriv->tml_maximize = clutter_effect_scale (priv->maximize_effect,
actor,
scale_x,
scale_y,
(ClutterEffectCompleteFunc)
on_maximize_effect_complete,
NULL);
return;
}
if (plugin->completed)
plugin->completed (plugin, actor, META_COMPOSITOR_CLUTTER_PLUGIN_MAXIMIZE);
}
/*
* See comments on the maximize() function.
*
* (Just a skeleton code.)
*/
static void
unmaximize (ClutterActor *actor, MetaCompWindowType type, gint workspace,
gint end_x, gint end_y, gint end_width, gint end_height)
{
MetaCompositorClutterPlugin *plugin = &META_COMPOSITOR_CLUTTER_PLUGIN_STRUCT;
if (type == META_COMP_WINDOW_NORMAL)
{
ActorPrivate *apriv = get_actor_private (actor);
apriv->is_maximized = FALSE;
printf ("Doing unmaximize to target %d,%d;%dx%d\n",
end_x, end_y, end_width, end_height);
}
/* Do this conditionally, if the effect requires completion callback. */
plugin->completed (plugin, actor, META_COMPOSITOR_CLUTTER_PLUGIN_UNMAXIMIZE);
}
static void
on_map_effect_complete (ClutterActor *actor, gpointer data)
{
/*
* Must reverse the effect of the effect.
*/
MetaCompositorClutterPlugin *plugin = &META_COMPOSITOR_CLUTTER_PLUGIN_STRUCT;
ActorPrivate *apriv = get_actor_private (actor);
apriv->tml_map = NULL;
clutter_actor_move_anchor_point_from_gravity (actor,
CLUTTER_GRAVITY_NORTH_WEST);
/* Decrease the running effect counter */
plugin->running--;
/* Now notify the manager that we are done with this effect */
if (plugin->completed)
plugin->completed (plugin, actor, META_COMPOSITOR_CLUTTER_PLUGIN_MAP);
}
/*
* Simple map handler: it applies a scale effect which must be reversed on
* completion).
*/
static void
map (ClutterActor *actor, MetaCompWindowType type, gint workspace)
{
MetaCompositorClutterPlugin *plugin = &META_COMPOSITOR_CLUTTER_PLUGIN_STRUCT;
PluginPrivate *priv = plugin->plugin_private;
if (type == META_COMP_WINDOW_NORMAL)
{
ActorPrivate *apriv = get_actor_private (actor);
clutter_actor_move_anchor_point_from_gravity (actor,
CLUTTER_GRAVITY_CENTER);
META_COMPOSITOR_CLUTTER_PLUGIN_STRUCT.running++;
clutter_actor_set_scale (actor, 0.0, 0.0);
clutter_actor_show (actor);
apriv->tml_map = clutter_effect_scale (priv->map_effect,
actor,
1.0,
1.0,
(ClutterEffectCompleteFunc)
on_map_effect_complete,
NULL);
apriv->is_minimized = FALSE;
}
else if (plugin->completed)
plugin->completed (plugin, actor, META_COMPOSITOR_CLUTTER_PLUGIN_MAP);
}
/*
* Destroy effect completion callback; this is a simple effect that requires no
* further action than decreasing the running effect counter and notifying the
* manager that the effect is completed.
*/
static void
on_destroy_effect_complete (ClutterActor *actor, gpointer data)
{
MetaCompositorClutterPlugin *plugin = &META_COMPOSITOR_CLUTTER_PLUGIN_STRUCT;
ActorPrivate *apriv = get_actor_private (actor);
apriv->tml_destroy = NULL;
plugin->running--;
if (plugin->completed)
plugin->completed (plugin, actor, META_COMPOSITOR_CLUTTER_PLUGIN_DESTROY);
}
/*
* Simple TV-out like effect.
*/
static void
destroy (ClutterActor *actor, MetaCompWindowType type, gint workspace)
{
MetaCompositorClutterPlugin *plugin = &META_COMPOSITOR_CLUTTER_PLUGIN_STRUCT;
PluginPrivate *priv = plugin->plugin_private;
if (type == META_COMP_WINDOW_NORMAL)
{
ActorPrivate *apriv = get_actor_private (actor);
clutter_actor_move_anchor_point_from_gravity (actor,
CLUTTER_GRAVITY_CENTER);
plugin->running++;
apriv->tml_destroy = clutter_effect_scale (priv->destroy_effect,
actor,
1.0,
0.0,
(ClutterEffectCompleteFunc)
on_destroy_effect_complete,
NULL);
}
else if (plugin->completed)
plugin->completed (plugin, actor, META_COMPOSITOR_CLUTTER_PLUGIN_DESTROY);
}
static void
kill_effect (ClutterActor *actor, gulong event)
{
MetaCompositorClutterPlugin *plugin = &META_COMPOSITOR_CLUTTER_PLUGIN_STRUCT;
ActorPrivate *apriv;
if (!(plugin->features & event))
{
/* Event we do not support */
return;
}
if (event & META_COMPOSITOR_CLUTTER_PLUGIN_SWITCH_WORKSPACE)
{
PluginPrivate *ppriv = plugin->plugin_private;
if (ppriv->tml_switch_workspace1)
{
clutter_timeline_stop (ppriv->tml_switch_workspace1);
clutter_timeline_stop (ppriv->tml_switch_workspace2);
on_switch_workspace_effect_complete (ppriv->desktop1, ppriv->actors);
}
if (!(event & ~META_COMPOSITOR_CLUTTER_PLUGIN_SWITCH_WORKSPACE))
{
/* Workspace switch only, nothing more to do */
return;
}
}
apriv = get_actor_private (actor);
if ((event & META_COMPOSITOR_CLUTTER_PLUGIN_MINIMIZE) && apriv->tml_minimize)
{
clutter_timeline_stop (apriv->tml_minimize);
on_minimize_effect_complete (actor, NULL);
}
if ((event & META_COMPOSITOR_CLUTTER_PLUGIN_MAXIMIZE) && apriv->tml_maximize)
{
clutter_timeline_stop (apriv->tml_maximize);
on_maximize_effect_complete (actor, NULL);
}
if ((event & META_COMPOSITOR_CLUTTER_PLUGIN_MAP) && apriv->tml_map)
{
clutter_timeline_stop (apriv->tml_map);
on_map_effect_complete (actor, NULL);
}
if ((event & META_COMPOSITOR_CLUTTER_PLUGIN_DESTROY) && apriv->tml_destroy)
{
clutter_timeline_stop (apriv->tml_destroy);
on_destroy_effect_complete (actor, NULL);
}
}
#if 0
const gchar * g_module_check_init (GModule *module);
const gchar *
g_module_check_init (GModule *module)
{
/*
* Unused; left here for documentation purposes.
*
* NB: this function is called *before* the plugin manager does its own
* initialization of the plugin struct, so you cannot process fields
* like .params in here; use the init function below instead.
*/
return NULL;
}
#endif
/*
* Core of the plugin init function, called for initial initialization and
* by the reload() function. Returns TRUE on success.
*/
static gboolean
do_init ()
{
MetaCompositorClutterPlugin *plugin = &META_COMPOSITOR_CLUTTER_PLUGIN_STRUCT;
PluginPrivate *priv = g_new0 (PluginPrivate, 1);
const gchar *params;
guint destroy_timeout = DESTROY_TIMEOUT;
guint minimize_timeout = MINIMIZE_TIMEOUT;
guint maximize_timeout = MAXIMIZE_TIMEOUT;
guint map_timeout = MAP_TIMEOUT;
guint switch_timeout = SWITCH_TIMEOUT;
const gchar *name;
plugin->plugin_private = priv;
name = plugin->name;
plugin->name = _(name);
params = plugin->params;
if (params)
{
gchar *p;
if (strstr (params, "debug"))
{
g_debug ("%s: Entering debug mode.",
META_COMPOSITOR_CLUTTER_PLUGIN_STRUCT.name);
priv->debug_mode = TRUE;
/*
* Double the effect duration to make them easier to observe.
*/
destroy_timeout *= 2;
minimize_timeout *= 2;
maximize_timeout *= 2;
map_timeout *= 2;
switch_timeout *= 2;
}
if ((p = strstr (params, "disable:")))
{
gchar *d = g_strdup (p+8);
p = strchr (d, ';');
if (p)
*p = 0;
if (strstr (d, "minimize"))
plugin->features &= ~ META_COMPOSITOR_CLUTTER_PLUGIN_MINIMIZE;
if (strstr (d, "maximize"))
plugin->features &= ~ META_COMPOSITOR_CLUTTER_PLUGIN_MAXIMIZE;
if (strstr (d, "unmaximize"))
plugin->features &= ~ META_COMPOSITOR_CLUTTER_PLUGIN_UNMAXIMIZE;
if (strstr (d, "map"))
plugin->features &= ~ META_COMPOSITOR_CLUTTER_PLUGIN_MAP;
if (strstr (d, "destroy"))
plugin->features &= ~ META_COMPOSITOR_CLUTTER_PLUGIN_DESTROY;
if (strstr (d, "switch-workspace"))
plugin->features &= ~META_COMPOSITOR_CLUTTER_PLUGIN_SWITCH_WORKSPACE;
g_free (d);
}
}
priv->destroy_effect
= clutter_effect_template_new (clutter_timeline_new_for_duration (
destroy_timeout),
CLUTTER_ALPHA_SINE_INC);
priv->minimize_effect
= clutter_effect_template_new (clutter_timeline_new_for_duration (
minimize_timeout),
CLUTTER_ALPHA_SINE_INC);
priv->maximize_effect
= clutter_effect_template_new (clutter_timeline_new_for_duration (
maximize_timeout),
CLUTTER_ALPHA_SINE_INC);
priv->map_effect
= clutter_effect_template_new (clutter_timeline_new_for_duration (
map_timeout),
CLUTTER_ALPHA_SINE_INC);
priv->switch_workspace_effect
= clutter_effect_template_new (clutter_timeline_new_for_duration (
switch_timeout),
CLUTTER_ALPHA_SINE_INC);
return TRUE;
}
META_COMPOSITOR_CLUTTER_PLUGIN_INIT_FUNC
{
return do_init ();
}
static void
free_plugin_private (PluginPrivate *priv)
{
g_object_unref (priv->destroy_effect);
g_object_unref (priv->minimize_effect);
g_object_unref (priv->maximize_effect);
g_object_unref (priv->switch_workspace_effect);
g_free (priv);
META_COMPOSITOR_CLUTTER_PLUGIN_STRUCT.plugin_private = NULL;
}
/*
* Called by the plugin manager when we stuff like the command line parameters
* changed.
*/
static gboolean
reload ()
{
PluginPrivate *priv;
priv = META_COMPOSITOR_CLUTTER_PLUGIN_STRUCT.plugin_private;
if (do_init ())
{
/* Success; free the old private struct */
free_plugin_private (priv);
return TRUE;
}
else
{
/* Fail -- fall back to the old private. */
META_COMPOSITOR_CLUTTER_PLUGIN_STRUCT.plugin_private = priv;
}
return FALSE;
}
/*
* GModule unload function -- do any cleanup required.
*/
void g_module_unload (GModule *module);
void g_module_unload (GModule *module)
{
PluginPrivate *priv;
priv = META_COMPOSITOR_CLUTTER_PLUGIN_STRUCT.plugin_private;
free_plugin_private (priv);
}

View File

@ -0,0 +1,644 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (c) 2008 Intel Corp.
*
* Author: Tomas Frydrych <tf@linux.intel.com>
*
* 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 "compositor-clutter-plugin-manager.h"
#include "compositor-clutter.h"
#include "prefs.h"
#include "errors.h"
#include "workspace.h"
#include <gmodule.h>
#include <string.h>
static gboolean meta_compositor_clutter_plugin_manager_reload (MetaCompositorClutterPluginManager *mgr);
struct MetaCompositorClutterPluginManager
{
MetaScreen *screen;
ClutterActor *stage;
GList *plugins; /* TODO -- maybe use hash table */
GList *unload; /* Plugins that are disabled and pending unload */
guint idle_unload_id;
};
typedef struct MetaCompositorClutterPluginPrivate MetaCompositorClutterPluginPrivate;
struct MetaCompositorClutterPluginPrivate
{
GModule *module;
gboolean disabled : 1;
};
/*
* This function gets called when an effect completes. It is responsible for
* any post-effect cleanup.
*/
static void
meta_compositor_clutter_effect_completed (MetaCompositorClutterPlugin *plugin,
ClutterActor *actor,
unsigned long event)
{
if (!actor)
{
g_warning ("Plugin [%s] passed NULL for actor!",
(plugin && plugin->name) ? plugin->name : "unknown");
}
meta_compositor_clutter_window_effect_completed (actor, event);
}
static void
free_plugin_workspaces (MetaCompositorClutterPlugin *plg)
{
GList *l;
l = plg->work_areas;
while (l)
{
g_free (l->data);
l = l->next;
}
if (plg->work_areas)
g_list_free (plg->work_areas);
plg->work_areas = NULL;
}
/*
* Gets work area geometry and stores it in list in the plugin.
*
* If the plg list is already populated, we simply replace it (we are dealing
* with a small number of items in the list and unfrequent changes).
*/
static void
update_plugin_workspaces (MetaScreen *screen,
MetaCompositorClutterPlugin *plg)
{
GList *l, *l2 = NULL;
l = meta_screen_get_workspaces (screen);
while (l)
{
MetaWorkspace *w = l->data;
PluginWorkspaceRectangle *r;
r = g_new0 (PluginWorkspaceRectangle, 1);
meta_workspace_get_work_area_all_xineramas (w, (MetaRectangle*)r);
l2 = g_list_append (l2, r);
l = l->next;
}
free_plugin_workspaces (plg);
plg->work_areas = l2;
}
/*
* Checks that the plugin is compatible with the WM and sets up the plugin
* struct.
*/
static MetaCompositorClutterPlugin *
meta_compositor_clutter_plugin_load (MetaScreen *screen,
ClutterActor *stage,
GModule *module,
const gchar *params)
{
MetaCompositorClutterPlugin *plg;
if (g_module_symbol (module,
META_COMPOSITOR_CLUTTER_PLUGIN_STRUCT_NAME,
(gpointer *)&plg))
{
if (plg->version_api == METACITY_CLUTTER_PLUGIN_API_VERSION)
{
MetaCompositorClutterPluginPrivate *priv;
gboolean (*init_func) (void);
priv = g_new0 (MetaCompositorClutterPluginPrivate, 1);
plg->params = g_strdup (params);
plg->completed = meta_compositor_clutter_effect_completed;
plg->stage = stage;
plg->manager_private = priv;
priv->module = module;
meta_screen_get_size (screen,
&plg->screen_width, &plg->screen_height);
update_plugin_workspaces (screen, plg);
/*
* Check for and run the plugin init function.
*/
if (g_module_symbol (module,
META_COMPOSITOR_CLUTTER_PLUGIN_INIT_FUNC_NAME,
(gpointer *)&init_func) &&
!init_func())
{
g_free (plg->params);
g_free (priv);
free_plugin_workspaces (plg);
return NULL;
}
meta_verbose ("Loaded plugin [%s]\n", plg->name);
return plg;
}
}
return NULL;
}
/*
* Attempst to unload a plugin; returns FALSE if plugin cannot be unloaded at
* present (e.g., and effect is in progress) and should be scheduled for
* removal later.
*/
static gboolean
meta_compositor_clutter_plugin_unload (MetaCompositorClutterPlugin *plg)
{
MetaCompositorClutterPluginPrivate *priv;
GModule *module;
priv = plg->manager_private;
module = priv->module;
if (plg->running)
{
priv->disabled = TRUE;
return FALSE;
}
g_free (plg->params);
plg->params = NULL;
g_free (priv);
plg->manager_private = NULL;
g_module_close (module);
return TRUE;
}
/*
* Iddle callback to remove plugins that could not be removed directly and are
* pending for removal.
*/
static gboolean
meta_compositor_clutter_plugin_manager_idle_unload (MetaCompositorClutterPluginManager *mgr)
{
GList *l = mgr->unload;
gboolean dont_remove = TRUE;
while (l)
{
MetaCompositorClutterPlugin *plg = l->data;
if (meta_compositor_clutter_plugin_unload (plg))
{
/* Remove from list */
GList *p = l->prev;
GList *n = l->next;
if (!p)
mgr->unload = n;
else
p->next = n;
if (n)
n->prev = p;
g_list_free_1 (l);
l = n;
}
else
l = l->next;
}
if (!mgr->unload)
{
/* If no more unloads are pending, remove the handler as well */
dont_remove = FALSE;
mgr->idle_unload_id = 0;
}
return dont_remove;
}
/*
* Unloads all plugins
*/
static void
meta_compositor_clutter_plugin_manager_unload (MetaCompositorClutterPluginManager *mgr)
{
GList *plugins = mgr->plugins;
while (plugins)
{
MetaCompositorClutterPlugin *plg = plugins->data;
/* If the plugin could not be removed, move it to the unload list */
if (!meta_compositor_clutter_plugin_unload (plg))
{
mgr->unload = g_list_prepend (mgr->unload, plg);
if (!mgr->idle_unload_id)
{
mgr->idle_unload_id = g_idle_add ((GSourceFunc)
meta_compositor_clutter_plugin_manager_idle_unload,
mgr);
}
}
plugins = plugins->next;
}
g_list_free (mgr->plugins);
mgr->plugins = NULL;
}
static void
prefs_changed_callback (MetaPreference pref,
void *data)
{
MetaCompositorClutterPluginManager *mgr = data;
if (pref == META_PREF_CLUTTER_PLUGINS)
{
meta_compositor_clutter_plugin_manager_reload (mgr);
}
else if (pref == META_PREF_NUM_WORKSPACES)
{
meta_compositor_clutter_plugin_manager_update_workspaces (mgr);
}
}
/*
* Loads all plugins listed in gconf registry.
*/
static gboolean
meta_compositor_clutter_plugin_manager_load (MetaCompositorClutterPluginManager *mgr)
{
const gchar *dpath = METACITY_PKGLIBDIR "/plugins/clutter/";
GSList *plugins;
plugins = meta_prefs_get_clutter_plugins ();
while (plugins)
{
gchar *plg_string;
gchar *params;
plg_string = g_strdup (plugins->data);
if (plg_string)
{
GModule *plg;
gchar *path;
params = strchr (plg_string, ':');
if (params)
{
*params = 0;
++params;
}
path = g_strconcat (dpath, plg_string, ".so", NULL);
if ((plg = g_module_open (path, 0)))
{
MetaCompositorClutterPlugin *p;
if ((p = meta_compositor_clutter_plugin_load (mgr->screen,
mgr->stage,
plg, params)))
mgr->plugins = g_list_prepend (mgr->plugins, p);
else
g_module_close (plg);
}
g_free (path);
g_free (plg_string);
}
plugins = plugins->next;
}
if (mgr->plugins != NULL)
{
meta_prefs_add_listener (prefs_changed_callback, mgr);
return TRUE;
}
return FALSE;
}
/*
* Reloads all plugins
*/
static gboolean
meta_compositor_clutter_plugin_manager_reload (MetaCompositorClutterPluginManager *mgr)
{
/* TODO -- brute force; should we build a list of plugins to load and list of
* plugins to unload? We are probably not going to have large numbers of
* plugins loaded at the same time, so it might not be worth it.
*/
meta_compositor_clutter_plugin_manager_unload (mgr);
return meta_compositor_clutter_plugin_manager_load (mgr);
}
static gboolean
meta_compositor_clutter_plugin_manager_init (MetaCompositorClutterPluginManager *mgr)
{
return meta_compositor_clutter_plugin_manager_load (mgr);
}
void
meta_compositor_clutter_plugin_manager_update_workspace (MetaCompositorClutterPluginManager *mgr, MetaWorkspace *w)
{
GList *l;
gint n;
n = meta_workspace_index (w);
l = mgr->plugins;
while (l)
{
MetaCompositorClutterPlugin *plg = l->data;
PluginWorkspaceRectangle *r = g_list_nth_data (plg->work_areas, n);
if (r)
{
meta_workspace_get_work_area_all_xineramas (w, (MetaRectangle*)r);
}
else
{
/* Something not entirely right; redo the whole thing */
update_plugin_workspaces (mgr->screen, plg);
return;
}
l = l->next;
}
}
void
meta_compositor_clutter_plugin_manager_update_workspaces (MetaCompositorClutterPluginManager *mgr)
{
GList *l;
l = mgr->plugins;
while (l)
{
MetaCompositorClutterPlugin *plg = l->data;
update_plugin_workspaces (mgr->screen, plg);
l = l->next;
}
}
MetaCompositorClutterPluginManager *
meta_compositor_clutter_plugin_manager_new (MetaScreen *screen,
ClutterActor *stage)
{
MetaCompositorClutterPluginManager *mgr;
mgr = g_new0 (MetaCompositorClutterPluginManager, 1);
mgr->screen = screen;
mgr->stage = stage;
if (!meta_compositor_clutter_plugin_manager_init (mgr))
{
g_free (mgr);
mgr = NULL;
}
return mgr;
}
void
meta_compositor_clutter_plugin_manager_kill_effect (MetaCompositorClutterPluginManager *mgr,
ClutterActor *actor,
unsigned long events)
{
GList *l = mgr->plugins;
while (l)
{
MetaCompositorClutterPlugin *plg = l->data;
MetaCompositorClutterPluginPrivate *priv = plg->manager_private;
if (!priv->disabled && (plg->features & events) && plg->kill_effect)
plg->kill_effect (actor, events);
l = l->next;
}
}
#define ALL_BUT_SWITCH \
META_COMPOSITOR_CLUTTER_PLUGIN_ALL_EFFECTS & \
~META_COMPOSITOR_CLUTTER_PLUGIN_SWITCH_WORKSPACE
/*
* Public method that the compositor hooks into for events that require
* no additional parameters.
*
* Returns TRUE if at least one of the plugins handled the event type (i.e.,
* if the return value is FALSE, there will be no subsequent call to the
* manager completed() callback, and the compositor must ensure that any
* appropriate post-effect cleanup is carried out.
*/
gboolean
meta_compositor_clutter_plugin_manager_event_0 (MetaCompositorClutterPluginManager *mgr,
ClutterActor *actor,
unsigned long event,
MetaCompWindowType type,
gint workspace)
{
GList *l = mgr->plugins;
gboolean retval = FALSE;
while (l)
{
MetaCompositorClutterPlugin *plg = l->data;
MetaCompositorClutterPluginPrivate *priv = plg->manager_private;
if (!priv->disabled && (plg->features & event))
{
retval = TRUE;
switch (event)
{
case META_COMPOSITOR_CLUTTER_PLUGIN_MINIMIZE:
if (plg->minimize)
{
meta_compositor_clutter_plugin_manager_kill_effect (mgr,
actor,
ALL_BUT_SWITCH);
plg->minimize (actor, type, workspace);
}
break;
case META_COMPOSITOR_CLUTTER_PLUGIN_MAP:
if (plg->map)
{
meta_compositor_clutter_plugin_manager_kill_effect (mgr,
actor,
ALL_BUT_SWITCH);
plg->map (actor, type, workspace);
}
break;
case META_COMPOSITOR_CLUTTER_PLUGIN_DESTROY:
if (plg->destroy)
{
plg->destroy (actor, type, workspace);
}
break;
default:
g_warning ("Incorrect handler called for event %lu", event);
}
}
l = l->next;
}
return retval;
}
/*
* The public method that the compositor hooks into for events that require
* up to 4 additional integer parameters.
*
* Returns TRUE if at least one of the plugins handled the event type (i.e.,
* if the return value is FALSE, there will be no subsequent call to the
* manager completed() callback, and the compositor must ensure that any
* appropriate post-effect cleanup is carried out.
*/
gboolean
meta_compositor_clutter_plugin_manager_event_4i (MetaCompositorClutterPluginManager *mgr,
ClutterActor *actor,
unsigned long event,
MetaCompWindowType type,
gint workspace,
gint i0,
gint i1,
gint i2,
gint i3)
{
GList *l = mgr->plugins;
gboolean retval = FALSE;
while (l)
{
MetaCompositorClutterPlugin *plg = l->data;
MetaCompositorClutterPluginPrivate *priv = plg->manager_private;
if (!priv->disabled && (plg->features & event))
{
retval = TRUE;
switch (event)
{
case META_COMPOSITOR_CLUTTER_PLUGIN_MAXIMIZE:
if (plg->maximize)
{
meta_compositor_clutter_plugin_manager_kill_effect (mgr,
actor,
ALL_BUT_SWITCH);
plg->maximize (actor, type, workspace, i0, i1, i2, i3);
}
break;
case META_COMPOSITOR_CLUTTER_PLUGIN_UNMAXIMIZE:
if (plg->unmaximize)
{
meta_compositor_clutter_plugin_manager_kill_effect (mgr,
actor,
ALL_BUT_SWITCH);
plg->unmaximize (actor, type, workspace, i0, i1, i2, i3);
}
break;
default:
g_warning ("Incorrect handler called for event %lu", event);
}
}
l = l->next;
}
return retval;
}
/*
* The public method that the compositor hooks into for desktop switching.
*
* Returns TRUE if at least one of the plugins handled the event type (i.e.,
* if the return value is FALSE, there will be no subsequent call to the
* manager completed() callback, and the compositor must ensure that any
* appropriate post-effect cleanup is carried out.
*/
gboolean
meta_compositor_clutter_plugin_manager_switch_workspace (MetaCompositorClutterPluginManager *mgr,
const GList **actors,
gint from,
gint to)
{
GList *l = mgr->plugins;
gboolean retval = FALSE;
while (l)
{
MetaCompositorClutterPlugin *plg = l->data;
MetaCompositorClutterPluginPrivate *priv = plg->manager_private;
if (!priv->disabled &&
(plg->features & META_COMPOSITOR_CLUTTER_PLUGIN_SWITCH_WORKSPACE) &&
(actors && *actors))
{
if (plg->switch_workspace)
{
retval = TRUE;
meta_compositor_clutter_plugin_manager_kill_effect (mgr,
CLUTTER_ACTOR ((*actors)->data),
META_COMPOSITOR_CLUTTER_PLUGIN_SWITCH_WORKSPACE);
plg->switch_workspace (actors, from, to);
}
}
l = l->next;
}
return retval;
}

View File

@ -0,0 +1,58 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (c) 2008 Intel Corp.
*
* Author: Tomas Frydrych <tf@linux.intel.com>
*
* 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_COMPOSITOR_CLUTTER_PLUGIN_MANAGER_H_
#define META_COMPOSITOR_CLUTTER_PLUGIN_MANAGER_H_
#include "types.h"
#include "screen.h"
#include "compositor-clutter-plugin.h"
typedef struct MetaCompositorClutterPluginManager MetaCompositorClutterPluginManager;
MetaCompositorClutterPluginManager * meta_compositor_clutter_plugin_manager_new (MetaScreen *screen, ClutterActor *stage);
gboolean meta_compositor_clutter_plugin_manager_event_0 (MetaCompositorClutterPluginManager *mgr,
ClutterActor *actor,
unsigned long event,
MetaCompWindowType type,
gint workspace);
gboolean meta_compositor_clutter_plugin_manager_event_4i (MetaCompositorClutterPluginManager *mgr,
ClutterActor *actor,
unsigned long event,
MetaCompWindowType type,
gint workspace,
gint i0,
gint i1,
gint i2,
gint i3);
void meta_compositor_clutter_plugin_manager_update_workspaces (MetaCompositorClutterPluginManager *mgr);
void meta_compositor_clutter_plugin_manager_update_workspace (MetaCompositorClutterPluginManager *mgr, MetaWorkspace *w);
gboolean meta_compositor_clutter_plugin_manager_switch_workspace (MetaCompositorClutterPluginManager *mgr,
const GList **actors,
gint from,
gint to);
#endif

View File

@ -17,6 +17,7 @@
#include "window.h" #include "window.h"
#include "compositor-private.h" #include "compositor-private.h"
#include "compositor-clutter.h" #include "compositor-clutter.h"
#include "compositor-clutter-plugin-manager.h"
#include "xprops.h" #include "xprops.h"
#include <X11/Xatom.h> #include <X11/Xatom.h>
#include <X11/Xlibint.h> #include <X11/Xlibint.h>
@ -41,16 +42,13 @@
#define TILE_WIDTH (3*MAX_TILE_SZ) #define TILE_WIDTH (3*MAX_TILE_SZ)
#define TILE_HEIGHT (3*MAX_TILE_SZ) #define TILE_HEIGHT (3*MAX_TILE_SZ)
#define DESTROY_TIMEOUT 300
#define MINIMIZE_TIMEOUT 600
/* /*
* Register GType wrapper for XWindowAttributes, so we do not have to * Register GType wrapper for XWindowAttributes, so we do not have to
* query window attributes in the MetaCompWindow constructor but can pass * query window attributes in the MetaCompWindow constructor but can pass
* them as a property to the constructor (so we can gracefully handle the case * them as a property to the constructor (so we can gracefully handle the case
* where no attributes can be retrieved). * where no attributes can be retrieved).
* *
* NB -- we only need a subset of the attribute; at some point we might want * NB -- we only need a subset of the attributes; at some point we might want
* to just store the relevant values rather than the whole struct. * to just store the relevant values rather than the whole struct.
*/ */
#define META_TYPE_XATTRS (meta_xattrs_get_type ()) #define META_TYPE_XATTRS (meta_xattrs_get_type ())
@ -110,27 +108,8 @@ composite_at_least_version (MetaDisplay *display, int maj, int min)
return (major > maj || (major == maj && minor >= min)); return (major > maj || (major == maj && minor >= min));
} }
#endif #endif
typedef enum _MetaCompWindowType
{
/*
* Types shared with MetaWindow
*/
META_COMP_WINDOW_NORMAL = META_WINDOW_NORMAL,
META_COMP_WINDOW_DESKTOP = META_WINDOW_DESKTOP,
META_COMP_WINDOW_DOCK = META_WINDOW_DOCK,
META_COMP_WINDOW_MENU = META_WINDOW_MENU,
/*
* Extended types that WM does not care about, but we do.
*/
META_COMP_WINDOW_TOOLTIP = 0xf000,
META_COMP_WINDOW_DROP_DOWN_MENU,
META_COMP_WINDOW_DND,
} MetaCompWindowType;
typedef struct _MetaCompositorClutter typedef struct _MetaCompositorClutter
{ {
MetaCompositor compositor; MetaCompositor compositor;
@ -157,8 +136,9 @@ typedef struct _MetaCompScreen
Window output; Window output;
GSList *dock_windows; GSList *dock_windows;
ClutterEffectTemplate *destroy_effect; gint switch_workspace_in_progress;
ClutterEffectTemplate *minimize_effect;
MetaCompositorClutterPluginManager *plugin_mgr;
} MetaCompScreen; } MetaCompScreen;
/* /*
@ -204,12 +184,28 @@ struct _MetaCompWindowPrivate
guint8 opacity; guint8 opacity;
/*
* These need to be counters rather than flags, since more plugins
* can implement same effect; the practicality of stacking effects
* might be dubious, but we have to at least handle it correctly.
*/
gint minimize_in_progress;
gint maximize_in_progress;
gint unmaximize_in_progress;
gint map_in_progress;
gint destroy_in_progress;
gboolean needs_shadow : 1; gboolean needs_shadow : 1;
gboolean shaped : 1; gboolean shaped : 1;
gboolean destroy_pending : 1; gboolean destroy_pending : 1;
gboolean argb32 : 1; gboolean argb32 : 1;
gboolean minimize_in_progress : 1;
gboolean disposed : 1; gboolean disposed : 1;
gboolean is_minimized : 1;
/* Desktop switching flags */
gboolean needs_map : 1;
gboolean needs_unmap : 1;
gboolean needs_repair : 1;
}; };
enum enum
@ -515,6 +511,12 @@ meta_comp_window_get_window_type (MetaCompWindow *self)
Atom *atoms; Atom *atoms;
gint i; gint i;
if (priv->attrs.override_redirect)
{
priv->type = META_COMP_WINDOW_OVERRIDE;
return;
}
/* /*
* If the window is managed by the WM, get the type from the WM, * If the window is managed by the WM, get the type from the WM,
* otherwise do it the hard way. * otherwise do it the hard way.
@ -613,7 +615,7 @@ meta_comp_window_has_shadow (MetaCompWindow *self)
MetaCompWindowPrivate * priv = self->priv; MetaCompWindowPrivate * priv = self->priv;
/* /*
* Do not add shadows to ARGB windows (since they are probably transparent * Do not add shadows to ARGB windows (since they are probably transparent)
*/ */
if (priv->argb32 || priv->opacity != 0xff) if (priv->argb32 || priv->opacity != 0xff)
{ {
@ -685,6 +687,170 @@ meta_comp_window_has_shadow (MetaCompWindow *self)
return FALSE; return FALSE;
} }
static void repair_win (MetaCompWindow *cw);
static void map_win (MetaCompWindow *cw);
static void unmap_win (MetaCompWindow *cw);
static void
meta_compositor_clutter_finish_workspace_switch (MetaCompScreen *info)
{
GList *last = g_list_last (info->windows);
GList *l = last;
while (l)
{
MetaCompWindow *cw = l->data;
MetaCompWindowPrivate *priv = cw->priv;
if (priv->needs_map && !priv->needs_unmap)
{
map_win (cw);
}
if (priv->needs_unmap)
{
unmap_win (cw);
}
l = l->prev;
}
/*
* Now fix up stacking order in case the plugin messed it up.
*/
l = last;
while (l)
{
ClutterActor *a = l->data;
GList *prev = l->prev;
if (prev)
{
ClutterActor *above_me = prev->data;
clutter_actor_raise (above_me, a);
}
else
{
ClutterActor *a = l->data;
clutter_actor_raise_top (a);
}
l = prev;
}
}
void
meta_compositor_clutter_window_effect_completed (ClutterActor *actor,
gulong event)
{
MetaCompWindow *cw = META_COMP_WINDOW (actor);
MetaCompWindowPrivate *priv = cw->priv;
MetaScreen *screen = priv->screen;
MetaCompScreen *info = meta_screen_get_compositor_data (screen);
switch (event)
{
case META_COMPOSITOR_CLUTTER_PLUGIN_MINIMIZE:
{
ClutterActor *a = CLUTTER_ACTOR (cw);
gint height = clutter_actor_get_height (a);
priv->minimize_in_progress--;
if (priv->minimize_in_progress < 0)
{
g_warning ("Error in minimize accounting.");
priv->minimize_in_progress = 0;
}
if (!priv->minimize_in_progress)
{
priv->is_minimized = TRUE;
clutter_actor_set_position (a, 0, -height);
}
}
break;
case META_COMPOSITOR_CLUTTER_PLUGIN_MAP:
/*
* Make sure that the actor is at the correct place in case
* the plugin fscked.
*/
priv->map_in_progress--;
if (priv->map_in_progress < 0)
{
g_warning ("Error in map accounting.");
priv->map_in_progress = 0;
}
if (!priv->map_in_progress)
{
priv->is_minimized = FALSE;
clutter_actor_set_anchor_point (actor, 0, 0);
clutter_actor_set_position (actor, priv->attrs.x, priv->attrs.y);
clutter_actor_show_all (actor);
}
break;
case META_COMPOSITOR_CLUTTER_PLUGIN_DESTROY:
priv->destroy_in_progress--;
if (priv->destroy_in_progress < 0)
{
g_warning ("Error in destroy accounting.");
priv->destroy_in_progress = 0;
}
if (!priv->destroy_in_progress)
{
clutter_actor_destroy (actor);
}
break;
case META_COMPOSITOR_CLUTTER_PLUGIN_UNMAXIMIZE:
priv->unmaximize_in_progress--;
if (priv->unmaximize_in_progress < 0)
{
g_warning ("Error in unmaximize accounting.");
priv->unmaximize_in_progress = 0;
}
if (!priv->unmaximize_in_progress)
{
clutter_actor_set_position (actor, priv->attrs.x, priv->attrs.y);
meta_comp_window_detach (cw);
repair_win (cw);
}
break;
case META_COMPOSITOR_CLUTTER_PLUGIN_MAXIMIZE:
priv->maximize_in_progress--;
if (priv->maximize_in_progress < 0)
{
g_warning ("Error in maximize accounting.");
priv->maximize_in_progress = 0;
}
if (!priv->maximize_in_progress)
{
clutter_actor_set_position (actor, priv->attrs.x, priv->attrs.y);
meta_comp_window_detach (cw);
repair_win (cw);
}
break;
case META_COMPOSITOR_CLUTTER_PLUGIN_SWITCH_WORKSPACE:
/* FIXME -- must redo stacking order */
info->switch_workspace_in_progress--;
if (info->switch_workspace_in_progress < 0)
{
g_warning ("Error in workspace_switch accounting!");
info->switch_workspace_in_progress = 0;
}
if (!info->switch_workspace_in_progress)
meta_compositor_clutter_finish_workspace_switch (info);
break;
default: ;
}
}
static void static void
clutter_cmp_destroy (MetaCompositor *compositor) clutter_cmp_destroy (MetaCompositor *compositor)
@ -694,6 +860,10 @@ clutter_cmp_destroy (MetaCompositor *compositor)
#endif #endif
} }
/*
* If force is TRUE, free the back pixmap; if FALSE, only free it if the
* backing pixmap has actually changed.
*/
static void static void
meta_comp_window_detach (MetaCompWindow *self) meta_comp_window_detach (MetaCompWindow *self)
{ {
@ -702,12 +872,12 @@ meta_comp_window_detach (MetaCompWindow *self)
MetaDisplay *display = meta_screen_get_display (screen); MetaDisplay *display = meta_screen_get_display (screen);
Display *xdisplay = meta_display_get_xdisplay (display); Display *xdisplay = meta_display_get_xdisplay (display);
if (priv->back_pixmap) if (!priv->back_pixmap)
{ return;
XFreePixmap (xdisplay, priv->back_pixmap); XFreePixmap (xdisplay, priv->back_pixmap);
priv->back_pixmap = None; priv->back_pixmap = None;
} }
}
static void static void
destroy_win (MetaDisplay *display, Window xwindow) destroy_win (MetaDisplay *display, Window xwindow)
@ -719,8 +889,6 @@ destroy_win (MetaDisplay *display, Window xwindow)
if (cw == NULL) if (cw == NULL)
return; return;
meta_verbose ("destroying a window... 0x%x (%p)\n", (guint) xwindow, cw);
clutter_actor_destroy (CLUTTER_ACTOR (cw)); clutter_actor_destroy (CLUTTER_ACTOR (cw));
} }
@ -752,6 +920,7 @@ restack_win (MetaCompWindow *cw, Window above)
info->windows = g_list_delete_link (info->windows, sibling); info->windows = g_list_delete_link (info->windows, sibling);
info->windows = g_list_append (info->windows, cw); info->windows = g_list_append (info->windows, cw);
if (!info->switch_workspace_in_progress)
clutter_actor_raise_top (CLUTTER_ACTOR (cw)); clutter_actor_raise_top (CLUTTER_ACTOR (cw));
} }
else if (previous_above != above) else if (previous_above != above)
@ -772,6 +941,7 @@ restack_win (MetaCompWindow *cw, Window above)
info->windows = g_list_delete_link (info->windows, sibling); info->windows = g_list_delete_link (info->windows, sibling);
info->windows = g_list_insert_before (info->windows, index, cw); info->windows = g_list_insert_before (info->windows, index, cw);
if (!info->switch_workspace_in_progress)
clutter_actor_raise (CLUTTER_ACTOR (cw), above_win); clutter_actor_raise (CLUTTER_ACTOR (cw), above_win);
} }
} }
@ -790,60 +960,116 @@ resize_win (MetaCompWindow *cw,
priv->attrs.x = x; priv->attrs.x = x;
priv->attrs.y = y; priv->attrs.y = y;
clutter_actor_set_position (CLUTTER_ACTOR (cw), x, y);
/* Note, let named named pixmap resync actually resize actor */
if (priv->attrs.width != width || priv->attrs.height != height)
meta_comp_window_detach (cw);
priv->attrs.width = width; priv->attrs.width = width;
priv->attrs.height = height; priv->attrs.height = height;
priv->attrs.border_width = border_width; priv->attrs.border_width = border_width;
priv->attrs.override_redirect = override_redirect; priv->attrs.override_redirect = override_redirect;
if (priv->maximize_in_progress ||
priv->unmaximize_in_progress ||
priv->map_in_progress)
return;
meta_comp_window_detach (cw);
clutter_actor_set_position (CLUTTER_ACTOR (cw), x, y);
} }
static void static void
map_win (MetaDisplay *display, MetaScreen *screen, Window id) map_win (MetaCompWindow *cw)
{ {
MetaCompWindow *cw = find_window_for_screen (screen, id);
MetaCompWindowPrivate *priv; MetaCompWindowPrivate *priv;
MetaCompScreen *info;
if (cw == NULL) if (cw == NULL)
return; return;
priv = cw->priv; priv = cw->priv;
info = meta_screen_get_compositor_data (priv->screen);
if (priv->attrs.map_state == IsViewable)
return;
priv->attrs.map_state = IsViewable; priv->attrs.map_state = IsViewable;
priv->minimize_in_progress = FALSE; /*
* Now repair the window; this ensures that the actor is correctly sized
* before we run any effects on it.
*/
priv->needs_map = FALSE;
meta_comp_window_detach (cw);
repair_win (cw);
clutter_actor_show (CLUTTER_ACTOR (cw)); /*
* Make sure the position is set correctly (we might have got moved while
* unmapped.
*/
if (!info->switch_workspace_in_progress)
{
clutter_actor_set_anchor_point (CLUTTER_ACTOR (cw), 0, 0);
clutter_actor_set_position (CLUTTER_ACTOR (cw),
cw->priv->attrs.x, cw->priv->attrs.y);
} }
priv->map_in_progress++;
/*
* If a plugin manager is present, try to run an effect; if no effect of this
* type is present, destroy the actor.
*/
if (info->switch_workspace_in_progress || !info->plugin_mgr ||
!meta_compositor_clutter_plugin_manager_event_0 (info->plugin_mgr,
CLUTTER_ACTOR (cw),
META_COMPOSITOR_CLUTTER_PLUGIN_MAP,
cw->priv->type, 0))
{
clutter_actor_show_all (CLUTTER_ACTOR (cw));
priv->map_in_progress--;
priv->is_minimized = FALSE;
}
}
static void static void
unmap_win (MetaDisplay *display, MetaScreen *screen, Window id) unmap_win (MetaCompWindow *cw)
{ {
MetaCompWindow *cw = find_window_for_screen (screen, id);
MetaCompScreen *info = meta_screen_get_compositor_data (screen);
MetaCompWindowPrivate *priv; MetaCompWindowPrivate *priv;
MetaCompScreen *info;
if (cw == NULL) if (cw == NULL)
return; return;
priv = cw->priv; priv = cw->priv;
info = meta_screen_get_compositor_data (priv->screen);
/*
* If the needs_unmap flag is set, we carry on even if the winow is
* already marked as unmapped; this is necessary so windows temporarily
* shown during an effect (like desktop switch) are properly hidden again.
*/
if (priv->attrs.map_state == IsUnmapped && !priv->needs_unmap)
return;
if (priv->window && priv->window == info->focus_window) if (priv->window && priv->window == info->focus_window)
info->focus_window = NULL; info->focus_window = NULL;
if (info->switch_workspace_in_progress)
{
/*
* Cannot unmap windows while switching desktops effect is in progress.
*/
priv->needs_unmap = TRUE;
return;
}
priv->attrs.map_state = IsUnmapped; priv->attrs.map_state = IsUnmapped;
meta_comp_window_detach (cw);
if (!priv->minimize_in_progress) if (!priv->minimize_in_progress)
clutter_actor_hide (CLUTTER_ACTOR (cw)); {
ClutterActor *a = CLUTTER_ACTOR (cw);
clutter_actor_hide (a);
}
priv->needs_unmap = FALSE;
priv->needs_map = FALSE;
} }
@ -895,7 +1121,6 @@ add_win (MetaScreen *screen, MetaWindow *window, Window xwindow)
CLUTTER_ACTOR (cw)); CLUTTER_ACTOR (cw));
clutter_actor_hide (CLUTTER_ACTOR (cw)); clutter_actor_hide (CLUTTER_ACTOR (cw));
/* Only add the window to the list of docks if it needs a shadow */
if (priv->type == META_COMP_WINDOW_DOCK) if (priv->type == META_COMP_WINDOW_DOCK)
{ {
meta_verbose ("Appending 0x%x to dock windows\n", (guint)xwindow); meta_verbose ("Appending 0x%x to dock windows\n", (guint)xwindow);
@ -940,7 +1165,11 @@ add_win (MetaScreen *screen, MetaWindow *window, Window xwindow)
g_hash_table_insert (info->windows_by_xid, (gpointer) xwindow, cw); g_hash_table_insert (info->windows_by_xid, (gpointer) xwindow, cw);
if (priv->attrs.map_state == IsViewable) if (priv->attrs.map_state == IsViewable)
map_win (display, screen, xwindow); {
/* Need to reset the map_state for map_win() to work */
priv->attrs.map_state = IsUnmapped;
map_win (cw);
}
} }
static void static void
@ -952,6 +1181,7 @@ repair_win (MetaCompWindow *cw)
Display *xdisplay = meta_display_get_xdisplay (display); Display *xdisplay = meta_display_get_xdisplay (display);
MetaCompScreen *info = meta_screen_get_compositor_data (screen); MetaCompScreen *info = meta_screen_get_compositor_data (screen);
Window xwindow = priv->xwindow; Window xwindow = priv->xwindow;
gboolean full = FALSE;
if (xwindow == meta_screen_get_xroot (screen) || if (xwindow == meta_screen_get_xroot (screen) ||
xwindow == clutter_x11_get_stage_window (CLUTTER_STAGE (info->stage))) xwindow == clutter_x11_get_stage_window (CLUTTER_STAGE (info->stage)))
@ -962,8 +1192,23 @@ repair_win (MetaCompWindow *cw)
if (priv->back_pixmap == None) if (priv->back_pixmap == None)
{ {
gint pxm_width, pxm_height; gint pxm_width, pxm_height;
XWindowAttributes attr;
meta_error_trap_push (display);
XGrabServer (xdisplay);
XGetWindowAttributes (xdisplay, xwindow, &attr);
if (attr.map_state == IsViewable)
priv->back_pixmap = XCompositeNameWindowPixmap (xdisplay, xwindow); priv->back_pixmap = XCompositeNameWindowPixmap (xdisplay, xwindow);
else
{
priv->back_pixmap = None;
}
XUngrabServer (xdisplay);
meta_error_trap_pop (display, FALSE);
if (priv->back_pixmap == None) if (priv->back_pixmap == None)
{ {
@ -984,6 +1229,8 @@ repair_win (MetaCompWindow *cw)
if (priv->shadow) if (priv->shadow)
clutter_actor_set_size (priv->shadow, pxm_width, pxm_height); clutter_actor_set_size (priv->shadow, pxm_width, pxm_height);
full = TRUE;
} }
/* /*
@ -995,9 +1242,10 @@ repair_win (MetaCompWindow *cw)
* If we are using TFP we update the whole texture (this simply trigers * If we are using TFP we update the whole texture (this simply trigers
* the texture rebind). * the texture rebind).
*/ */
if (CLUTTER_GLX_IS_TEXTURE_PIXMAP (priv->actor) && if (full ||
(CLUTTER_GLX_IS_TEXTURE_PIXMAP (priv->actor) &&
clutter_glx_texture_pixmap_using_extension ( clutter_glx_texture_pixmap_using_extension (
CLUTTER_GLX_TEXTURE_PIXMAP (priv->actor))) CLUTTER_GLX_TEXTURE_PIXMAP (priv->actor))))
{ {
XDamageSubtract (xdisplay, priv->damage, None, None); XDamageSubtract (xdisplay, priv->damage, None, None);
@ -1041,6 +1289,8 @@ repair_win (MetaCompWindow *cw)
} }
meta_error_trap_pop (display, FALSE); meta_error_trap_pop (display, FALSE);
priv->needs_repair = FALSE;
} }
@ -1109,9 +1359,20 @@ process_damage (MetaCompositorClutter *compositor,
priv = cw->priv; priv = cw->priv;
if (priv->destroy_pending) if (priv->destroy_pending ||
priv->maximize_in_progress ||
priv->unmaximize_in_progress)
{
priv->needs_repair = TRUE;
return; return;
}
/*
* Check if the event queue does not already contain DetstroyNotify for this
* window -- if it does, we need to stop updating the pixmap (to avoid damage
* notifications that come from the window teardown), and process the destroy
* immediately.
*/
if (XCheckTypedWindowEvent (dpy, drawable, DestroyNotify, &next)) if (XCheckTypedWindowEvent (dpy, drawable, DestroyNotify, &next))
{ {
priv->destroy_pending = TRUE; priv->destroy_pending = TRUE;
@ -1138,6 +1399,9 @@ process_configure_notify (MetaCompositorClutter *compositor,
} }
else else
{ {
/*
* Check for root window geometry change
*/
GSList *l = meta_display_get_screens (display); GSList *l = meta_display_get_screens (display);
while (l) while (l)
@ -1188,8 +1452,8 @@ process_circulate_notify (MetaCompositorClutter *compositor,
above = top->priv->xwindow; above = top->priv->xwindow;
else else
above = None; above = None;
restack_win (cw, above);
restack_win (cw, above);
} }
static void static void
@ -1213,7 +1477,7 @@ process_unmap (MetaCompositorClutter *compositor,
XEvent next; XEvent next;
MetaCompWindowPrivate *priv = cw->priv; MetaCompWindowPrivate *priv = cw->priv;
if (priv->destroy_pending) if (priv->attrs.map_state == IsUnmapped || priv->destroy_pending)
return; return;
if (XCheckTypedWindowEvent (dpy, xwin, DestroyNotify, &next)) if (XCheckTypedWindowEvent (dpy, xwin, DestroyNotify, &next))
@ -1224,7 +1488,7 @@ process_unmap (MetaCompositorClutter *compositor,
} }
meta_verbose ("processing unmap of 0x%x (%p)\n", (guint)xwin, cw); meta_verbose ("processing unmap of 0x%x (%p)\n", (guint)xwin, cw);
unmap_win (compositor->display, priv->screen, xwin); unmap_win (cw);
} }
} }
@ -1237,7 +1501,7 @@ process_map (MetaCompositorClutter *compositor,
event->window); event->window);
if (cw) if (cw)
map_win (compositor->display, cw->priv->screen, event->window); map_win (cw);
} }
static void static void
@ -1374,21 +1638,13 @@ clutter_cmp_manage_screen (MetaCompositor *compositor,
XReparentWindow (xdisplay, xwin, info->output, 0, 0); XReparentWindow (xdisplay, xwin, info->output, 0, 0);
info->plugin_mgr =
meta_compositor_clutter_plugin_manager_new (screen, info->stage);
clutter_actor_show_all (info->stage); clutter_actor_show_all (info->stage);
/* Now we're up and running we can show the output if needed */ /* Now we're up and running we can show the output if needed */
show_overlay_window (screen, info->output); show_overlay_window (screen, info->output);
info->destroy_effect
= clutter_effect_template_new (clutter_timeline_new_for_duration (
DESTROY_TIMEOUT),
CLUTTER_ALPHA_SINE_INC);
info->minimize_effect
= clutter_effect_template_new (clutter_timeline_new_for_duration (
MINIMIZE_TIMEOUT),
CLUTTER_ALPHA_SINE_INC);
#endif #endif
} }
@ -1531,13 +1787,6 @@ clutter_cmp_set_active_window (MetaCompositor *compositor,
#endif #endif
} }
static void
on_destroy_effect_complete (ClutterActor *actor,
gpointer user_data)
{
clutter_actor_destroy (actor);
}
static void static void
clutter_cmp_destroy_window (MetaCompositor *compositor, clutter_cmp_destroy_window (MetaCompositor *compositor,
MetaWindow *window) MetaWindow *window)
@ -1547,6 +1796,7 @@ clutter_cmp_destroy_window (MetaCompositor *compositor,
MetaScreen *screen = meta_window_get_screen (window); MetaScreen *screen = meta_window_get_screen (window);
MetaCompScreen *info = meta_screen_get_compositor_data (screen); MetaCompScreen *info = meta_screen_get_compositor_data (screen);
MetaFrame *f = meta_window_get_frame (window); MetaFrame *f = meta_window_get_frame (window);
MetaCompWindowPrivate *priv;
/* Chances are we actually get the window frame here */ /* Chances are we actually get the window frame here */
cw = find_window_for_screen (screen, cw = find_window_for_screen (screen,
@ -1555,6 +1805,8 @@ clutter_cmp_destroy_window (MetaCompositor *compositor,
if (!cw) if (!cw)
return; return;
priv = cw->priv;
/* /*
* We remove the window from internal lookup hashes and thus any other * We remove the window from internal lookup hashes and thus any other
* unmap events etc fail * unmap events etc fail
@ -1564,38 +1816,22 @@ clutter_cmp_destroy_window (MetaCompositor *compositor,
(gpointer) (f ? meta_frame_get_xwindow (f) : (gpointer) (f ? meta_frame_get_xwindow (f) :
meta_window_get_xwindow (window))); meta_window_get_xwindow (window)));
clutter_actor_move_anchor_point_from_gravity (CLUTTER_ACTOR (cw),
CLUTTER_GRAVITY_CENTER);
clutter_effect_fade (info->destroy_effect,
CLUTTER_ACTOR (cw),
0,
on_destroy_effect_complete,
(gpointer)cw);
clutter_effect_scale (info->destroy_effect ,
CLUTTER_ACTOR (cw),
1.0,
0.0,
NULL,
NULL);
#endif
}
static void
on_minimize_effect_complete (ClutterActor *actor,
gpointer user_data)
{
MetaCompWindow *cw = (MetaCompWindow *)user_data;
/* /*
* Must reverse the effect of the effect once we hide the actor. * If a plugin manager is present, try to run an effect; if no effect of this
* type is present, destroy the actor.
*/ */
clutter_actor_hide (CLUTTER_ACTOR (cw)); priv->destroy_in_progress++;
clutter_actor_set_opacity (CLUTTER_ACTOR (cw), cw->priv->opacity);
clutter_actor_set_scale (CLUTTER_ACTOR (cw), 1.0, 1.0); if (!info->plugin_mgr ||
clutter_actor_move_anchor_point_from_gravity (CLUTTER_ACTOR (cw), !meta_compositor_clutter_plugin_manager_event_0 (info->plugin_mgr,
CLUTTER_GRAVITY_NORTH_WEST); CLUTTER_ACTOR (cw),
META_COMPOSITOR_CLUTTER_PLUGIN_DESTROY,
cw->priv->type, 0))
{
priv->destroy_in_progress--;
clutter_actor_destroy (CLUTTER_ACTOR (cw));
}
#endif
} }
static void static void
@ -1617,29 +1853,185 @@ clutter_cmp_minimize_window (MetaCompositor *compositor, MetaWindow *window)
if (!cw) if (!cw)
return; return;
meta_verbose ("Animating minimize of 0x%x\n", /*
(guint)meta_window_get_xwindow (window)); * If there is a plugin manager, try to run an effect; if no effect is
* executed, hide the actor.
*/
cw->priv->minimize_in_progress++;
cw->priv->minimize_in_progress = TRUE; if (!info->plugin_mgr ||
!meta_compositor_clutter_plugin_manager_event_0 (info->plugin_mgr,
clutter_actor_move_anchor_point_from_gravity (CLUTTER_ACTOR (cw),
CLUTTER_GRAVITY_SOUTH_WEST);
clutter_effect_fade (info->minimize_effect,
CLUTTER_ACTOR (cw), CLUTTER_ACTOR (cw),
0, META_COMPOSITOR_CLUTTER_PLUGIN_MINIMIZE,
on_minimize_effect_complete, cw->priv->type, 0))
(gpointer)cw); {
ClutterActor *a = CLUTTER_ACTOR (cw);
gint height = clutter_actor_get_height (a);
clutter_effect_scale (info->minimize_effect, cw->priv->is_minimized = TRUE;
CLUTTER_ACTOR (cw), cw->priv->minimize_in_progress--;
0.0, clutter_actor_set_position (a, 0, -height);
0.0, }
NULL,
NULL);
#endif #endif
} }
static void
clutter_cmp_maximize_window (MetaCompositor *compositor, MetaWindow *window,
gint x, gint y, gint width, gint height)
{
#ifdef HAVE_COMPOSITE_EXTENSIONS
MetaCompWindow *cw;
MetaCompScreen *info;
MetaScreen *screen;
MetaFrame *f = meta_window_get_frame (window);
screen = meta_window_get_screen (window);
info = meta_screen_get_compositor_data (screen);
/* Chances are we actually get the window frame here */
cw = find_window_for_screen (screen,
f ? meta_frame_get_xwindow (f) :
meta_window_get_xwindow (window));
if (!cw)
return;
cw->priv->maximize_in_progress++;
if (!info->plugin_mgr ||
!meta_compositor_clutter_plugin_manager_event_4i (info->plugin_mgr,
CLUTTER_ACTOR (cw),
META_COMPOSITOR_CLUTTER_PLUGIN_MAXIMIZE,
cw->priv->type, 0, x, y, width, height))
{
cw->priv->maximize_in_progress--;
}
#endif
}
static void
clutter_cmp_unmaximize_window (MetaCompositor *compositor, MetaWindow *window,
gint x, gint y, gint width, gint height)
{
#ifdef HAVE_COMPOSITE_EXTENSIONS
MetaCompWindow *cw;
MetaCompScreen *info;
MetaScreen *screen;
MetaFrame *f = meta_window_get_frame (window);
screen = meta_window_get_screen (window);
info = meta_screen_get_compositor_data (screen);
/* Chances are we actually get the window frame here */
cw = find_window_for_screen (screen,
f ? meta_frame_get_xwindow (f) :
meta_window_get_xwindow (window));
if (!cw)
return;
cw->priv->unmaximize_in_progress++;
if (!info->plugin_mgr ||
!meta_compositor_clutter_plugin_manager_event_4i (info->plugin_mgr,
CLUTTER_ACTOR (cw),
META_COMPOSITOR_CLUTTER_PLUGIN_UNMAXIMIZE,
cw->priv->type, 0, x, y, width, height))
{
cw->priv->unmaximize_in_progress--;
}
#endif
}
static void
clutter_cmp_update_workspace_geometry (MetaCompositor *compositor,
MetaWorkspace *workspace)
{
#ifdef HAVE_COMPOSITE_EXTENSIONS
MetaScreen *screen = meta_workspace_get_screen (workspace);
MetaCompScreen *info;
MetaCompositorClutterPluginManager *mgr;
info = meta_screen_get_compositor_data (screen);
mgr = info->plugin_mgr;
if (!mgr || !workspace)
return;
meta_compositor_clutter_plugin_manager_update_workspace (mgr, workspace);
#endif
}
static void
clutter_cmp_switch_workspace (MetaCompositor *compositor,
MetaScreen *screen,
MetaWorkspace *from,
MetaWorkspace *to)
{
#ifdef HAVE_COMPOSITE_EXTENSIONS
MetaCompScreen *info;
GList *l;
gint to_indx, from_indx;
info = meta_screen_get_compositor_data (screen);
to_indx = meta_workspace_index (to);
from_indx = meta_workspace_index (from);
l = info->windows;
while (l)
{
MetaCompWindow *cw = l->data;
MetaWindow *mw = cw->priv->window;
gboolean sticky;
gint workspace = -1;
sticky = (!mw || meta_window_is_on_all_workspaces (mw));
if (!sticky)
{
MetaWorkspace *w;
w = meta_window_get_workspace (cw->priv->window);
workspace = meta_workspace_index (w);
/*
* If the window is not on the target workspace, mark it for
* unmap.
*/
if (to_indx != workspace)
{
cw->priv->needs_unmap = TRUE;
}
else
{
cw->priv->needs_map = TRUE;
cw->priv->needs_unmap = FALSE;
}
}
/*
* Attach workspace number to the actor, so the plugin can use it.
*/
g_object_set_data (G_OBJECT (cw),
META_COMPOSITOR_CLUTTER_PLUGIN_WORKSPACE_KEY,
GINT_TO_POINTER (workspace));
l = l->next;
}
info->switch_workspace_in_progress++;
if (!info->plugin_mgr ||
!meta_compositor_clutter_plugin_manager_switch_workspace (
info->plugin_mgr,
(const GList **)&info->windows,
from_indx,
to_indx))
{
info->switch_workspace_in_progress--;
}
#endif
}
static MetaCompositor comp_info = { static MetaCompositor comp_info = {
clutter_cmp_destroy, clutter_cmp_destroy,
clutter_cmp_manage_screen, clutter_cmp_manage_screen,
@ -1651,7 +2043,11 @@ static MetaCompositor comp_info = {
clutter_cmp_get_window_pixmap, clutter_cmp_get_window_pixmap,
clutter_cmp_set_active_window, clutter_cmp_set_active_window,
clutter_cmp_destroy_window, clutter_cmp_destroy_window,
clutter_cmp_minimize_window clutter_cmp_minimize_window,
clutter_cmp_maximize_window,
clutter_cmp_unmaximize_window,
clutter_cmp_update_workspace_geometry,
clutter_cmp_switch_workspace
}; };
MetaCompositor * MetaCompositor *

View File

@ -26,7 +26,10 @@
#define META_COMPOSITOR_CLUTTER_H_ #define META_COMPOSITOR_CLUTTER_H_
#include "types.h" #include "types.h"
#include "compositor-clutter-plugin.h"
MetaCompositor *meta_compositor_clutter_new (MetaDisplay *display); MetaCompositor *meta_compositor_clutter_new (MetaDisplay *display);
void meta_compositor_clutter_window_effect_completed (ClutterActor *actor, gulong event);
#endif #endif

View File

@ -55,6 +55,28 @@ struct _MetaCompositor
void (*minimize_window) (MetaCompositor *compositor, void (*minimize_window) (MetaCompositor *compositor,
MetaWindow *window); MetaWindow *window);
void (*maximize_window) (MetaCompositor *compositor,
MetaWindow *window,
int x,
int y,
int width,
int height);
void (*unmaximize_window) (MetaCompositor *compositor,
MetaWindow *window,
int x,
int y,
int width,
int height);
void (*update_workspace_geometry) (MetaCompositor *compositor,
MetaWorkspace *workspace);
void (*switch_workspace) (MetaCompositor *compositor,
MetaScreen *screen,
MetaWorkspace *from,
MetaWorkspace *to);
}; };
#endif #endif

View File

@ -81,17 +81,6 @@ composite_at_least_version (MetaDisplay *display,
#endif #endif
typedef enum _MetaCompWindowType
{
META_COMP_WINDOW_NORMAL,
META_COMP_WINDOW_DND,
META_COMP_WINDOW_DESKTOP,
META_COMP_WINDOW_DOCK,
META_COMP_WINDOW_MENU,
META_COMP_WINDOW_DROP_DOWN_MENU,
META_COMP_WINDOW_TOOLTIP,
} MetaCompWindowType;
typedef enum _MetaShadowType typedef enum _MetaShadowType
{ {
META_SHADOW_SMALL, META_SHADOW_SMALL,

View File

@ -23,6 +23,7 @@
#include "compositor-private.h" #include "compositor-private.h"
#include "compositor-xrender.h" #include "compositor-xrender.h"
#include "compositor-clutter.h" #include "compositor-clutter.h"
#include "prefs.h"
#ifdef WITH_CLUTTER #ifdef WITH_CLUTTER
int meta_compositor_can_use_clutter__ = 0; int meta_compositor_can_use_clutter__ = 0;
@ -35,7 +36,7 @@ meta_compositor_new (MetaDisplay *display)
#ifdef WITH_CLUTTER #ifdef WITH_CLUTTER
/* At some point we would have a way to select between backends */ /* At some point we would have a way to select between backends */
/* return meta_compositor_xrender_new (display); */ /* return meta_compositor_xrender_new (display); */
if (meta_compositor_can_use_clutter__) if (meta_compositor_can_use_clutter__ && !meta_prefs_get_clutter_disabled ())
return meta_compositor_clutter_new (display); return meta_compositor_clutter_new (display);
else else
#endif #endif
@ -188,3 +189,55 @@ meta_compositor_minimize_window (MetaCompositor *compositor,
compositor->minimize_window (compositor, window); compositor->minimize_window (compositor, window);
#endif #endif
} }
void
meta_compositor_maximize_window (MetaCompositor *compositor,
MetaWindow *window,
int x,
int y,
int width,
int height)
{
#ifdef HAVE_COMPOSITE_EXTENSIONS
if (compositor && compositor->maximize_window)
compositor->maximize_window (compositor, window, x, y, width, height);
#endif
}
void
meta_compositor_unmaximize_window (MetaCompositor *compositor,
MetaWindow *window,
int x,
int y,
int width,
int height)
{
#ifdef HAVE_COMPOSITE_EXTENSIONS
if (compositor && compositor->unmaximize_window)
compositor->unmaximize_window (compositor, window, x, y, width, height);
#endif
}
void
meta_compositor_update_workspace_geometry (MetaCompositor *compositor,
MetaWorkspace *workspace)
{
#ifdef HAVE_COMPOSITE_EXTENSIONS
if (compositor && compositor->update_workspace_geometry)
compositor->update_workspace_geometry (compositor, workspace);
#endif
}
void
meta_compositor_switch_workspace (MetaCompositor *compositor,
MetaScreen *screen,
MetaWorkspace *from,
MetaWorkspace *to)
{
#ifdef HAVE_COMPOSITE_EXTENSIONS
if (compositor && compositor->switch_workspace)
compositor->switch_workspace (compositor, screen, from, to);
#endif
}

View File

@ -25,7 +25,7 @@
#include <config.h> #include <config.h>
#include "constraints.h" #include "constraints.h"
#include "workspace.h" #include "workspace-private.h"
#include "place.h" #include "place.h"
#include <stdlib.h> #include <stdlib.h>

View File

@ -26,7 +26,7 @@
#include <config.h> #include <config.h>
#include "core.h" #include "core.h"
#include "frame-private.h" #include "frame-private.h"
#include "workspace.h" #include "workspace-private.h"
#include "prefs.h" #include "prefs.h"
/* Looks up the MetaWindow representing the frame of the given X window. /* Looks up the MetaWindow representing the frame of the given X window.

View File

@ -49,7 +49,6 @@
typedef struct _MetaKeyBinding MetaKeyBinding; typedef struct _MetaKeyBinding MetaKeyBinding;
typedef struct _MetaStack MetaStack; typedef struct _MetaStack MetaStack;
typedef struct _MetaUISlave MetaUISlave; typedef struct _MetaUISlave MetaUISlave;
typedef struct _MetaWorkspace MetaWorkspace;
typedef struct _MetaWindowPropHooks MetaWindowPropHooks; typedef struct _MetaWindowPropHooks MetaWindowPropHooks;
typedef struct _MetaGroupPropHooks MetaGroupPropHooks; typedef struct _MetaGroupPropHooks MetaGroupPropHooks;

View File

@ -44,7 +44,7 @@
#include "prefs.h" #include "prefs.h"
#include "resizepopup.h" #include "resizepopup.h"
#include "xprops.h" #include "xprops.h"
#include "workspace.h" #include "workspace-private.h"
#include "bell.h" #include "bell.h"
#include "effects.h" #include "effects.h"
#include "compositor.h" #include "compositor.h"

View File

@ -25,7 +25,7 @@
#include "edge-resistance.h" #include "edge-resistance.h"
#include "boxes.h" #include "boxes.h"
#include "display-private.h" #include "display-private.h"
#include "workspace.h" #include "workspace-private.h"
/* A simple macro for whether a given window's edges are potentially /* A simple macro for whether a given window's edges are potentially
* relevant for resistance/snapping during a move/resize operation * relevant for resistance/snapping during a move/resize operation

View File

@ -28,7 +28,7 @@
#include <config.h> #include <config.h>
#include "keybindings.h" #include "keybindings.h"
#include "workspace.h" #include "workspace-private.h"
#include "errors.h" #include "errors.h"
#include "edge-resistance.h" #include "edge-resistance.h"
#include "ui.h" #include "ui.h"

View File

@ -63,6 +63,10 @@
#define KEY_WORKSPACE_NAME_PREFIX "/apps/metacity/workspace_names/name_" #define KEY_WORKSPACE_NAME_PREFIX "/apps/metacity/workspace_names/name_"
#ifdef WITH_CLUTTER
#define KEY_CLUTTER_DISABLED "/apps/metacity/general/clutter_disabled"
#define KEY_CLUTTER_PLUGINS "/apps/metacity/general/clutter_plugins"
#endif
#ifdef HAVE_GCONF #ifdef HAVE_GCONF
static GConfClient *default_client = NULL; static GConfClient *default_client = NULL;
@ -105,6 +109,11 @@ static char *terminal_command = NULL;
static char *workspace_names[MAX_REASONABLE_WORKSPACES] = { NULL, }; static char *workspace_names[MAX_REASONABLE_WORKSPACES] = { NULL, };
#ifdef WITH_CLUTTER
static gboolean clutter_disabled = FALSE;
static GSList *clutter_plugins = NULL;
#endif
#ifdef HAVE_GCONF #ifdef HAVE_GCONF
static gboolean handle_preference_update_enum (const gchar *key, GConfValue *value); static gboolean handle_preference_update_enum (const gchar *key, GConfValue *value);
@ -407,6 +416,13 @@ static MetaBoolPreference preferences_bool[] =
&compositing_manager, &compositing_manager,
FALSE, FALSE,
}, },
#ifdef WITH_CLUTTER
{ "/apps/metacity/general/clutter_disabled",
META_PREF_CLUTTER_DISABLED,
&clutter_disabled,
FALSE,
},
#endif
{ NULL, 0, NULL, FALSE }, { NULL, 0, NULL, FALSE },
}; };
@ -1018,6 +1034,13 @@ meta_prefs_init (void)
handle_preference_init_string (); handle_preference_init_string ();
handle_preference_init_int (); handle_preference_init_int ();
#ifdef WITH_CLUTTER
clutter_plugins = gconf_client_get_list (default_client, KEY_CLUTTER_PLUGINS,
GCONF_VALUE_STRING, &err);
cleanup_error (&err);
#endif
/* @@@ Is there any reason we don't do the add_dir here? */ /* @@@ Is there any reason we don't do the add_dir here? */
for (gconf_dir_cursor=gconf_dirs_we_are_interested_in; for (gconf_dir_cursor=gconf_dirs_we_are_interested_in;
*gconf_dir_cursor!=NULL; *gconf_dir_cursor!=NULL;
@ -1200,6 +1223,25 @@ change_notify (GConfClient *client,
if (update_workspace_name (key, str)) if (update_workspace_name (key, str))
queue_changed (META_PREF_WORKSPACE_NAMES); queue_changed (META_PREF_WORKSPACE_NAMES);
} }
#ifdef WITH_CLUTTER
else if (g_str_equal (key, KEY_CLUTTER_PLUGINS))
{
GError *err = NULL;
GSList *l;
l = gconf_client_get_list (default_client, KEY_CLUTTER_PLUGINS,
GCONF_VALUE_STRING, &err);
if (!l)
{
cleanup_error (&err);
goto out;
}
clutter_plugins = l;
queue_changed (META_PREF_CLUTTER_PLUGINS);
}
#endif
else else
{ {
meta_topic (META_DEBUG_PREFS, "Key %s doesn't mean anything to Metacity\n", meta_topic (META_DEBUG_PREFS, "Key %s doesn't mean anything to Metacity\n",
@ -1779,6 +1821,12 @@ meta_preference_to_string (MetaPreference pref)
case META_PREF_COMPOSITING_MANAGER: case META_PREF_COMPOSITING_MANAGER:
return "COMPOSITING_MANAGER"; return "COMPOSITING_MANAGER";
#ifdef WITH_CLUTTER
case META_PREF_CLUTTER_DISABLED:
return "CLUTTER_DISABLED";
case META_PREF_CLUTTER_PLUGINS:
return "CLUTTER_PLUGINS";
#endif
} }
return "(unknown)"; return "(unknown)";
@ -2923,6 +2971,61 @@ meta_prefs_set_compositing_manager (gboolean whether)
#endif #endif
} }
#ifdef WITH_CLUTTER
gboolean
meta_prefs_get_clutter_disabled (void)
{
return clutter_disabled;
}
void
meta_prefs_set_clutter_disabled (gboolean whether)
{
#ifdef HAVE_GCONF
GError *err = NULL;
gconf_client_set_bool (default_client,
KEY_CLUTTER_DISABLED,
whether,
&err);
if (err)
{
meta_warning (_("Error setting clutter status status: %s\n"),
err->message);
g_error_free (err);
}
#else
clutter_disabled = whether;
#endif
}
GSList *
meta_prefs_get_clutter_plugins (void)
{
return clutter_plugins;
}
void
meta_prefs_set_clutter_plugins (GSList *list)
{
GError *err = NULL;
gconf_client_set_list (default_client,
KEY_CLUTTER_PLUGINS,
GCONF_VALUE_STRING,
list,
&err);
if (err)
{
meta_warning (_("Error setting clutter plugin list: %s\n"),
err->message);
g_error_free (err);
}
}
#endif
#ifndef HAVE_GCONF #ifndef HAVE_GCONF
static void static void
init_button_layout(void) init_button_layout(void)

View File

@ -33,7 +33,7 @@
#include "window-private.h" #include "window-private.h"
#include "frame-private.h" #include "frame-private.h"
#include "prefs.h" #include "prefs.h"
#include "workspace.h" #include "workspace-private.h"
#include "keybindings.h" #include "keybindings.h"
#include "stack.h" #include "stack.h"
#include "xprops.h" #include "xprops.h"
@ -2800,3 +2800,10 @@ meta_screen_unset_cm_selection (MetaScreen *screen)
XSetSelectionOwner (screen->display->xdisplay, a, None, CurrentTime); XSetSelectionOwner (screen->display->xdisplay, a, None, CurrentTime);
} }
#endif /* HAVE_COMPOSITE_EXTENSIONS */ #endif /* HAVE_COMPOSITE_EXTENSIONS */
GList *
meta_screen_get_workspaces (MetaScreen *screen)
{
return screen->workspaces;
}

View File

@ -49,12 +49,6 @@ typedef struct _MetaWindowQueue MetaWindowQueue;
typedef gboolean (*MetaWindowForeachFunc) (MetaWindow *window, typedef gboolean (*MetaWindowForeachFunc) (MetaWindow *window,
void *data); void *data);
typedef enum
{
META_MAXIMIZE_HORIZONTAL = 1 << 0,
META_MAXIMIZE_VERTICAL = 1 << 1
} MetaMaximizeFlags;
typedef enum { typedef enum {
META_CLIENT_TYPE_UNKNOWN = 0, META_CLIENT_TYPE_UNKNOWN = 0,
META_CLIENT_TYPE_APPLICATION = 1, META_CLIENT_TYPE_APPLICATION = 1,

View File

@ -30,7 +30,7 @@
#include "util.h" #include "util.h"
#include "frame-private.h" #include "frame-private.h"
#include "errors.h" #include "errors.h"
#include "workspace.h" #include "workspace-private.h"
#include "stack.h" #include "stack.h"
#include "keybindings.h" #include "keybindings.h"
#include "ui.h" #include "ui.h"
@ -2582,11 +2582,25 @@ meta_window_maximize (MetaWindow *window,
directions, directions,
NULL); NULL);
if (window->display->compositor)
{
meta_window_move_resize_now (window);
meta_compositor_maximize_window (window->display->compositor,
window,
window->frame->rect.x,
window->frame->rect.y,
window->frame->rect.width,
window->frame->rect.height);
}
else
{
/* move_resize with new maximization constraints /* move_resize with new maximization constraints
*/ */
meta_window_queue(window, META_QUEUE_MOVE_RESIZE); meta_window_queue(window, META_QUEUE_MOVE_RESIZE);
} }
} }
}
static void static void
unmaximize_window_before_freeing (MetaWindow *window) unmaximize_window_before_freeing (MetaWindow *window)
@ -2681,12 +2695,32 @@ meta_window_unmaximize (MetaWindow *window,
window->display->grab_anchor_window_pos = target_rect; window->display->grab_anchor_window_pos = target_rect;
} }
if (window->display->compositor)
{
meta_window_move_resize (window, meta_window_move_resize (window,
FALSE, FALSE,
target_rect.x, target_rect.x,
target_rect.y, target_rect.y,
target_rect.width, target_rect.width,
target_rect.height); target_rect.height);
meta_window_move_resize_now (window);
meta_compositor_unmaximize_window (window->display->compositor,
window,
window->frame->rect.x,
window->frame->rect.y,
window->frame->rect.width,
window->frame->rect.height);
}
else
{
meta_window_move_resize (window,
FALSE,
target_rect.x,
target_rect.y,
target_rect.width,
target_rect.height);
}
if (window->display->grab_wireframe_active) if (window->display->grab_wireframe_active)
{ {
@ -8170,3 +8204,19 @@ meta_window_get_type_atom (MetaWindow *window)
{ {
return window->type_atom; return window->type_atom;
} }
MetaWorkspace *
meta_window_get_workspace (MetaWindow *window)
{
if (window->on_all_workspaces)
return window->screen->active_workspace;
return window->workspace;
}
gboolean
meta_window_is_on_all_workspaces (MetaWindow *window)
{
return window->on_all_workspaces;
}

View File

@ -30,9 +30,10 @@
* 02111-1307, USA. * 02111-1307, USA.
*/ */
#ifndef META_WORKSPACE_H #ifndef META_WORKSPACE_PRIVATE_H
#define META_WORKSPACE_H #define META_WORKSPACE_PRIVATE_H
#include "workspace.h"
#include "window-private.h" #include "window-private.h"
/* Negative to avoid conflicting with real workspace /* Negative to avoid conflicting with real workspace
@ -80,7 +81,6 @@ void meta_workspace_activate_with_focus (MetaWorkspace *workspace,
guint32 timestamp); guint32 timestamp);
void meta_workspace_activate (MetaWorkspace *workspace, void meta_workspace_activate (MetaWorkspace *workspace,
guint32 timestamp); guint32 timestamp);
int meta_workspace_index (MetaWorkspace *workspace);
GList* meta_workspace_list_windows (MetaWorkspace *workspace); GList* meta_workspace_list_windows (MetaWorkspace *workspace);
void meta_workspace_invalidate_work_area (MetaWorkspace *workspace); void meta_workspace_invalidate_work_area (MetaWorkspace *workspace);
@ -89,8 +89,6 @@ void meta_workspace_invalidate_work_area (MetaWorkspace *workspace);
void meta_workspace_get_work_area_for_xinerama (MetaWorkspace *workspace, void meta_workspace_get_work_area_for_xinerama (MetaWorkspace *workspace,
int which_xinerama, int which_xinerama,
MetaRectangle *area); MetaRectangle *area);
void meta_workspace_get_work_area_all_xineramas (MetaWorkspace *workspace,
MetaRectangle *area);
GList* meta_workspace_get_onscreen_region (MetaWorkspace *workspace); GList* meta_workspace_get_onscreen_region (MetaWorkspace *workspace);
GList* meta_workspace_get_onxinerama_region (MetaWorkspace *workspace, GList* meta_workspace_get_onxinerama_region (MetaWorkspace *workspace,
int which_xinerama); int which_xinerama);

View File

@ -25,8 +25,14 @@
#include <config.h> #include <config.h>
#include "workspace.h" #include "workspace.h"
#include "workspace-private.h"
#include "errors.h" #include "errors.h"
#include "prefs.h" #include "prefs.h"
#ifdef HAVE_COMPOSITE_EXTENSIONS
#include "compositor.h"
#endif
#include <X11/Xatom.h> #include <X11/Xatom.h>
#include <string.h> #include <string.h>
@ -383,6 +389,19 @@ meta_workspace_activate_with_focus (MetaWorkspace *workspace,
meta_topic (META_DEBUG_FOCUS, "Focusing default window on new workspace\n"); meta_topic (META_DEBUG_FOCUS, "Focusing default window on new workspace\n");
meta_workspace_focus_default_window (workspace, NULL, timestamp); meta_workspace_focus_default_window (workspace, NULL, timestamp);
} }
#ifdef HAVE_COMPOSITE_EXTENSIONS
{
/*
* Notify the compositor that the active workspace changed.
*/
MetaScreen *screen = workspace->screen;
MetaDisplay *display = meta_screen_get_display (screen);
MetaCompositor *comp = meta_display_get_compositor (display);
meta_compositor_switch_workspace (comp, screen, old, workspace);
}
#endif
} }
void void
@ -682,6 +701,19 @@ ensure_work_areas_validated (MetaWorkspace *workspace)
/* We're all done, YAAY! Record that everything has been validated. */ /* We're all done, YAAY! Record that everything has been validated. */
workspace->work_areas_invalid = FALSE; workspace->work_areas_invalid = FALSE;
#ifdef HAVE_COMPOSITE_EXTENSIONS
{
/*
* Notify the compositor that the workspace geometry has changed.
*/
MetaScreen *screen = workspace->screen;
MetaDisplay *display = meta_screen_get_display (screen);
MetaCompositor *comp = meta_display_get_compositor (display);
meta_compositor_update_workspace_geometry (comp, workspace);
}
#endif
} }
void void
@ -974,3 +1006,10 @@ focus_ancestor_or_mru_window (MetaWorkspace *workspace,
timestamp); timestamp);
} }
} }
MetaScreen *
meta_workspace_get_screen (MetaWorkspace *workspace)
{
return workspace->screen;
}

View File

@ -0,0 +1,257 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (c) 2008 Intel Corp.
*
* Author: Tomas Frydrych <tf@linux.intel.com>
*
* 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_COMPOSITOR_CLUTTER_PLUGIN_H_
#define META_COMPOSITOR_CLUTTER_PLUGIN_H_
#include "types.h"
#include "config.h"
#include "compositor.h"
#include <clutter/clutter.h>
/*
* This file defines the plugin API.
*
* Effects plugin is shared library loaded via dlopen(); it is recommended
* that the GModule API is used (otherwise you are on your own to do proper
* plugin clean up when the module is unloaded).
*
* The plugin interface is exported via the MetaCompositorClutterPlugin struct.
*/
/*
* Alias MetaRectangle to PluginWorkspaceRectangle in anticipation of
* making this file metacity-independent (we want the plugins to be portable
* between different WMs.
*/
typedef MetaRectangle PluginWorkspaceRectangle;
/*
* The name of the header struct; use as:
*
* MetaCompositorClutterPlugin META_COMPOSITOR_CLUTTER_PLUGIN_STRUCT =
* {
* ...
* };
*
* See clutter-plugins/simple.c for example code.
*/
#define META_COMPOSITOR_CLUTTER_PLUGIN_STRUCT MCCPS__
/*
* Definition for the plugin init function; use as:
*
* META_COMPOSITOR_CLUTTER_PLUGIN_INIT_FUNC
* {
* init code ...
* }
*
* See clutter-plugins/simple.c for example code.
*/
#define META_COMPOSITOR_CLUTTER_PLUGIN_INIT_FUNC \
gboolean mccp_init__(void); \
gboolean mccp_init__()
/* Private; must match the above */
#define META_COMPOSITOR_CLUTTER_PLUGIN_STRUCT_NAME "MCCPS__"
#define META_COMPOSITOR_CLUTTER_PLUGIN_INIT_FUNC_NAME "mccp_init__"
typedef struct MetaCompositorClutterPlugin MetaCompositorClutterPlugin;
/*
* Feature flags: identify events that the plugin can handle; a plugin can
* handle one or more events.
*/
#define META_COMPOSITOR_CLUTTER_PLUGIN_MINIMIZE 0x00000001UL
#define META_COMPOSITOR_CLUTTER_PLUGIN_MAXIMIZE 0x00000002UL
#define META_COMPOSITOR_CLUTTER_PLUGIN_UNMAXIMIZE 0x00000004UL
#define META_COMPOSITOR_CLUTTER_PLUGIN_MAP 0x00000008UL
#define META_COMPOSITOR_CLUTTER_PLUGIN_DESTROY 0x00000010UL
#define META_COMPOSITOR_CLUTTER_PLUGIN_SWITCH_WORKSPACE 0x00000020UL
#define META_COMPOSITOR_CLUTTER_PLUGIN_ALL_EFFECTS 0xffffffffUL
/*
* A key that the switch_workspace() handler can use to retrive workspace
* the actor is on.
*/
#define META_COMPOSITOR_CLUTTER_PLUGIN_WORKSPACE_KEY "MCCP-Manager-workspace"
struct MetaCompositorClutterPlugin
{
/*
* Version information; the first three numbers match the Metacity version
* with which the plugin was compiled (see clutter-plugins/simple.c for sample
* code).
*/
guint version_major;
guint version_minor;
guint version_micro;
/*
* Version of the plugin API; this is unrelated to the matacity version
* per se. The API version is checked by the plugin manager and must match
* the one used by it (see clutter-plugins/simple.c for sample code).
*/
guint version_api;
#ifndef META_COMPOSITOR_CLUTTER_BUILDING_PLUGIN
const
#endif
gchar *name; /* Human-readable name for UI */
gulong features; /* or-ed feature flags */
/*
* Event handlers
*
* Plugins must not make any special assumptions about the nature of
* ClutterActor, as the implementation details can change.
*
* Plugins must restore actor properties on completion (i.e., fade effects
* must restore opacity back to the original value, scale effects scale,
* etc.).
*
* On completion, each event handler must call the manager completed()
* callback function.
*/
void (*minimize) (ClutterActor *actor,
MetaCompWindowType type,
gint workspace);
void (*maximize) (ClutterActor *actor,
MetaCompWindowType type,
gint workspace,
gint x,
gint y,
gint width,
gint height);
void (*unmaximize) (ClutterActor *actor,
MetaCompWindowType type,
gint workspace,
gint x,
gint y,
gint width,
gint height);
void (*map) (ClutterActor *actor,
MetaCompWindowType type,
gint workspace);
void (*destroy) (ClutterActor *actor,
MetaCompWindowType type,
gint workspace);
/*
* Each actor in the list has a workspace number attached to it using
* g_object_set_data() with key META_COMPOSITOR_CLUTTER_PLUGIN_WORKSPACE_KEY;
* workspace < 0 indicates the window is sticky (i.e., on all desktops).
*/
void (*switch_workspace) (const GList **actors,
gint from,
gint to);
/*
* Called if an effect should be killed prematurely; the plugin must
* call the completed() callback as if the effect terminated naturally.
* The events parameter is a bitmask indicating which effects are to be
* killed.
*/
void (*kill_effect) (ClutterActor *actor,
gulong events);
/*
* The plugin manager will call this function when module should be reloaded.
* This happens, for example, when the parameters for the plugin changed.
*/
gboolean (*reload) (void);
#ifdef META_COMPOSITOR_CLUTTER_BUILDING_PLUGIN
const
#endif
gchar *params; /* String containing additional parameters for the plugin;
* this is specified after the pluing name in the gconf
* database, separated by a colon.
*
* The following parameter tokens need to be handled by all
* plugins:
*
* 'debug'
* Indicates running in debug mode; the plugin
* might want to print useful debug info, or
* extend effect duration, etc.
*
* 'disable: ...;'
*
* The disable token indicates that the effects
* listed after the colon should be disabled.
*
* The list is comma-separated, terminated by a
* semicolon and consisting of the following
* tokens:
*
* minimize
* maximize
* unmaximize
* map
* destroy
* switch-workspace
*/
ClutterActor *stage;
gint screen_width;
gint screen_height;
GList *work_areas; /* List of PluginWorkspaceRectangles defining the
* geometry of individual workspaces.
*/
gint running; /* Plugin must increase this counter for each effect it starts
* decrease it again once the effect finishes.
*/
void *plugin_private; /* Plugin private data go here; use the plugin init
* function to allocate and initialize any private
* data.
*/
/*
* Manager callback for completed effects; this function must be called on the
* completion of each effect.
*
* For switch-workspace effect the plugin might pass back any actor from the
* actor list, but the actor parameter must not be NULL.
*/
void (*completed) (MetaCompositorClutterPlugin *plugin,
ClutterActor *actor,
unsigned long event);
/* Private; manager private data. */
void *manager_private;
};
#endif

View File

@ -27,6 +27,28 @@
#include "types.h" #include "types.h"
#include "boxes.h" #include "boxes.h"
#include "window.h"
#include "workspace.h"
typedef enum _MetaCompWindowType
{
/*
* Types shared with MetaWindow
*/
META_COMP_WINDOW_NORMAL = META_WINDOW_NORMAL,
META_COMP_WINDOW_DESKTOP = META_WINDOW_DESKTOP,
META_COMP_WINDOW_DOCK = META_WINDOW_DOCK,
META_COMP_WINDOW_MENU = META_WINDOW_MENU,
/*
* Extended types that WM does not care about, but we do.
*/
META_COMP_WINDOW_TOOLTIP = 0xf000,
META_COMP_WINDOW_DROP_DOWN_MENU,
META_COMP_WINDOW_DND,
META_COMP_WINDOW_OVERRIDE,
} MetaCompWindowType;
#ifdef WITH_CLUTTER #ifdef WITH_CLUTTER
extern int meta_compositor_can_use_clutter__; extern int meta_compositor_can_use_clutter__;
@ -79,6 +101,31 @@ void
meta_compositor_minimize_window (MetaCompositor *compositor, meta_compositor_minimize_window (MetaCompositor *compositor,
MetaWindow *window); MetaWindow *window);
void
meta_compositor_maximize_window (MetaCompositor *compositor,
MetaWindow *window,
int x,
int y,
int width,
int height);
void
meta_compositor_unmaximize_window (MetaCompositor *compositor,
MetaWindow *window,
int x,
int y,
int width,
int height);
void
meta_compositor_update_workspace_geometry (MetaCompositor *compositor,
MetaWorkspace *workspace);
void
meta_compositor_switch_workspace (MetaCompositor *compositor,
MetaScreen *screen,
MetaWorkspace *from,
MetaWorkspace *to);
#endif #endif

View File

@ -59,7 +59,11 @@ typedef enum
META_PREF_GNOME_ANIMATIONS, META_PREF_GNOME_ANIMATIONS,
META_PREF_CURSOR_THEME, META_PREF_CURSOR_THEME,
META_PREF_CURSOR_SIZE, META_PREF_CURSOR_SIZE,
META_PREF_COMPOSITING_MANAGER META_PREF_COMPOSITING_MANAGER,
#ifdef WITH_CLUTTER
META_PREF_CLUTTER_DISABLED,
META_PREF_CLUTTER_PLUGINS
#endif
} MetaPreference; } MetaPreference;
typedef void (* MetaPrefsChangedFunc) (MetaPreference pref, typedef void (* MetaPrefsChangedFunc) (MetaPreference pref,
@ -120,6 +124,21 @@ gboolean meta_prefs_get_compositing_manager (void);
*/ */
void meta_prefs_set_compositing_manager (gboolean whether); void meta_prefs_set_compositing_manager (gboolean whether);
#ifdef WITH_CLUTTER
gboolean meta_prefs_get_clutter_disabled (void);
void meta_prefs_set_clutter_disabled (gboolean whether);
GSList * meta_prefs_get_clutter_plugins (void);
/**
* Sets whether the compositor is turned on.
*
* \param whether TRUE to turn on, FALSE to turn off
*/
void meta_prefs_set_clutter_plugins (GSList *list);
#endif
/* Screen bindings */ /* Screen bindings */
#define META_KEYBINDING_WORKSPACE_1 "switch_to_workspace_1" #define META_KEYBINDING_WORKSPACE_1 "switch_to_workspace_1"
#define META_KEYBINDING_WORKSPACE_2 "switch_to_workspace_2" #define META_KEYBINDING_WORKSPACE_2 "switch_to_workspace_2"

View File

@ -45,4 +45,5 @@ void meta_screen_set_cm_selection (MetaScreen *screen);
void meta_screen_unset_cm_selection (MetaScreen *screen); void meta_screen_unset_cm_selection (MetaScreen *screen);
#endif #endif
GList *meta_screen_get_workspaces (MetaScreen *screen);
#endif #endif

View File

@ -27,5 +27,5 @@ typedef struct _MetaDisplay MetaDisplay;
typedef struct _MetaFrame MetaFrame; typedef struct _MetaFrame MetaFrame;
typedef struct _MetaScreen MetaScreen; typedef struct _MetaScreen MetaScreen;
typedef struct _MetaWindow MetaWindow; typedef struct _MetaWindow MetaWindow;
typedef struct _MetaWorkspace MetaWorkspace;
#endif #endif

View File

@ -41,6 +41,12 @@ typedef enum
META_WINDOW_SPLASHSCREEN, META_WINDOW_SPLASHSCREEN,
} MetaWindowType; } MetaWindowType;
typedef enum
{
META_MAXIMIZE_HORIZONTAL = 1 << 0,
META_MAXIMIZE_VERTICAL = 1 << 1
} MetaMaximizeFlags;
MetaFrame *meta_window_get_frame (MetaWindow *window); MetaFrame *meta_window_get_frame (MetaWindow *window);
gboolean meta_window_has_focus (MetaWindow *window); gboolean meta_window_has_focus (MetaWindow *window);
gboolean meta_window_is_shaded (MetaWindow *window); gboolean meta_window_is_shaded (MetaWindow *window);
@ -50,5 +56,6 @@ MetaDisplay *meta_window_get_display (MetaWindow *window);
Window meta_window_get_xwindow (MetaWindow *window); Window meta_window_get_xwindow (MetaWindow *window);
MetaWindowType meta_window_get_type (MetaWindow *window); MetaWindowType meta_window_get_type (MetaWindow *window);
Atom meta_window_get_type_atom (MetaWindow *window); Atom meta_window_get_type_atom (MetaWindow *window);
MetaWorkspace *meta_window_get_workspace (MetaWindow *window);
gboolean meta_window_is_on_all_workspaces (MetaWindow *window);
#endif #endif

44
src/include/workspace.h Normal file
View File

@ -0,0 +1,44 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/**
* \file workspace.h Workspaces
*
* A workspace is a set of windows which all live on the same
* screen. (You may also see the name "desktop" around the place,
* which is the EWMH's name for the same thing.) Only one workspace
* of a screen may be active at once; all windows on all other workspaces
* are unmapped.
*/
/*
* Copyright (C) 2001 Havoc Pennington
* Copyright (C) 2004, 2005 Elijah Newren
*
* 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_WORKSPACE_H
#define META_WORKSPACE_H
#include "types.h"
#include "boxes.h"
#include "screen.h"
int meta_workspace_index (MetaWorkspace *workspace);
MetaScreen *meta_workspace_get_screen (MetaWorkspace *workspace);
void meta_workspace_get_work_area_all_xineramas (MetaWorkspace *workspace,
MetaRectangle *area);
#endif

View File

@ -340,6 +340,36 @@
</locale> </locale>
</schema> </schema>
<schema>
<key>/schemas/apps/metacity/general/clutter_disabled</key>
<applyto>/apps/metacity/general/clutter_disabled</applyto>
<owner>metacity</owner>
<type>bool</type>
<default>false</default>
<locale name="C">
<short>Clutter Disabled</short>
<long>
Determines whether Clutter compositing backend should be disabled.
Change to this option will not take effect until Metacity is
restarted.
</long>
</locale>
</schema>
<schema>
<key>/schemas/apps/metacity/general/clutter_plugins</key>
<applyto>/apps/metacity/general/clutter_plugins</applyto>
<owner>metacity</owner>
<type>list</type>
<default></default>
<locale name="C">
<short>Clutter Plugins</short>
<long>
Plugins to load for the Clutter-based compositing manager.
</long>
</locale>
</schema>
<schema> <schema>
<key>/schemas/apps/metacity/workspace_names/name</key> <key>/schemas/apps/metacity/workspace_names/name</key>
<applyto>/apps/metacity/workspace_names/name_1</applyto> <applyto>/apps/metacity/workspace_names/name_1</applyto>

View File

@ -30,7 +30,7 @@
#include "tabpopup.h" #include "tabpopup.h"
/* FIXME these two includes are 100% broken ... /* FIXME these two includes are 100% broken ...
*/ */
#include "../core/workspace.h" #include "../core/workspace-private.h"
#include "../core/frame-private.h" #include "../core/frame-private.h"
#include "draw-workspace.h" #include "draw-workspace.h"
#include <gtk/gtk.h> #include <gtk/gtk.h>