mutter/src/compositor/plugins/default.c
Jasper St. Pierre 5e3d93da87 plugin: Remove unneeded wrapper APIs
Mutter originally started out with the idea that only a subset of the total
API was exposed to plugins, so some APIs are duplicated on MutterPlugin.
We've long since abandoned that idea; remove these wrappers.

https://bugzilla.gnome.org/show_bug.cgi?id=671103
2012-03-08 16:52:44 -05:00

781 lines
24 KiB
C

/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
/*
* Copyright (c) 2008 Intel Corp.
*
* Author: Tomas Frydrych <tf@linux.intel.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*/
#include <meta/meta-plugin.h>
#include <meta/window.h>
#include <libintl.h>
#define _(x) dgettext (GETTEXT_PACKAGE, x)
#define N_(x) x
#include <clutter/clutter.h>
#include <gmodule.h>
#include <string.h>
#define DESTROY_TIMEOUT 250
#define MINIMIZE_TIMEOUT 250
#define MAXIMIZE_TIMEOUT 250
#define MAP_TIMEOUT 250
#define SWITCH_TIMEOUT 500
#define ACTOR_DATA_KEY "MCCP-Default-actor-data"
#define META_TYPE_DEFAULT_PLUGIN (meta_default_plugin_get_type ())
#define META_DEFAULT_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_DEFAULT_PLUGIN, MetaDefaultPlugin))
#define META_DEFAULT_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_DEFAULT_PLUGIN, MetaDefaultPluginClass))
#define META_IS_DEFAULT_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_DEFAULT_PLUGIN_TYPE))
#define META_IS_DEFAULT_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_DEFAULT_PLUGIN))
#define META_DEFAULT_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_DEFAULT_PLUGIN, MetaDefaultPluginClass))
#define META_DEFAULT_PLUGIN_GET_PRIVATE(obj) \
(G_TYPE_INSTANCE_GET_PRIVATE ((obj), META_TYPE_DEFAULT_PLUGIN, MetaDefaultPluginPrivate))
typedef struct _MetaDefaultPlugin MetaDefaultPlugin;
typedef struct _MetaDefaultPluginClass MetaDefaultPluginClass;
typedef struct _MetaDefaultPluginPrivate MetaDefaultPluginPrivate;
struct _MetaDefaultPlugin
{
MetaPlugin parent;
MetaDefaultPluginPrivate *priv;
};
struct _MetaDefaultPluginClass
{
MetaPluginClass parent_class;
};
static GQuark actor_data_quark = 0;
static void minimize (MetaPlugin *plugin,
MetaWindowActor *actor);
static void map (MetaPlugin *plugin,
MetaWindowActor *actor);
static void destroy (MetaPlugin *plugin,
MetaWindowActor *actor);
static void maximize (MetaPlugin *plugin,
MetaWindowActor *actor,
gint x,
gint y,
gint width,
gint height);
static void unmaximize (MetaPlugin *plugin,
MetaWindowActor *actor,
gint x,
gint y,
gint width,
gint height);
static void switch_workspace (MetaPlugin *plugin,
gint from,
gint to,
MetaMotionDirection direction);
static void kill_window_effects (MetaPlugin *plugin,
MetaWindowActor *actor);
static void kill_switch_workspace (MetaPlugin *plugin);
static const MetaPluginInfo * plugin_info (MetaPlugin *plugin);
META_PLUGIN_DECLARE(MetaDefaultPlugin, meta_default_plugin);
/*
* Plugin private data that we store in the .plugin_private member.
*/
struct _MetaDefaultPluginPrivate
{
/* Valid only when switch_workspace effect is in progress */
ClutterTimeline *tml_switch_workspace1;
ClutterTimeline *tml_switch_workspace2;
ClutterActor *desktop1;
ClutterActor *desktop2;
MetaPluginInfo info;
gboolean debug_mode : 1;
};
/*
* Per actor private data we attach to each actor.
*/
typedef struct _ActorPrivate
{
ClutterActor *orig_parent;
ClutterTimeline *tml_minimize;
ClutterTimeline *tml_maximize;
ClutterTimeline *tml_destroy;
ClutterTimeline *tml_map;
gboolean is_minimized : 1;
gboolean is_maximized : 1;
} ActorPrivate;
/* callback data for when animations complete */
typedef struct
{
ClutterActor *actor;
MetaPlugin *plugin;
} EffectCompleteData;
static void
meta_default_plugin_dispose (GObject *object)
{
/* MetaDefaultPluginPrivate *priv = META_DEFAULT_PLUGIN (object)->priv;
*/
G_OBJECT_CLASS (meta_default_plugin_parent_class)->dispose (object);
}
static void
meta_default_plugin_finalize (GObject *object)
{
G_OBJECT_CLASS (meta_default_plugin_parent_class)->finalize (object);
}
static void
meta_default_plugin_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
switch (prop_id)
{
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
meta_default_plugin_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
switch (prop_id)
{
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
start (MetaPlugin *plugin)
{
MetaDefaultPluginPrivate *priv = META_DEFAULT_PLUGIN (plugin)->priv;
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;
if (meta_plugin_debug_mode (plugin))
{
g_debug ("Plugin %s: Entering debug mode.", priv->info.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;
}
}
static void
meta_default_plugin_class_init (MetaDefaultPluginClass *klass)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
MetaPluginClass *plugin_class = META_PLUGIN_CLASS (klass);
gobject_class->finalize = meta_default_plugin_finalize;
gobject_class->dispose = meta_default_plugin_dispose;
gobject_class->set_property = meta_default_plugin_set_property;
gobject_class->get_property = meta_default_plugin_get_property;
plugin_class->start = start;
plugin_class->map = map;
plugin_class->minimize = minimize;
plugin_class->maximize = maximize;
plugin_class->unmaximize = unmaximize;
plugin_class->destroy = destroy;
plugin_class->switch_workspace = switch_workspace;
plugin_class->plugin_info = plugin_info;
plugin_class->kill_window_effects = kill_window_effects;
plugin_class->kill_switch_workspace = kill_switch_workspace;
g_type_class_add_private (gobject_class, sizeof (MetaDefaultPluginPrivate));
}
static void
meta_default_plugin_init (MetaDefaultPlugin *self)
{
MetaDefaultPluginPrivate *priv;
self->priv = priv = META_DEFAULT_PLUGIN_GET_PRIVATE (self);
priv->info.name = "Default Effects";
priv->info.version = "0.1";
priv->info.author = "Intel Corp.";
priv->info.license = "GPL";
priv->info.description = "This is an example of a plugin implementation.";
}
/*
* Actor private data accessor
*/
static void
free_actor_private (gpointer data)
{
if (G_LIKELY (data != NULL))
g_slice_free (ActorPrivate, data);
}
static ActorPrivate *
get_actor_private (MetaWindowActor *actor)
{
ActorPrivate *priv = g_object_get_qdata (G_OBJECT (actor), actor_data_quark);
if (G_UNLIKELY (actor_data_quark == 0))
actor_data_quark = g_quark_from_static_string (ACTOR_DATA_KEY);
if (G_UNLIKELY (!priv))
{
priv = g_slice_new0 (ActorPrivate);
g_object_set_qdata_full (G_OBJECT (actor),
actor_data_quark, priv,
free_actor_private);
}
return priv;
}
static void
on_switch_workspace_effect_complete (ClutterTimeline *timeline, gpointer data)
{
MetaPlugin *plugin = META_PLUGIN (data);
MetaDefaultPluginPrivate *priv = META_DEFAULT_PLUGIN (plugin)->priv;
MetaScreen *screen = meta_plugin_get_screen (plugin);
GList *l = meta_get_window_actors (screen);
while (l)
{
ClutterActor *a = l->data;
MetaWindowActor *window_actor = META_WINDOW_ACTOR (a);
ActorPrivate *apriv = get_actor_private (window_actor);
if (apriv->orig_parent)
{
clutter_actor_reparent (a, apriv->orig_parent);
apriv->orig_parent = NULL;
}
l = l->next;
}
clutter_actor_destroy (priv->desktop1);
clutter_actor_destroy (priv->desktop2);
priv->tml_switch_workspace1 = NULL;
priv->tml_switch_workspace2 = NULL;
priv->desktop1 = NULL;
priv->desktop2 = NULL;
meta_plugin_switch_workspace_completed (plugin);
}
static void
switch_workspace (MetaPlugin *plugin,
gint from, gint to,
MetaMotionDirection direction)
{
MetaScreen *screen;
MetaDefaultPluginPrivate *priv = META_DEFAULT_PLUGIN (plugin)->priv;
GList *l;
ClutterActor *workspace0 = clutter_group_new ();
ClutterActor *workspace1 = clutter_group_new ();
ClutterActor *stage;
int screen_width, screen_height;
ClutterAnimation *animation;
screen = meta_plugin_get_screen (plugin);
stage = CLUTTER (meta_get_stage_for_screen (screen));
meta_screen_get_size (screen,
&screen_width,
&screen_height);
clutter_actor_set_anchor_point (workspace1,
screen_width,
screen_height);
clutter_actor_set_position (workspace1,
screen_width,
screen_height);
clutter_actor_set_scale (workspace1, 0.0, 0.0);
clutter_container_add_actor (CLUTTER_CONTAINER (stage), workspace1);
clutter_container_add_actor (CLUTTER_CONTAINER (stage), workspace0);
if (from == to)
{
meta_plugin_switch_workspace_completed (plugin);
return;
}
l = g_list_last (meta_get_window_actors (screen));
while (l)
{
MetaWindowActor *window_actor = l->data;
ActorPrivate *apriv = get_actor_private (window_actor);
ClutterActor *actor = CLUTTER_ACTOR (window_actor);
gint win_workspace;
win_workspace = meta_window_actor_get_workspace (window_actor);
if (win_workspace == to || win_workspace == from)
{
apriv->orig_parent = clutter_actor_get_parent (actor);
clutter_actor_reparent (actor,
win_workspace == to ? workspace1 : workspace0);
clutter_actor_show_all (actor);
clutter_actor_raise_top (actor);
}
else if (win_workspace < 0)
{
/* Sticky window */
apriv->orig_parent = NULL;
}
else
{
/* Window on some other desktop */
clutter_actor_hide (actor);
apriv->orig_parent = NULL;
}
l = l->prev;
}
priv->desktop1 = workspace0;
priv->desktop2 = workspace1;
animation = clutter_actor_animate (workspace0, CLUTTER_EASE_IN_SINE,
SWITCH_TIMEOUT,
"scale-x", 1.0,
"scale-y", 1.0,
NULL);
priv->tml_switch_workspace1 = clutter_animation_get_timeline (animation);
g_signal_connect (priv->tml_switch_workspace1,
"completed",
G_CALLBACK (on_switch_workspace_effect_complete),
plugin);
animation = clutter_actor_animate (workspace1, CLUTTER_EASE_IN_SINE,
SWITCH_TIMEOUT,
"scale-x", 0.0,
"scale-y", 0.0,
NULL);
priv->tml_switch_workspace2 = clutter_animation_get_timeline (animation);
}
/*
* Minimize effect completion callback; this function restores actor state, and
* calls the manager callback function.
*/
static void
on_minimize_effect_complete (ClutterTimeline *timeline, EffectCompleteData *data)
{
/*
* Must reverse the effect of the effect; must hide it first to ensure
* that the restoration will not be visible.
*/
MetaPlugin *plugin = data->plugin;
ActorPrivate *apriv;
MetaWindowActor *window_actor = META_WINDOW_ACTOR (data->actor);
apriv = get_actor_private (META_WINDOW_ACTOR (data->actor));
apriv->tml_minimize = NULL;
clutter_actor_hide (data->actor);
/* FIXME - we shouldn't assume the original scale, it should be saved
* at the start of the effect */
clutter_actor_set_scale (data->actor, 1.0, 1.0);
clutter_actor_move_anchor_point_from_gravity (data->actor,
CLUTTER_GRAVITY_NORTH_WEST);
/* Now notify the manager that we are done with this effect */
meta_plugin_minimize_completed (plugin, window_actor);
g_free (data);
}
/*
* Simple minimize handler: it applies a scale effect (which must be reversed on
* completion).
*/
static void
minimize (MetaPlugin *plugin, MetaWindowActor *window_actor)
{
MetaWindowType type;
MetaWindow *meta_window = meta_window_actor_get_meta_window (window_actor);
ClutterActor *actor = CLUTTER_ACTOR (window_actor);
type = meta_window_get_window_type (meta_window);
if (type == META_WINDOW_NORMAL)
{
ClutterAnimation *animation;
EffectCompleteData *data = g_new0 (EffectCompleteData, 1);
ActorPrivate *apriv = get_actor_private (window_actor);
apriv->is_minimized = TRUE;
clutter_actor_move_anchor_point_from_gravity (actor,
CLUTTER_GRAVITY_CENTER);
animation = clutter_actor_animate (actor,
CLUTTER_EASE_IN_SINE,
MINIMIZE_TIMEOUT,
"scale-x", 0.0,
"scale-y", 0.0,
NULL);
apriv->tml_minimize = clutter_animation_get_timeline (animation);
data->plugin = plugin;
data->actor = actor;
g_signal_connect (apriv->tml_minimize, "completed",
G_CALLBACK (on_minimize_effect_complete),
data);
}
else
meta_plugin_minimize_completed (plugin, window_actor);
}
/*
* Minimize effect completion callback; this function restores actor state, and
* calls the manager callback function.
*/
static void
on_maximize_effect_complete (ClutterTimeline *timeline, EffectCompleteData *data)
{
/*
* Must reverse the effect of the effect.
*/
MetaPlugin *plugin = data->plugin;
MetaWindowActor *window_actor = META_WINDOW_ACTOR (data->actor);
ActorPrivate *apriv = get_actor_private (window_actor);
apriv->tml_maximize = NULL;
/* FIXME - don't assume the original scale was 1.0 */
clutter_actor_set_scale (data->actor, 1.0, 1.0);
clutter_actor_move_anchor_point_from_gravity (data->actor,
CLUTTER_GRAVITY_NORTH_WEST);
/* Now notify the manager that we are done with this effect */
meta_plugin_maximize_completed (plugin, window_actor);
g_free (data);
}
/*
* 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 (MetaPlugin *plugin,
MetaWindowActor *window_actor,
gint end_x, gint end_y, gint end_width, gint end_height)
{
MetaWindowType type;
ClutterActor *actor = CLUTTER_ACTOR (window_actor);
MetaWindow *meta_window = meta_window_actor_get_meta_window (window_actor);
gdouble scale_x = 1.0;
gdouble scale_y = 1.0;
gfloat anchor_x = 0;
gfloat anchor_y = 0;
type = meta_window_get_window_type (meta_window);
if (type == META_WINDOW_NORMAL)
{
ClutterAnimation *animation;
EffectCompleteData *data = g_new0 (EffectCompleteData, 1);
ActorPrivate *apriv = get_actor_private (window_actor);
gfloat width, height;
gfloat 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);
animation = clutter_actor_animate (actor,
CLUTTER_EASE_IN_SINE,
MAXIMIZE_TIMEOUT,
"scale-x", scale_x,
"scale-y", scale_y,
NULL);
apriv->tml_maximize = clutter_animation_get_timeline (animation);
data->plugin = plugin;
data->actor = actor;
g_signal_connect (apriv->tml_maximize, "completed",
G_CALLBACK (on_maximize_effect_complete),
data);
return;
}
meta_plugin_maximize_completed (plugin, window_actor);
}
/*
* See comments on the maximize() function.
*
* (Just a skeleton code.)
*/
static void
unmaximize (MetaPlugin *plugin,
MetaWindowActor *window_actor,
gint end_x, gint end_y, gint end_width, gint end_height)
{
MetaWindow *meta_window = meta_window_actor_get_meta_window (window_actor);
MetaWindowType type = meta_window_get_window_type (meta_window);
if (type == META_WINDOW_NORMAL)
{
ActorPrivate *apriv = get_actor_private (window_actor);
apriv->is_maximized = FALSE;
}
/* Do this conditionally, if the effect requires completion callback. */
meta_plugin_unmaximize_completed (plugin, window_actor);
}
static void
on_map_effect_complete (ClutterTimeline *timeline, EffectCompleteData *data)
{
/*
* Must reverse the effect of the effect.
*/
MetaPlugin *plugin = data->plugin;
MetaWindowActor *window_actor = META_WINDOW_ACTOR (data->actor);
ActorPrivate *apriv = get_actor_private (window_actor);
apriv->tml_map = NULL;
clutter_actor_move_anchor_point_from_gravity (data->actor,
CLUTTER_GRAVITY_NORTH_WEST);
/* Now notify the manager that we are done with this effect */
meta_plugin_map_completed (plugin, window_actor);
g_free (data);
}
/*
* Simple map handler: it applies a scale effect which must be reversed on
* completion).
*/
static void
map (MetaPlugin *plugin, MetaWindowActor *window_actor)
{
MetaWindowType type;
ClutterActor *actor = CLUTTER_ACTOR (window_actor);
MetaWindow *meta_window = meta_window_actor_get_meta_window (window_actor);
type = meta_window_get_window_type (meta_window);
if (type == META_WINDOW_NORMAL)
{
ClutterAnimation *animation;
EffectCompleteData *data = g_new0 (EffectCompleteData, 1);
ActorPrivate *apriv = get_actor_private (window_actor);
clutter_actor_move_anchor_point_from_gravity (actor,
CLUTTER_GRAVITY_CENTER);
clutter_actor_set_scale (actor, 0.0, 0.0);
clutter_actor_show (actor);
animation = clutter_actor_animate (actor,
CLUTTER_EASE_IN_SINE,
MAP_TIMEOUT,
"scale-x", 1.0,
"scale-y", 1.0,
NULL);
apriv->tml_map = clutter_animation_get_timeline (animation);
data->actor = actor;
data->plugin = plugin;
g_signal_connect (apriv->tml_map, "completed",
G_CALLBACK (on_map_effect_complete),
data);
apriv->is_minimized = FALSE;
}
else
meta_plugin_map_completed (plugin, window_actor);
}
/*
* Destroy effect completion callback; this is a simple effect that requires no
* further action than notifying the manager that the effect is completed.
*/
static void
on_destroy_effect_complete (ClutterTimeline *timeline, EffectCompleteData *data)
{
MetaPlugin *plugin = data->plugin;
MetaWindowActor *window_actor = META_WINDOW_ACTOR (data->actor);
ActorPrivate *apriv = get_actor_private (window_actor);
apriv->tml_destroy = NULL;
meta_plugin_destroy_completed (plugin, window_actor);
}
/*
* Simple TV-out like effect.
*/
static void
destroy (MetaPlugin *plugin, MetaWindowActor *window_actor)
{
MetaWindowType type;
ClutterActor *actor = CLUTTER_ACTOR (window_actor);
MetaWindow *meta_window = meta_window_actor_get_meta_window (window_actor);
type = meta_window_get_window_type (meta_window);
if (type == META_WINDOW_NORMAL)
{
ClutterAnimation *animation;
EffectCompleteData *data = g_new0 (EffectCompleteData, 1);
ActorPrivate *apriv = get_actor_private (window_actor);
clutter_actor_move_anchor_point_from_gravity (actor,
CLUTTER_GRAVITY_CENTER);
animation = clutter_actor_animate (actor,
CLUTTER_EASE_IN_SINE,
DESTROY_TIMEOUT,
"scale-x", 0.0,
"scale-y", 1.0,
NULL);
apriv->tml_destroy = clutter_animation_get_timeline (animation);
data->plugin = plugin;
data->actor = actor;
g_signal_connect (apriv->tml_destroy, "completed",
G_CALLBACK (on_destroy_effect_complete),
data);
}
else
meta_plugin_destroy_completed (plugin, window_actor);
}
static void
kill_switch_workspace (MetaPlugin *plugin)
{
MetaDefaultPluginPrivate *priv = META_DEFAULT_PLUGIN (plugin)->priv;
if (priv->tml_switch_workspace1)
{
clutter_timeline_stop (priv->tml_switch_workspace1);
clutter_timeline_stop (priv->tml_switch_workspace2);
g_signal_emit_by_name (priv->tml_switch_workspace1, "completed", NULL);
}
}
static void
kill_window_effects (MetaPlugin *plugin,
MetaWindowActor *window_actor)
{
ActorPrivate *apriv;
apriv = get_actor_private (window_actor);
if (apriv->tml_minimize)
{
clutter_timeline_stop (apriv->tml_minimize);
g_signal_emit_by_name (apriv->tml_minimize, "completed", NULL);
}
if (apriv->tml_maximize)
{
clutter_timeline_stop (apriv->tml_maximize);
g_signal_emit_by_name (apriv->tml_maximize, "completed", NULL);
}
if (apriv->tml_map)
{
clutter_timeline_stop (apriv->tml_map);
g_signal_emit_by_name (apriv->tml_map, "completed", NULL);
}
if (apriv->tml_destroy)
{
clutter_timeline_stop (apriv->tml_destroy);
g_signal_emit_by_name (apriv->tml_destroy, "completed", NULL);
}
}
static const MetaPluginInfo *
plugin_info (MetaPlugin *plugin)
{
MetaDefaultPluginPrivate *priv = META_DEFAULT_PLUGIN (plugin)->priv;
return &priv->info;
}