From 9b3a0d1ad8f7aff6776326a95a4b3633b10caa92 Mon Sep 17 00:00:00 2001 From: Tomas Frydrych Date: Thu, 18 Sep 2008 16:09:11 +0100 Subject: [PATCH] Basic plugin infastructure and a sample simple plugin. --- .gitignore | 60 ++ configure.in | 14 + src/Makefile.am | 13 +- src/compositor/clutter-plugins/Makefile.am | 21 + src/compositor/clutter-plugins/README | 41 + src/compositor/clutter-plugins/simple.c | 811 ++++++++++++++++++ .../compositor-clutter-plugin-manager.c | 644 ++++++++++++++ .../compositor-clutter-plugin-manager.h | 58 ++ src/compositor/compositor-clutter.c | 700 +++++++++++---- src/compositor/compositor-clutter.h | 3 + src/compositor/compositor-private.h | 22 + src/compositor/compositor-xrender.c | 11 - src/compositor/compositor.c | 55 +- src/core/constraints.c | 2 +- src/core/core.c | 2 +- src/core/display-private.h | 1 - src/core/display.c | 2 +- src/core/edge-resistance.c | 2 +- src/core/keybindings.c | 2 +- src/core/prefs.c | 317 ++++--- src/core/screen.c | 9 +- src/core/window-private.h | 6 - src/core/window.c | 70 +- src/core/{workspace.h => workspace-private.h} | 8 +- src/core/workspace.c | 39 + src/include/compositor-clutter-plugin.h | 257 ++++++ src/include/compositor.h | 47 + src/include/prefs.h | 27 +- src/include/screen.h | 1 + src/include/types.h | 2 +- src/include/window.h | 9 +- src/include/workspace.h | 44 + src/metacity.schemas.in | 30 + src/ui/tabpopup.c | 2 +- 34 files changed, 3023 insertions(+), 309 deletions(-) create mode 100644 .gitignore create mode 100644 src/compositor/clutter-plugins/Makefile.am create mode 100644 src/compositor/clutter-plugins/README create mode 100644 src/compositor/clutter-plugins/simple.c create mode 100644 src/compositor/compositor-clutter-plugin-manager.c create mode 100644 src/compositor/compositor-clutter-plugin-manager.h rename src/core/{workspace.h => workspace-private.h} (93%) create mode 100644 src/include/compositor-clutter-plugin.h create mode 100644 src/include/workspace.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..31803c7e0 --- /dev/null +++ b/.gitignore @@ -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 diff --git a/configure.in b/configure.in index a4eb448dd..262bfa0ef 100644 --- a/configure.in +++ b/configure.in @@ -8,6 +8,9 @@ m4_define([metacity_micro_version], [2]) m4_define([metacity_version], [metacity_major_version.metacity_minor_version.metacity_micro_version]) + +m4_define([metacity_clutter_plugin_api_version], [1]) + AC_INIT([metacity], [metacity_version], [http://bugzilla.gnome.org/enter_bug.cgi?product=metacity]) @@ -17,6 +20,15 @@ AC_CONFIG_HEADERS(config.h) AM_INIT_AUTOMAKE 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 AC_SUBST(ACLOCAL_AMFLAGS, "\${ACLOCAL_FLAGS}") @@ -290,6 +302,7 @@ fi if test x$have_clutter = xyes; then CLUTTER_PACKAGE=clutter-0.8 METACITY_PC_MODULES="$METACITY_PC_MODULES $CLUTTER_PACKAGE " + PKG_CHECK_MODULES(CLUTTER, $CLUTTER_PACKAGE) AC_DEFINE(WITH_CLUTTER, , [Building with Clutter compositor]) fi @@ -509,6 +522,7 @@ src/wm-tester/Makefile src/libmetacity-private.pc src/tools/Makefile src/themes/Makefile +src/compositor/clutter-plugins/Makefile po/Makefile.in ]) diff --git a/src/Makefile.am b/src/Makefile.am index dfcc6df81..b6ff5a17b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -2,7 +2,11 @@ lib_LTLIBRARIES = libmetacity-private.la 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= \ core/async-getprop.c \ @@ -99,8 +103,11 @@ metacity_SOURCES= \ ui/ui.c if WITH_CLUTTER -metacity_SOURCES += compositor/compositor-clutter.c \ - compositor/compositor-clutter.h +metacity_SOURCES += compositor/compositor-clutter.c \ + compositor/compositor-clutter.h \ + include/compositor-clutter-plugin.h \ + compositor/compositor-clutter-plugin-manager.c \ + compositor/compositor-clutter-plugin-manager.h endif # by setting libmetacity_private_la_CFLAGS, the files shared with diff --git a/src/compositor/clutter-plugins/Makefile.am b/src/compositor/clutter-plugins/Makefile.am new file mode 100644 index 000000000..f064eaca1 --- /dev/null +++ b/src/compositor/clutter-plugins/Makefile.am @@ -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 diff --git a/src/compositor/clutter-plugins/README b/src/compositor/clutter-plugins/README new file mode 100644 index 000000000..071d62438 --- /dev/null +++ b/src/compositor/clutter-plugins/README @@ -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;'. + diff --git a/src/compositor/clutter-plugins/simple.c b/src/compositor/clutter-plugins/simple.c new file mode 100644 index 000000000..28fe86c4d --- /dev/null +++ b/src/compositor/clutter-plugins/simple.c @@ -0,0 +1,811 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +/* + * Copyright (c) 2008 Intel Corp. + * + * Author: Tomas Frydrych + * + * 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 +#define _(x) dgettext (GETTEXT_PACKAGE, x) +#define N_(x) x + +#include +#include +#include + +#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); +} diff --git a/src/compositor/compositor-clutter-plugin-manager.c b/src/compositor/compositor-clutter-plugin-manager.c new file mode 100644 index 000000000..8690d3332 --- /dev/null +++ b/src/compositor/compositor-clutter-plugin-manager.c @@ -0,0 +1,644 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +/* + * Copyright (c) 2008 Intel Corp. + * + * Author: Tomas Frydrych + * + * 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 +#include + +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; +} diff --git a/src/compositor/compositor-clutter-plugin-manager.h b/src/compositor/compositor-clutter-plugin-manager.h new file mode 100644 index 000000000..3df20e466 --- /dev/null +++ b/src/compositor/compositor-clutter-plugin-manager.h @@ -0,0 +1,58 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +/* + * Copyright (c) 2008 Intel Corp. + * + * Author: Tomas Frydrych + * + * 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 diff --git a/src/compositor/compositor-clutter.c b/src/compositor/compositor-clutter.c index 13c5975fd..cc008fa76 100644 --- a/src/compositor/compositor-clutter.c +++ b/src/compositor/compositor-clutter.c @@ -17,6 +17,7 @@ #include "window.h" #include "compositor-private.h" #include "compositor-clutter.h" +#include "compositor-clutter-plugin-manager.h" #include "xprops.h" #include #include @@ -41,16 +42,13 @@ #define TILE_WIDTH (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 * query window attributes in the MetaCompWindow constructor but can pass * them as a property to the constructor (so we can gracefully handle the case * 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. */ #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)); } - #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 { MetaCompositor compositor; @@ -157,8 +136,9 @@ typedef struct _MetaCompScreen Window output; GSList *dock_windows; - ClutterEffectTemplate *destroy_effect; - ClutterEffectTemplate *minimize_effect; + gint switch_workspace_in_progress; + + MetaCompositorClutterPluginManager *plugin_mgr; } MetaCompScreen; /* @@ -204,12 +184,28 @@ struct _MetaCompWindowPrivate guint8 opacity; - gboolean needs_shadow : 1; - gboolean shaped : 1; - gboolean destroy_pending : 1; - gboolean argb32 : 1; - gboolean minimize_in_progress : 1; - gboolean disposed : 1; + /* + * 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 shaped : 1; + gboolean destroy_pending : 1; + gboolean argb32 : 1; + gboolean disposed : 1; + gboolean is_minimized : 1; + + /* Desktop switching flags */ + gboolean needs_map : 1; + gboolean needs_unmap : 1; + gboolean needs_repair : 1; }; enum @@ -515,6 +511,12 @@ meta_comp_window_get_window_type (MetaCompWindow *self) Atom *atoms; 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, * otherwise do it the hard way. @@ -613,7 +615,7 @@ meta_comp_window_has_shadow (MetaCompWindow *self) 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) { @@ -685,6 +687,170 @@ meta_comp_window_has_shadow (MetaCompWindow *self) 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 clutter_cmp_destroy (MetaCompositor *compositor) @@ -694,19 +860,23 @@ clutter_cmp_destroy (MetaCompositor *compositor) #endif } +/* + * If force is TRUE, free the back pixmap; if FALSE, only free it if the + * backing pixmap has actually changed. + */ static void meta_comp_window_detach (MetaCompWindow *self) { - MetaCompWindowPrivate *priv = self->priv; - MetaScreen *screen = priv->screen; - MetaDisplay *display = meta_screen_get_display (screen); - Display *xdisplay = meta_display_get_xdisplay (display); + MetaCompWindowPrivate *priv = self->priv; + MetaScreen *screen = priv->screen; + MetaDisplay *display = meta_screen_get_display (screen); + Display *xdisplay = meta_display_get_xdisplay (display); - if (priv->back_pixmap) - { - XFreePixmap (xdisplay, priv->back_pixmap); - priv->back_pixmap = None; - } + if (!priv->back_pixmap) + return; + + XFreePixmap (xdisplay, priv->back_pixmap); + priv->back_pixmap = None; } static void @@ -719,8 +889,6 @@ destroy_win (MetaDisplay *display, Window xwindow) if (cw == NULL) return; - meta_verbose ("destroying a window... 0x%x (%p)\n", (guint) xwindow, cw); - clutter_actor_destroy (CLUTTER_ACTOR (cw)); } @@ -752,7 +920,8 @@ restack_win (MetaCompWindow *cw, Window above) info->windows = g_list_delete_link (info->windows, sibling); info->windows = g_list_append (info->windows, cw); - clutter_actor_raise_top (CLUTTER_ACTOR (cw)); + if (!info->switch_workspace_in_progress) + clutter_actor_raise_top (CLUTTER_ACTOR (cw)); } else if (previous_above != above) { @@ -772,7 +941,8 @@ restack_win (MetaCompWindow *cw, Window above) info->windows = g_list_delete_link (info->windows, sibling); info->windows = g_list_insert_before (info->windows, index, cw); - clutter_actor_raise (CLUTTER_ACTOR (cw), above_win); + if (!info->switch_workspace_in_progress) + clutter_actor_raise (CLUTTER_ACTOR (cw), above_win); } } } @@ -790,60 +960,116 @@ resize_win (MetaCompWindow *cw, priv->attrs.x = x; 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.height = height; priv->attrs.border_width = border_width; 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 -map_win (MetaDisplay *display, MetaScreen *screen, Window id) +map_win (MetaCompWindow *cw) { - MetaCompWindow *cw = find_window_for_screen (screen, id); MetaCompWindowPrivate *priv; + MetaCompScreen *info; if (cw == NULL) return; priv = cw->priv; + info = meta_screen_get_compositor_data (priv->screen); + + if (priv->attrs.map_state == IsViewable) + return; 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 -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; + MetaCompScreen *info; if (cw == NULL) return; 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) 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; - meta_comp_window_detach (cw); - 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_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) { meta_verbose ("Appending 0x%x to dock windows\n", (guint)xwindow); @@ -940,18 +1165,23 @@ add_win (MetaScreen *screen, MetaWindow *window, Window xwindow) g_hash_table_insert (info->windows_by_xid, (gpointer) xwindow, cw); 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 repair_win (MetaCompWindow *cw) { - MetaCompWindowPrivate *priv = cw->priv; - MetaScreen *screen = priv->screen; - MetaDisplay *display = meta_screen_get_display (screen); + MetaCompWindowPrivate *priv = cw->priv; + MetaScreen *screen = priv->screen; + MetaDisplay *display = meta_screen_get_display (screen); Display *xdisplay = meta_display_get_xdisplay (display); - MetaCompScreen *info = meta_screen_get_compositor_data (screen); - Window xwindow = priv->xwindow; + MetaCompScreen *info = meta_screen_get_compositor_data (screen); + Window xwindow = priv->xwindow; + gboolean full = FALSE; if (xwindow == meta_screen_get_xroot (screen) || xwindow == clutter_x11_get_stage_window (CLUTTER_STAGE (info->stage))) @@ -962,8 +1192,23 @@ repair_win (MetaCompWindow *cw) if (priv->back_pixmap == None) { gint pxm_width, pxm_height; + XWindowAttributes attr; - priv->back_pixmap = XCompositeNameWindowPixmap (xdisplay, xwindow); + meta_error_trap_push (display); + + XGrabServer (xdisplay); + + XGetWindowAttributes (xdisplay, xwindow, &attr); + + if (attr.map_state == IsViewable) + priv->back_pixmap = XCompositeNameWindowPixmap (xdisplay, xwindow); + else + { + priv->back_pixmap = None; + } + + XUngrabServer (xdisplay); + meta_error_trap_pop (display, FALSE); if (priv->back_pixmap == None) { @@ -984,6 +1229,8 @@ repair_win (MetaCompWindow *cw) if (priv->shadow) 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 * the texture rebind). */ - if (CLUTTER_GLX_IS_TEXTURE_PIXMAP (priv->actor) && - clutter_glx_texture_pixmap_using_extension ( - CLUTTER_GLX_TEXTURE_PIXMAP (priv->actor))) + if (full || + (CLUTTER_GLX_IS_TEXTURE_PIXMAP (priv->actor) && + clutter_glx_texture_pixmap_using_extension ( + CLUTTER_GLX_TEXTURE_PIXMAP (priv->actor)))) { XDamageSubtract (xdisplay, priv->damage, None, None); @@ -1041,6 +1289,8 @@ repair_win (MetaCompWindow *cw) } meta_error_trap_pop (display, FALSE); + + priv->needs_repair = FALSE; } @@ -1109,9 +1359,20 @@ process_damage (MetaCompositorClutter *compositor, priv = cw->priv; - if (priv->destroy_pending) - return; + if (priv->destroy_pending || + priv->maximize_in_progress || + priv->unmaximize_in_progress) + { + priv->needs_repair = TRUE; + 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)) { priv->destroy_pending = TRUE; @@ -1138,6 +1399,9 @@ process_configure_notify (MetaCompositorClutter *compositor, } else { + /* + * Check for root window geometry change + */ GSList *l = meta_display_get_screens (display); while (l) @@ -1188,8 +1452,8 @@ process_circulate_notify (MetaCompositorClutter *compositor, above = top->priv->xwindow; else above = None; - restack_win (cw, above); + restack_win (cw, above); } static void @@ -1213,7 +1477,7 @@ process_unmap (MetaCompositorClutter *compositor, XEvent next; MetaCompWindowPrivate *priv = cw->priv; - if (priv->destroy_pending) + if (priv->attrs.map_state == IsUnmapped || priv->destroy_pending) return; 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); - unmap_win (compositor->display, priv->screen, xwin); + unmap_win (cw); } } @@ -1237,7 +1501,7 @@ process_map (MetaCompositorClutter *compositor, event->window); if (cw) - map_win (compositor->display, cw->priv->screen, event->window); + map_win (cw); } static void @@ -1374,21 +1638,13 @@ clutter_cmp_manage_screen (MetaCompositor *compositor, 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); /* Now we're up and running we can show the output if needed */ 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 } @@ -1531,22 +1787,16 @@ clutter_cmp_set_active_window (MetaCompositor *compositor, #endif } -static void -on_destroy_effect_complete (ClutterActor *actor, - gpointer user_data) -{ - clutter_actor_destroy (actor); -} - static void clutter_cmp_destroy_window (MetaCompositor *compositor, MetaWindow *window) { #ifdef HAVE_COMPOSITE_EXTENSIONS - MetaCompWindow *cw = NULL; - MetaScreen *screen = meta_window_get_screen (window); - MetaCompScreen *info = meta_screen_get_compositor_data (screen); - MetaFrame *f = meta_window_get_frame (window); + MetaCompWindow *cw = NULL; + MetaScreen *screen = meta_window_get_screen (window); + MetaCompScreen *info = meta_screen_get_compositor_data (screen); + MetaFrame *f = meta_window_get_frame (window); + MetaCompWindowPrivate *priv; /* Chances are we actually get the window frame here */ cw = find_window_for_screen (screen, @@ -1555,6 +1805,8 @@ clutter_cmp_destroy_window (MetaCompositor *compositor, if (!cw) return; + priv = cw->priv; + /* * We remove the window from internal lookup hashes and thus any other * unmap events etc fail @@ -1564,38 +1816,22 @@ clutter_cmp_destroy_window (MetaCompositor *compositor, (gpointer) (f ? meta_frame_get_xwindow (f) : 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)); - clutter_actor_set_opacity (CLUTTER_ACTOR (cw), cw->priv->opacity); - clutter_actor_set_scale (CLUTTER_ACTOR (cw), 1.0, 1.0); - clutter_actor_move_anchor_point_from_gravity (CLUTTER_ACTOR (cw), - CLUTTER_GRAVITY_NORTH_WEST); + priv->destroy_in_progress++; + + if (!info->plugin_mgr || + !meta_compositor_clutter_plugin_manager_event_0 (info->plugin_mgr, + 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 @@ -1617,29 +1853,185 @@ clutter_cmp_minimize_window (MetaCompositor *compositor, MetaWindow *window) if (!cw) 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 (cw), + META_COMPOSITOR_CLUTTER_PLUGIN_MINIMIZE, + cw->priv->type, 0)) + { + ClutterActor *a = CLUTTER_ACTOR (cw); + gint height = clutter_actor_get_height (a); - clutter_actor_move_anchor_point_from_gravity (CLUTTER_ACTOR (cw), - CLUTTER_GRAVITY_SOUTH_WEST); - - clutter_effect_fade (info->minimize_effect, - CLUTTER_ACTOR (cw), - 0, - on_minimize_effect_complete, - (gpointer)cw); - - clutter_effect_scale (info->minimize_effect, - CLUTTER_ACTOR (cw), - 0.0, - 0.0, - NULL, - NULL); + cw->priv->is_minimized = TRUE; + cw->priv->minimize_in_progress--; + clutter_actor_set_position (a, 0, -height); + } #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 = { clutter_cmp_destroy, clutter_cmp_manage_screen, @@ -1651,7 +2043,11 @@ static MetaCompositor comp_info = { clutter_cmp_get_window_pixmap, clutter_cmp_set_active_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 * diff --git a/src/compositor/compositor-clutter.h b/src/compositor/compositor-clutter.h index 24d040c6f..6d8834c13 100644 --- a/src/compositor/compositor-clutter.h +++ b/src/compositor/compositor-clutter.h @@ -26,7 +26,10 @@ #define META_COMPOSITOR_CLUTTER_H_ #include "types.h" +#include "compositor-clutter-plugin.h" MetaCompositor *meta_compositor_clutter_new (MetaDisplay *display); +void meta_compositor_clutter_window_effect_completed (ClutterActor *actor, gulong event); + #endif diff --git a/src/compositor/compositor-private.h b/src/compositor/compositor-private.h index 27abae2d4..aeccda417 100644 --- a/src/compositor/compositor-private.h +++ b/src/compositor/compositor-private.h @@ -55,6 +55,28 @@ struct _MetaCompositor void (*minimize_window) (MetaCompositor *compositor, 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 diff --git a/src/compositor/compositor-xrender.c b/src/compositor/compositor-xrender.c index a85448db7..0a631a929 100644 --- a/src/compositor/compositor-xrender.c +++ b/src/compositor/compositor-xrender.c @@ -81,17 +81,6 @@ composite_at_least_version (MetaDisplay *display, #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 { META_SHADOW_SMALL, diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c index cc97025b0..5567e4344 100644 --- a/src/compositor/compositor.c +++ b/src/compositor/compositor.c @@ -23,6 +23,7 @@ #include "compositor-private.h" #include "compositor-xrender.h" #include "compositor-clutter.h" +#include "prefs.h" #ifdef WITH_CLUTTER int meta_compositor_can_use_clutter__ = 0; @@ -35,7 +36,7 @@ meta_compositor_new (MetaDisplay *display) #ifdef WITH_CLUTTER /* At some point we would have a way to select between backends */ /* 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); else #endif @@ -188,3 +189,55 @@ meta_compositor_minimize_window (MetaCompositor *compositor, compositor->minimize_window (compositor, window); #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 +} + diff --git a/src/core/constraints.c b/src/core/constraints.c index a7601a775..41cb55d95 100644 --- a/src/core/constraints.c +++ b/src/core/constraints.c @@ -25,7 +25,7 @@ #include #include "constraints.h" -#include "workspace.h" +#include "workspace-private.h" #include "place.h" #include diff --git a/src/core/core.c b/src/core/core.c index a63531fea..303ddcde7 100644 --- a/src/core/core.c +++ b/src/core/core.c @@ -26,7 +26,7 @@ #include #include "core.h" #include "frame-private.h" -#include "workspace.h" +#include "workspace-private.h" #include "prefs.h" /* Looks up the MetaWindow representing the frame of the given X window. diff --git a/src/core/display-private.h b/src/core/display-private.h index add2e4009..c41027e19 100644 --- a/src/core/display-private.h +++ b/src/core/display-private.h @@ -49,7 +49,6 @@ typedef struct _MetaKeyBinding MetaKeyBinding; typedef struct _MetaStack MetaStack; typedef struct _MetaUISlave MetaUISlave; -typedef struct _MetaWorkspace MetaWorkspace; typedef struct _MetaWindowPropHooks MetaWindowPropHooks; typedef struct _MetaGroupPropHooks MetaGroupPropHooks; diff --git a/src/core/display.c b/src/core/display.c index ee5c9f0e0..a122cc0e8 100644 --- a/src/core/display.c +++ b/src/core/display.c @@ -44,7 +44,7 @@ #include "prefs.h" #include "resizepopup.h" #include "xprops.h" -#include "workspace.h" +#include "workspace-private.h" #include "bell.h" #include "effects.h" #include "compositor.h" diff --git a/src/core/edge-resistance.c b/src/core/edge-resistance.c index fb7c2d2e4..44734a0f9 100644 --- a/src/core/edge-resistance.c +++ b/src/core/edge-resistance.c @@ -25,7 +25,7 @@ #include "edge-resistance.h" #include "boxes.h" #include "display-private.h" -#include "workspace.h" +#include "workspace-private.h" /* A simple macro for whether a given window's edges are potentially * relevant for resistance/snapping during a move/resize operation diff --git a/src/core/keybindings.c b/src/core/keybindings.c index c6e29faa9..47ec693a3 100644 --- a/src/core/keybindings.c +++ b/src/core/keybindings.c @@ -28,7 +28,7 @@ #include #include "keybindings.h" -#include "workspace.h" +#include "workspace-private.h" #include "errors.h" #include "edge-resistance.h" #include "ui.h" diff --git a/src/core/prefs.c b/src/core/prefs.c index dffe71cf2..8d107d3bb 100644 --- a/src/core/prefs.c +++ b/src/core/prefs.c @@ -2,11 +2,11 @@ /* Metacity preferences */ -/* +/* * Copyright (C) 2001 Havoc Pennington, Copyright (C) 2002 Red Hat Inc. * Copyright (C) 2006 Elijah Newren * Copyright (C) 2008 Thomas Thurman - * + * * 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 @@ -16,7 +16,7 @@ * 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 @@ -63,6 +63,10 @@ #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 static GConfClient *default_client = NULL; @@ -105,6 +109,11 @@ static char *terminal_command = 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 static gboolean handle_preference_update_enum (const gchar *key, GConfValue *value); @@ -407,6 +416,13 @@ static MetaBoolPreference preferences_bool[] = &compositing_manager, FALSE, }, +#ifdef WITH_CLUTTER + { "/apps/metacity/general/clutter_disabled", + META_PREF_CLUTTER_DISABLED, + &clutter_disabled, + FALSE, + }, +#endif { NULL, 0, NULL, FALSE }, }; @@ -568,7 +584,7 @@ handle_preference_init_int (void) { MetaIntPreference *cursor = preferences_int; - + while (cursor->key!=NULL) { gint value; @@ -613,7 +629,7 @@ handle_preference_update_enum (const gchar *key, GConfValue *value) if (cursor->key==NULL) /* Didn't recognise that key. */ return FALSE; - + /* Setting it to null (that is, removing it) always means * "don't change". */ @@ -636,7 +652,7 @@ handle_preference_update_enum (const gchar *key, GConfValue *value) */ old_value = * ((gint *) cursor->target); - + /* Now look it up... */ if (!gconf_string_to_enum (cursor->symtab, @@ -651,7 +667,7 @@ handle_preference_update_enum (const gchar *key, GConfValue *value) * (We know the old value, so we can look up a suitable string in * the symtab.) */ - + meta_warning (_("GConf key '%s' is set to an invalid value\n"), key); return TRUE; @@ -681,7 +697,7 @@ handle_preference_update_bool (const gchar *key, GConfValue *value) if (cursor->target==NULL) /* No work for us to do. */ return TRUE; - + if (value==NULL) { /* Value was destroyed; let's get out of here. */ @@ -710,7 +726,7 @@ handle_preference_update_bool (const gchar *key, GConfValue *value) */ old_value = * ((gboolean *) cursor->target); - + /* Now look it up... */ *((gboolean *) cursor->target) = gconf_value_get_bool (value); @@ -796,7 +812,7 @@ handle_preference_update_int (const gchar *key, GConfValue *value) if (cursor->target==NULL) /* No work for us to do. */ return TRUE; - + if (value==NULL) { /* Value was destroyed. */ @@ -836,7 +852,7 @@ handle_preference_update_int (const gchar *key, GConfValue *value) } return TRUE; - + } @@ -876,7 +892,7 @@ meta_prefs_remove_listener (MetaPrefsChangedFunc func, return; } - + tmp = tmp->next; } @@ -891,9 +907,9 @@ emit_changed (MetaPreference pref) meta_topic (META_DEBUG_PREFS, "Notifying listeners that pref %s changed\n", meta_preference_to_string (pref)); - + copy = g_list_copy (listeners); - + tmp = copy; while (tmp != NULL) @@ -915,24 +931,24 @@ changed_idle_handler (gpointer data) GList *copy; changed_idle = 0; - + copy = g_list_copy (changes); /* reentrancy paranoia */ g_list_free (changes); changes = NULL; - + tmp = copy; while (tmp != NULL) { MetaPreference pref = GPOINTER_TO_INT (tmp->data); emit_changed (pref); - + tmp = tmp->next; } g_list_free (copy); - + return FALSE; } @@ -940,7 +956,7 @@ static void queue_changed (MetaPreference pref) { meta_topic (META_DEBUG_PREFS, "Queueing change of pref %s\n", - meta_preference_to_string (pref)); + meta_preference_to_string (pref)); if (g_list_find (changes, GINT_TO_POINTER (pref)) == NULL) changes = g_list_prepend (changes, GINT_TO_POINTER (pref)); @@ -993,10 +1009,10 @@ meta_prefs_init (void) #ifdef HAVE_GCONF GError *err = NULL; gchar **gconf_dir_cursor; - + if (default_client != NULL) return; - + /* returns a reference which we hold forever */ default_client = gconf_client_get_default (); @@ -1018,6 +1034,13 @@ meta_prefs_init (void) handle_preference_init_string (); 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? */ for (gconf_dir_cursor=gconf_dirs_we_are_interested_in; *gconf_dir_cursor!=NULL; @@ -1041,10 +1064,10 @@ meta_prefs_init (void) */ titlebar_font = pango_font_description_from_string ("Sans Bold 10"); current_theme = g_strdup ("Atlanta"); - + init_button_layout(); #endif /* HAVE_GCONF */ - + init_bindings (); init_commands (); init_workspace_names (); @@ -1074,7 +1097,7 @@ change_notify (GConfClient *client, const char *key; GConfValue *value; gint i=0; - + key = gconf_entry_get_key (entry); value = gconf_entry_get_value (entry); @@ -1200,12 +1223,31 @@ change_notify (GConfClient *client, if (update_workspace_name (key, str)) 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 { meta_topic (META_DEBUG_PREFS, "Key %s doesn't mean anything to Metacity\n", key); } - + out: /* nothing */ return; /* AIX compiler wants something after a label like out: */ @@ -1217,7 +1259,7 @@ cleanup_error (GError **error) if (*error) { meta_warning ("%s\n", (*error)->message); - + g_error_free (*error); *error = NULL; } @@ -1255,7 +1297,7 @@ static void maybe_give_disable_workarounds_warning (void) { static gboolean first_disable = TRUE; - + if (first_disable && disable_workarounds) { first_disable = FALSE; @@ -1389,7 +1431,7 @@ mouse_button_mods_handler (MetaPreference pref, { meta_topic (META_DEBUG_KEYBINDINGS, "Failed to parse new gconf value\n"); - + meta_warning (_("\"%s\" found in configuration database is " "not a valid value for mouse button modifier\n"), string_value); @@ -1401,7 +1443,7 @@ mouse_button_mods_handler (MetaPreference pref, static gboolean button_layout_equal (const MetaButtonLayout *a, const MetaButtonLayout *b) -{ +{ int i; i = 0; @@ -1440,7 +1482,7 @@ button_function_from_string (const char *str) return META_BUTTON_FUNCTION_ABOVE; else if (strcmp (str, "stick") == 0) return META_BUTTON_FUNCTION_STICK; - else + else /* don't know; give up */ return META_BUTTON_FUNCTION_LAST; } @@ -1478,11 +1520,11 @@ button_layout_handler (MetaPreference pref, MetaButtonLayout new_layout; char **sides; int i; - + /* We need to ignore unknown button functions, for * compat with future versions */ - + sides = g_strsplit (string_value, ":", 2); if (sides[0] != NULL) @@ -1498,7 +1540,7 @@ button_layout_handler (MetaPreference pref, new_layout.left_buttons_has_spacer[i] = FALSE; ++i; } - + buttons = g_strsplit (sides[0], ",", -1); i = 0; b = 0; @@ -1535,13 +1577,13 @@ button_layout_handler (MetaPreference pref, buttons[b]); } } - + ++b; } new_layout.left_buttons[i] = META_BUTTON_FUNCTION_LAST; new_layout.left_buttons_has_spacer[i] = FALSE; - + g_strfreev (buttons); } @@ -1558,7 +1600,7 @@ button_layout_handler (MetaPreference pref, new_layout.right_buttons_has_spacer[i] = FALSE; ++i; } - + buttons = g_strsplit (sides[1], ",", -1); i = 0; b = 0; @@ -1594,24 +1636,24 @@ button_layout_handler (MetaPreference pref, buttons[b]); } } - + ++b; } new_layout.right_buttons[i] = META_BUTTON_FUNCTION_LAST; new_layout.right_buttons_has_spacer[i] = FALSE; - + g_strfreev (buttons); } g_strfreev (sides); - + /* Invert the button layout for RTL languages */ if (meta_ui_get_direction() == META_UI_DIRECTION_RTL) { MetaButtonLayout rtl_layout; int j; - + for (i = 0; new_layout.left_buttons[i] != META_BUTTON_FUNCTION_LAST; i++); for (j = 0; j < i; j++) { @@ -1623,7 +1665,7 @@ button_layout_handler (MetaPreference pref, } rtl_layout.right_buttons[j] = META_BUTTON_FUNCTION_LAST; rtl_layout.right_buttons_has_spacer[j] = FALSE; - + for (i = 0; new_layout.right_buttons[i] != META_BUTTON_FUNCTION_LAST; i++); for (j = 0; j < i; j++) { @@ -1638,7 +1680,7 @@ button_layout_handler (MetaPreference pref, new_layout = rtl_layout; } - + if (button_layout_equal (&button_layout, &new_layout)) { /* Same as before, so duck out */ @@ -1671,7 +1713,7 @@ gboolean meta_prefs_get_application_based (void) { return FALSE; /* For now, we never want this to do anything */ - + return application_based; } @@ -1683,7 +1725,7 @@ meta_prefs_get_disable_workarounds (void) #ifdef HAVE_GCONF #define MAX_REASONABLE_AUTO_RAISE_DELAY 10000 - + #endif /* HAVE_GCONF */ #ifdef WITH_VERBOSE_MODE @@ -1704,7 +1746,7 @@ meta_preference_to_string (MetaPreference pref) case META_PREF_RAISE_ON_CLICK: return "RAISE_ON_CLICK"; - + case META_PREF_THEME: return "THEME"; @@ -1737,7 +1779,7 @@ meta_preference_to_string (MetaPreference pref) case META_PREF_AUTO_RAISE: return "AUTO_RAISE"; - + case META_PREF_AUTO_RAISE_DELAY: return "AUTO_RAISE_DELAY"; @@ -1779,6 +1821,12 @@ meta_preference_to_string (MetaPreference pref) case META_PREF_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)"; @@ -1790,7 +1838,7 @@ meta_prefs_set_num_workspaces (int n_workspaces) { #ifdef HAVE_GCONF GError *err; - + if (default_client == NULL) return; @@ -1798,7 +1846,7 @@ meta_prefs_set_num_workspaces (int n_workspaces) n_workspaces = 1; if (n_workspaces > MAX_REASONABLE_WORKSPACES) n_workspaces = MAX_REASONABLE_WORKSPACES; - + err = NULL; gconf_client_set_int (default_client, KEY_NUM_WORKSPACES, @@ -1824,7 +1872,7 @@ static MetaKeyPref screen_bindings[] = { { META_KEYBINDING_WORKSPACE_5, NULL, FALSE }, { META_KEYBINDING_WORKSPACE_6, NULL, FALSE }, { META_KEYBINDING_WORKSPACE_7, NULL, FALSE }, - { META_KEYBINDING_WORKSPACE_8, NULL, FALSE }, + { META_KEYBINDING_WORKSPACE_8, NULL, FALSE }, { META_KEYBINDING_WORKSPACE_9, NULL, FALSE }, { META_KEYBINDING_WORKSPACE_10, NULL, FALSE }, { META_KEYBINDING_WORKSPACE_11, NULL, FALSE }, @@ -1855,7 +1903,7 @@ static MetaKeyPref screen_bindings[] = { { META_KEYBINDING_COMMAND_5, NULL, FALSE }, { META_KEYBINDING_COMMAND_6, NULL, FALSE }, { META_KEYBINDING_COMMAND_7, NULL, FALSE }, - { META_KEYBINDING_COMMAND_8, NULL, FALSE }, + { META_KEYBINDING_COMMAND_8, NULL, FALSE }, { META_KEYBINDING_COMMAND_9, NULL, FALSE }, { META_KEYBINDING_COMMAND_10, NULL, FALSE }, { META_KEYBINDING_COMMAND_11, NULL, FALSE }, @@ -1941,7 +1989,7 @@ init_bindings (void) #ifdef HAVE_GCONF int i; GError *err; - + i = 0; while (window_bindings[i].name) { @@ -1958,7 +2006,7 @@ init_bindings (void) update_binding (&window_bindings[i], str_val); - g_free (str_val); + g_free (str_val); g_free (key); key = g_strconcat (KEY_WINDOW_BINDINGS_PREFIX, "/", @@ -2000,7 +2048,7 @@ init_bindings (void) update_binding (&screen_bindings[i], str_val); - g_free (str_val); + g_free (str_val); g_free (key); key = g_strconcat (KEY_SCREEN_BINDINGS_PREFIX, "/", @@ -2033,7 +2081,7 @@ init_bindings (void) /* Find which window_bindings entry this window_string_bindings entry * corresponds to. */ - while (strcmp(window_bindings[which].name, + while (strcmp(window_bindings[which].name, window_string_bindings[i].name) != 0) which++; @@ -2051,12 +2099,12 @@ init_bindings (void) /* Find which window_bindings entry this window_string_bindings entry * corresponds to. */ - while (strcmp(screen_bindings[which].name, + while (strcmp(screen_bindings[which].name, screen_string_bindings[i].name) != 0) which++; /* Set the binding */ - update_binding (&screen_bindings[which], + update_binding (&screen_bindings[which], screen_string_bindings[i].keybinding); ++i; @@ -2070,7 +2118,7 @@ init_commands (void) #ifdef HAVE_GCONF int i; GError *err; - + i = 0; while (i < MAX_COMMANDS) { @@ -2085,7 +2133,7 @@ init_commands (void) update_command (key, str_val); - g_free (str_val); + g_free (str_val); g_free (key); ++i; @@ -2103,7 +2151,7 @@ init_workspace_names (void) #ifdef HAVE_GCONF int i; GError *err; - + i = 0; while (i < MAX_REASONABLE_WORKSPACES) { @@ -2119,8 +2167,8 @@ init_workspace_names (void) update_workspace_name (key, str_val); g_assert (workspace_names[i] != NULL); - - g_free (str_val); + + g_free (str_val); g_free (key); ++i; @@ -2144,11 +2192,11 @@ update_binding (MetaKeyPref *binding, MetaVirtualModifier mods; MetaKeyCombo *combo; gboolean changed; - + meta_topic (META_DEBUG_KEYBINDINGS, "Binding \"%s\" has new gconf value \"%s\"\n", binding->name, value ? value : "none"); - + keysym = 0; keycode = 0; mods = 0; @@ -2172,7 +2220,7 @@ update_binding (MetaKeyPref *binding, binding->bindings = g_slist_alloc(); binding->bindings->data = blank; } - + combo = binding->bindings->data; #ifdef HAVE_GCONF @@ -2187,7 +2235,7 @@ update_binding (MetaKeyPref *binding, gchar *old_setting; gchar *key; GError *err = NULL; - + meta_warning ("Cannot bind \"%s\" to %s: it needs a modifier " "such as Ctrl or Alt.\n", binding->name, @@ -2214,7 +2262,7 @@ update_binding (MetaKeyPref *binding, key = g_strconcat (KEY_SCREEN_BINDINGS_PREFIX, "/", binding->name, NULL); - + gconf_client_set_string (gconf_client_get_default (), key, old_setting, &err); @@ -2225,7 +2273,7 @@ update_binding (MetaKeyPref *binding, g_error_free (err); err = NULL; } - + g_free (old_setting); g_free (key); @@ -2236,18 +2284,18 @@ update_binding (MetaKeyPref *binding, return TRUE; } #endif - + changed = FALSE; if (keysym != combo->keysym || keycode != combo->keycode || mods != combo->modifiers) { changed = TRUE; - + combo->keysym = keysym; combo->keycode = keycode; combo->modifiers = mods; - + meta_topic (META_DEBUG_KEYBINDINGS, "New keybinding for \"%s\" is keysym = 0x%x keycode = 0x%x mods = 0x%x\n", binding->name, combo->keysym, combo->keycode, @@ -2258,7 +2306,7 @@ update_binding (MetaKeyPref *binding, meta_topic (META_DEBUG_KEYBINDINGS, "Keybinding for \"%s\" is unchanged\n", binding->name); } - + return changed; } @@ -2279,7 +2327,7 @@ update_list_binding (MetaKeyPref *binding, meta_topic (META_DEBUG_KEYBINDINGS, "Binding \"%s\" has new gconf value\n", binding->name); - + if (binding->bindings == NULL) { /* We need to insert a dummy element into the list, because the first @@ -2290,7 +2338,7 @@ update_list_binding (MetaKeyPref *binding, binding->bindings = g_slist_alloc(); binding->bindings->data = blank; } - + /* Okay, so, we're about to provide a new list of key combos for this * action. Delete any pre-existing list. */ @@ -2302,7 +2350,7 @@ update_list_binding (MetaKeyPref *binding, } g_slist_free (binding->bindings->next); binding->bindings->next = NULL; - + while (pref_iterator) { keysym = 0; @@ -2326,7 +2374,7 @@ update_list_binding (MetaKeyPref *binding, default: g_assert_not_reached (); } - + if (!meta_ui_parse_accelerator (pref_string, &keysym, &keycode, &mods)) { meta_topic (META_DEBUG_KEYBINDINGS, @@ -2357,7 +2405,7 @@ update_list_binding (MetaKeyPref *binding, pref_iterator = pref_iterator->next; continue; } - + changed = TRUE; combo = g_malloc0 (sizeof (MetaKeyCombo)); @@ -2371,7 +2419,7 @@ update_list_binding (MetaKeyPref *binding, binding->name, keysym, keycode, mods); pref_iterator = pref_iterator->next; - } + } return changed; } @@ -2379,7 +2427,7 @@ static const gchar* relative_key (const gchar* key) { const gchar* end; - + end = strrchr (key, '/'); ++end; @@ -2391,13 +2439,13 @@ relative_key (const gchar* key) * notify */ static gboolean -find_and_update_binding (MetaKeyPref *bindings, +find_and_update_binding (MetaKeyPref *bindings, const char *name, const char *value) { const char *key; int i; - + if (*name == '/') key = relative_key (name); else @@ -2477,7 +2525,7 @@ update_command (const char *name, { char *p; int i; - + p = strrchr (name, '_'); if (p == NULL) { @@ -2485,7 +2533,7 @@ update_command (const char *name, "Command %s has no underscore?\n", name); return FALSE; } - + ++p; if (g_ascii_isdigit (*p)) @@ -2513,7 +2561,7 @@ update_command (const char *name, return FALSE; } } - + if (i >= MAX_COMMANDS) { meta_topic (META_DEBUG_KEYBINDINGS, @@ -2528,14 +2576,14 @@ update_command (const char *name, "Command %d is unchanged\n", i); return FALSE; } - + g_free (commands[i]); commands[i] = g_strdup (value); meta_topic (META_DEBUG_KEYBINDINGS, "Updated command %d to \"%s\"\n", i, commands[i] ? commands[i] : "none"); - + return TRUE; } @@ -2545,7 +2593,7 @@ const char* meta_prefs_get_command (int i) { g_return_val_if_fail (i >= 0 && i < MAX_COMMANDS, NULL); - + return commands[i]; } @@ -2566,7 +2614,7 @@ meta_prefs_get_gconf_key_for_command (int i) key = g_strdup_printf (KEY_COMMAND_PREFIX"%d", i + 1); break; } - + return key; } @@ -2589,7 +2637,7 @@ update_workspace_name (const char *name, { char *p; int i; - + p = strrchr (name, '_'); if (p == NULL) { @@ -2597,7 +2645,7 @@ update_workspace_name (const char *name, "Workspace name %s has no underscore?\n", name); return FALSE; } - + ++p; if (!g_ascii_isdigit (*p)) @@ -2606,10 +2654,10 @@ update_workspace_name (const char *name, "Workspace name %s doesn't end in number?\n", name); return FALSE; } - + i = atoi (p); i -= 1; /* count from 0 not 1 */ - + if (i >= MAX_REASONABLE_WORKSPACES) { meta_topic (META_DEBUG_PREFS, @@ -2622,7 +2670,7 @@ update_workspace_name (const char *name, meta_topic (META_DEBUG_PREFS, "Workspace name %d is unchanged\n", i); return FALSE; - } + } /* This is a bad hack. We have to treat empty string as * "unset" because the root window property can't contain @@ -2652,11 +2700,11 @@ update_workspace_name (const char *name, workspace_names[i] = d; } } - + meta_topic (META_DEBUG_PREFS, "Updated workspace name %d to \"%s\"\n", i, workspace_names[i] ? workspace_names[i] : "none"); - + return TRUE; } #endif /* HAVE_GCONF */ @@ -2671,7 +2719,7 @@ meta_prefs_get_workspace_name (int i) meta_topic (META_DEBUG_PREFS, "Getting workspace name for %d: \"%s\"\n", i, workspace_names[i]); - + return workspace_names[i]; } @@ -2682,7 +2730,7 @@ meta_prefs_change_workspace_name (int i, #ifdef HAVE_GCONF char *key; GError *err; - + g_return_if_fail (i >= 0 && i < MAX_REASONABLE_WORKSPACES); meta_topic (META_DEBUG_PREFS, @@ -2697,7 +2745,7 @@ meta_prefs_change_workspace_name (int i, */ if (name && *name == '\0') name = NULL; - + if ((name == NULL && workspace_names[i] == NULL) || (name && workspace_names[i] && strcmp (name, workspace_names[i]) == 0)) { @@ -2706,7 +2754,7 @@ meta_prefs_change_workspace_name (int i, i, name ? name : "none"); return; } - + key = gconf_key_for_workspace_name (i); err = NULL; @@ -2718,7 +2766,7 @@ meta_prefs_change_workspace_name (int i, gconf_client_unset (default_client, key, &err); - + if (err) { meta_warning (_("Error setting name for workspace %d to \"%s\": %s\n"), @@ -2726,7 +2774,7 @@ meta_prefs_change_workspace_name (int i, err->message); g_error_free (err); } - + g_free (key); #else g_free (workspace_names[i]); @@ -2739,9 +2787,9 @@ static char* gconf_key_for_workspace_name (int i) { char *key; - + key = g_strdup_printf (KEY_WORKSPACE_NAME_PREFIX"%d", i + 1); - + return key; } #endif /* HAVE_GCONF */ @@ -2774,7 +2822,7 @@ void meta_prefs_get_screen_bindings (const MetaKeyPref **bindings, int *n_bindings) { - + *bindings = screen_bindings; *n_bindings = (int) G_N_ELEMENTS (screen_bindings) - 1; } @@ -2845,7 +2893,7 @@ meta_prefs_get_keybinding_action (const char *name) { if (strcmp (screen_bindings[i].name, name) == 0) return (MetaKeyBindingAction) i; - + --i; } @@ -2888,7 +2936,7 @@ meta_prefs_get_window_binding (const char *name, *keysym = *modifiers = 0; return; } - + --i; } @@ -2923,12 +2971,67 @@ meta_prefs_set_compositing_manager (gboolean whether) #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 static void init_button_layout(void) { MetaButtonLayout button_layout_ltr = { - { + { /* buttons in the group on the left side */ META_BUTTON_FUNCTION_MENU, META_BUTTON_FUNCTION_LAST @@ -2942,7 +3045,7 @@ init_button_layout(void) } }; MetaButtonLayout button_layout_rtl = { - { + { /* buttons in the group on the left side */ META_BUTTON_FUNCTION_CLOSE, META_BUTTON_FUNCTION_MAXIMIZE, diff --git a/src/core/screen.c b/src/core/screen.c index e82dd932e..de2d50473 100644 --- a/src/core/screen.c +++ b/src/core/screen.c @@ -33,7 +33,7 @@ #include "window-private.h" #include "frame-private.h" #include "prefs.h" -#include "workspace.h" +#include "workspace-private.h" #include "keybindings.h" #include "stack.h" #include "xprops.h" @@ -2800,3 +2800,10 @@ meta_screen_unset_cm_selection (MetaScreen *screen) XSetSelectionOwner (screen->display->xdisplay, a, None, CurrentTime); } #endif /* HAVE_COMPOSITE_EXTENSIONS */ + +GList * +meta_screen_get_workspaces (MetaScreen *screen) +{ + return screen->workspaces; +} + diff --git a/src/core/window-private.h b/src/core/window-private.h index 1a9bcc2a0..8d6392875 100644 --- a/src/core/window-private.h +++ b/src/core/window-private.h @@ -49,12 +49,6 @@ typedef struct _MetaWindowQueue MetaWindowQueue; typedef gboolean (*MetaWindowForeachFunc) (MetaWindow *window, void *data); -typedef enum -{ - META_MAXIMIZE_HORIZONTAL = 1 << 0, - META_MAXIMIZE_VERTICAL = 1 << 1 -} MetaMaximizeFlags; - typedef enum { META_CLIENT_TYPE_UNKNOWN = 0, META_CLIENT_TYPE_APPLICATION = 1, diff --git a/src/core/window.c b/src/core/window.c index 09d95d9ed..bbb883799 100644 --- a/src/core/window.c +++ b/src/core/window.c @@ -30,7 +30,7 @@ #include "util.h" #include "frame-private.h" #include "errors.h" -#include "workspace.h" +#include "workspace-private.h" #include "stack.h" #include "keybindings.h" #include "ui.h" @@ -2582,9 +2582,23 @@ meta_window_maximize (MetaWindow *window, directions, NULL); - /* move_resize with new maximization constraints - */ - meta_window_queue(window, META_QUEUE_MOVE_RESIZE); + 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 + */ + meta_window_queue(window, META_QUEUE_MOVE_RESIZE); + } } } @@ -2681,12 +2695,32 @@ meta_window_unmaximize (MetaWindow *window, window->display->grab_anchor_window_pos = target_rect; } - meta_window_move_resize (window, - FALSE, - target_rect.x, - target_rect.y, - target_rect.width, - target_rect.height); + if (window->display->compositor) + { + meta_window_move_resize (window, + FALSE, + target_rect.x, + target_rect.y, + target_rect.width, + 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) { @@ -8170,3 +8204,19 @@ meta_window_get_type_atom (MetaWindow *window) { 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; +} + diff --git a/src/core/workspace.h b/src/core/workspace-private.h similarity index 93% rename from src/core/workspace.h rename to src/core/workspace-private.h index f4079ebb1..54f7c8146 100644 --- a/src/core/workspace.h +++ b/src/core/workspace-private.h @@ -30,9 +30,10 @@ * 02111-1307, USA. */ -#ifndef META_WORKSPACE_H -#define META_WORKSPACE_H +#ifndef META_WORKSPACE_PRIVATE_H +#define META_WORKSPACE_PRIVATE_H +#include "workspace.h" #include "window-private.h" /* Negative to avoid conflicting with real workspace @@ -80,7 +81,6 @@ void meta_workspace_activate_with_focus (MetaWorkspace *workspace, guint32 timestamp); void meta_workspace_activate (MetaWorkspace *workspace, guint32 timestamp); -int meta_workspace_index (MetaWorkspace *workspace); GList* meta_workspace_list_windows (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, int which_xinerama, 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_onxinerama_region (MetaWorkspace *workspace, int which_xinerama); diff --git a/src/core/workspace.c b/src/core/workspace.c index ddb2541f6..9f4cb7231 100644 --- a/src/core/workspace.c +++ b/src/core/workspace.c @@ -25,8 +25,14 @@ #include #include "workspace.h" +#include "workspace-private.h" #include "errors.h" #include "prefs.h" + +#ifdef HAVE_COMPOSITE_EXTENSIONS +#include "compositor.h" +#endif + #include #include @@ -383,6 +389,19 @@ meta_workspace_activate_with_focus (MetaWorkspace *workspace, meta_topic (META_DEBUG_FOCUS, "Focusing default window on new workspace\n"); 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 @@ -682,6 +701,19 @@ ensure_work_areas_validated (MetaWorkspace *workspace) /* We're all done, YAAY! Record that everything has been validated. */ 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 @@ -974,3 +1006,10 @@ focus_ancestor_or_mru_window (MetaWorkspace *workspace, timestamp); } } + +MetaScreen * +meta_workspace_get_screen (MetaWorkspace *workspace) +{ + return workspace->screen; +} + diff --git a/src/include/compositor-clutter-plugin.h b/src/include/compositor-clutter-plugin.h new file mode 100644 index 000000000..70c22a44e --- /dev/null +++ b/src/include/compositor-clutter-plugin.h @@ -0,0 +1,257 @@ +/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + +/* + * Copyright (c) 2008 Intel Corp. + * + * Author: Tomas Frydrych + * + * 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 + +/* + * 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 diff --git a/src/include/compositor.h b/src/include/compositor.h index 59dbd9a26..18b271ed5 100644 --- a/src/include/compositor.h +++ b/src/include/compositor.h @@ -27,6 +27,28 @@ #include "types.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 extern int meta_compositor_can_use_clutter__; @@ -79,6 +101,31 @@ void meta_compositor_minimize_window (MetaCompositor *compositor, 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 diff --git a/src/include/prefs.h b/src/include/prefs.h index 9a4d12855..ee412c661 100644 --- a/src/include/prefs.h +++ b/src/include/prefs.h @@ -2,10 +2,10 @@ /* Metacity preferences */ -/* +/* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2006 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 @@ -15,7 +15,7 @@ * 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 @@ -59,7 +59,11 @@ typedef enum META_PREF_GNOME_ANIMATIONS, META_PREF_CURSOR_THEME, 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; typedef void (* MetaPrefsChangedFunc) (MetaPreference pref, @@ -120,6 +124,21 @@ gboolean meta_prefs_get_compositing_manager (void); */ 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 */ #define META_KEYBINDING_WORKSPACE_1 "switch_to_workspace_1" #define META_KEYBINDING_WORKSPACE_2 "switch_to_workspace_2" diff --git a/src/include/screen.h b/src/include/screen.h index 9f8423172..941f5e41f 100644 --- a/src/include/screen.h +++ b/src/include/screen.h @@ -45,4 +45,5 @@ void meta_screen_set_cm_selection (MetaScreen *screen); void meta_screen_unset_cm_selection (MetaScreen *screen); #endif +GList *meta_screen_get_workspaces (MetaScreen *screen); #endif diff --git a/src/include/types.h b/src/include/types.h index 045b102f0..92d658f96 100644 --- a/src/include/types.h +++ b/src/include/types.h @@ -27,5 +27,5 @@ typedef struct _MetaDisplay MetaDisplay; typedef struct _MetaFrame MetaFrame; typedef struct _MetaScreen MetaScreen; typedef struct _MetaWindow MetaWindow; - +typedef struct _MetaWorkspace MetaWorkspace; #endif diff --git a/src/include/window.h b/src/include/window.h index 7a880df4d..a3f49928b 100644 --- a/src/include/window.h +++ b/src/include/window.h @@ -41,6 +41,12 @@ typedef enum META_WINDOW_SPLASHSCREEN, } MetaWindowType; +typedef enum +{ + META_MAXIMIZE_HORIZONTAL = 1 << 0, + META_MAXIMIZE_VERTICAL = 1 << 1 +} MetaMaximizeFlags; + MetaFrame *meta_window_get_frame (MetaWindow *window); gboolean meta_window_has_focus (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); MetaWindowType meta_window_get_type (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 diff --git a/src/include/workspace.h b/src/include/workspace.h new file mode 100644 index 000000000..a7e54fcfa --- /dev/null +++ b/src/include/workspace.h @@ -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 diff --git a/src/metacity.schemas.in b/src/metacity.schemas.in index 20d348623..22c585ac1 100644 --- a/src/metacity.schemas.in +++ b/src/metacity.schemas.in @@ -340,6 +340,36 @@ + + /schemas/apps/metacity/general/clutter_disabled + /apps/metacity/general/clutter_disabled + metacity + bool + false + + Clutter Disabled + + Determines whether Clutter compositing backend should be disabled. + Change to this option will not take effect until Metacity is + restarted. + + + + + + /schemas/apps/metacity/general/clutter_plugins + /apps/metacity/general/clutter_plugins + metacity + list + + + Clutter Plugins + + Plugins to load for the Clutter-based compositing manager. + + + + /schemas/apps/metacity/workspace_names/name /apps/metacity/workspace_names/name_1 diff --git a/src/ui/tabpopup.c b/src/ui/tabpopup.c index f38bb174e..e8251552d 100644 --- a/src/ui/tabpopup.c +++ b/src/ui/tabpopup.c @@ -30,7 +30,7 @@ #include "tabpopup.h" /* FIXME these two includes are 100% broken ... */ -#include "../core/workspace.h" +#include "../core/workspace-private.h" #include "../core/frame-private.h" #include "draw-workspace.h" #include