1
0
Fork 0
mutter-performance-source/src/compositor/mutter-plugin-manager.c

653 lines
18 KiB
C
Raw Normal View History

/* -*- 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 "config.h"
#include "compositor-private.h"
#include "mutter-plugin-manager.h"
#include "prefs.h"
#include "errors.h"
#include "workspace.h"
2008-12-17 09:33:56 +00:00
#include "mutter-module.h"
#include "../core/window-private.h"
#include <string.h>
#include <clutter/x11/clutter-x11.h>
2008-12-17 09:33:56 +00:00
/*
* There is only one instace of each module per the process.
*/
static GHashTable *plugin_modules = NULL;
static gboolean mutter_plugin_manager_reload (MutterPluginManager *plugin_mgr);
struct MutterPluginManager
{
MetaScreen *screen;
GList /* MutterPluginPending */ *pending_plugin_modules; /* Plugins not yet fully loaded */
GList /* MutterPlugin */ *plugins; /* TODO -- maybe use hash table */
GList *unload; /* Plugins that are disabled and pending unload */
guint idle_unload_id;
};
typedef struct MutterPluginPending
{
MutterModule *module;
char *path;
char *params;
} MutterPluginPending;
/*
* Checks that the plugin is compatible with the WM and sets up the plugin
* struct.
*/
static MutterPlugin *
2008-12-17 09:33:56 +00:00
mutter_plugin_load (MutterPluginManager *mgr,
MutterModule *module,
2008-11-03 09:59:01 +00:00
const gchar *params)
{
2008-12-17 09:33:56 +00:00
MutterPlugin *plugin = NULL;
GType plugin_type = mutter_module_get_plugin_type (module);
2008-12-17 09:33:56 +00:00
if (!plugin_type)
{
2008-12-17 09:33:56 +00:00
g_warning ("Plugin type not registered !!!");
return NULL;
}
2008-12-17 09:33:56 +00:00
plugin = g_object_new (plugin_type,
"screen", mgr->screen,
"params", params,
NULL);
return plugin;
}
/*
* 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
mutter_plugin_unload (MutterPlugin *plugin)
{
2008-12-17 09:33:56 +00:00
if (mutter_plugin_running (plugin))
{
2008-12-17 09:33:56 +00:00
g_object_set (plugin, "disabled", TRUE, NULL);
return FALSE;
}
2008-12-17 09:33:56 +00:00
g_object_unref (plugin);
return TRUE;
}
/*
* Iddle callback to remove plugins that could not be removed directly and are
* pending for removal.
*/
static gboolean
2008-11-03 09:59:01 +00:00
mutter_plugin_manager_idle_unload (MutterPluginManager *plugin_mgr)
{
GList *l = plugin_mgr->unload;
gboolean dont_remove = TRUE;
while (l)
{
MutterPlugin *plugin = l->data;
if (mutter_plugin_unload (plugin))
{
/* Remove from list */
GList *p = l->prev;
GList *n = l->next;
if (!p)
plugin_mgr->unload = n;
else
p->next = n;
if (n)
n->prev = p;
g_list_free_1 (l);
l = n;
}
else
l = l->next;
}
if (!plugin_mgr->unload)
{
/* If no more unloads are pending, remove the handler as well */
dont_remove = FALSE;
plugin_mgr->idle_unload_id = 0;
}
return dont_remove;
}
/*
* Unloads all plugins
*/
static void
2008-11-03 09:59:01 +00:00
mutter_plugin_manager_unload (MutterPluginManager *plugin_mgr)
{
GList *plugins = plugin_mgr->plugins;
while (plugins)
{
MutterPlugin *plugin = plugins->data;
/* If the plugin could not be removed, move it to the unload list */
if (!mutter_plugin_unload (plugin))
{
plugin_mgr->unload = g_list_prepend (plugin_mgr->unload, plugin);
if (!plugin_mgr->idle_unload_id)
{
plugin_mgr->idle_unload_id = g_idle_add ((GSourceFunc)
mutter_plugin_manager_idle_unload,
plugin_mgr);
}
}
plugins = plugins->next;
}
g_list_free (plugin_mgr->plugins);
plugin_mgr->plugins = NULL;
}
static void
prefs_changed_callback (MetaPreference pref,
void *data)
{
MutterPluginManager *plugin_mgr = data;
if (pref == META_PREF_CLUTTER_PLUGINS)
{
mutter_plugin_manager_reload (plugin_mgr);
}
2008-12-17 09:33:56 +00:00
}
static MutterModule *
mutter_plugin_manager_get_module (const gchar *path)
{
MutterModule *module = g_hash_table_lookup (plugin_modules, path);
if (!module &&
(module = g_object_new (MUTTER_TYPE_MODULE, "path", path, NULL)))
{
2008-12-17 09:33:56 +00:00
g_hash_table_insert (plugin_modules, g_strdup (path), module);
}
2008-12-17 09:33:56 +00:00
return module;
}
/*
* Loads all plugins listed in gconf registry.
*/
gboolean
mutter_plugin_manager_load (MutterPluginManager *plugin_mgr)
{
const gchar *dpath = MUTTER_PLUGIN_DIR "/";
GSList *plugins, *fallback = NULL;
plugins = meta_prefs_get_clutter_plugins ();
if (!plugins)
{
/*
* If no plugins are specified, try to load the default plugin.
*/
fallback = g_slist_append (fallback, "default");
plugins = fallback;
}
while (plugins)
{
gchar *plugin_string;
gchar *params;
plugin_string = g_strdup (plugins->data);
if (plugin_string)
{
2008-12-17 09:33:56 +00:00
MutterModule *module;
gchar *path;
params = strchr (plugin_string, ':');
if (params)
{
*params = 0;
++params;
}
if (g_path_is_absolute (plugin_string))
path = g_strdup (plugin_string);
else
path = g_strconcat (dpath, plugin_string, ".so", NULL);
2008-12-17 09:33:56 +00:00
module = mutter_plugin_manager_get_module (path);
if (module)
{
2008-12-18 12:56:32 +00:00
gboolean use_succeeded;
2008-12-17 09:33:56 +00:00
/*
* This dlopens the module and registers the plugin type with the
* GType system, if the module is not already loaded. When we
* create a plugin, the type system also calls g_type_module_use()
* to guarantee the module will not be unloaded during the plugin
* life time. Consequently we can unuse() the module again.
*/
2008-12-18 12:56:32 +00:00
use_succeeded = g_type_module_use (G_TYPE_MODULE (module));
2008-12-17 09:33:56 +00:00
if (use_succeeded)
{
MutterPluginPending *pending = g_new0 (MutterPluginPending, 1);
pending->module = module;
pending->path = g_strdup (path);
pending->params = g_strdup (params);
plugin_mgr->pending_plugin_modules =
g_list_prepend (plugin_mgr->pending_plugin_modules, pending);
}
}
else
2008-12-17 09:33:56 +00:00
g_warning ("Unable to load plugin module [%s]: %s",
path, g_module_error());
g_free (path);
g_free (plugin_string);
}
plugins = plugins->next;
}
if (fallback)
g_slist_free (fallback);
if (plugin_mgr->pending_plugin_modules != NULL)
{
meta_prefs_add_listener (prefs_changed_callback, plugin_mgr);
return TRUE;
}
return FALSE;
}
gboolean
mutter_plugin_manager_initialize (MutterPluginManager *plugin_mgr)
{
GList *iter;
for (iter = plugin_mgr->pending_plugin_modules; iter; iter = iter->next)
{
MutterPluginPending *pending = (MutterPluginPending*) iter->data;
MutterPlugin *p;
if ((p = mutter_plugin_load (plugin_mgr, pending->module, pending->params)))
{
plugin_mgr->plugins = g_list_prepend (plugin_mgr->plugins, p);
}
else
{
g_warning ("Plugin load for [%s] failed", pending->path);
}
g_type_module_unuse (G_TYPE_MODULE (pending->module));
g_free (pending->path);
g_free (pending->params);
g_free (pending);
}
g_list_free (plugin_mgr->pending_plugin_modules);
plugin_mgr->pending_plugin_modules = NULL;
return TRUE;
}
/*
* Reloads all plugins
*/
static gboolean
2008-11-03 09:59:01 +00:00
mutter_plugin_manager_reload (MutterPluginManager *plugin_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.
*/
/* Prevent stale grabs on unloaded plugins */
mutter_check_end_modal (plugin_mgr->screen);
mutter_plugin_manager_unload (plugin_mgr);
return mutter_plugin_manager_load (plugin_mgr);
}
MutterPluginManager *
mutter_plugin_manager_new (MetaScreen *screen)
{
MutterPluginManager *plugin_mgr;
2008-12-17 09:33:56 +00:00
if (!plugin_modules)
{
plugin_modules = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
NULL);
}
plugin_mgr = g_new0 (MutterPluginManager, 1);
plugin_mgr->screen = screen;
return plugin_mgr;
}
static void
2008-11-03 09:59:01 +00:00
mutter_plugin_manager_kill_effect (MutterPluginManager *plugin_mgr,
MutterWindow *actor,
unsigned long events)
{
GList *l = plugin_mgr->plugins;
while (l)
{
MutterPlugin *plugin = l->data;
2008-12-17 09:33:56 +00:00
MutterPluginClass *klass = MUTTER_PLUGIN_GET_CLASS (plugin);
2008-12-17 09:33:56 +00:00
if (!mutter_plugin_disabled (plugin)
&& (mutter_plugin_features (plugin) & events)
&& klass->kill_effect)
klass->kill_effect (plugin, actor, events);
l = l->next;
}
}
#define ALL_BUT_SWITCH \
MUTTER_PLUGIN_ALL_EFFECTS & \
~MUTTER_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
2008-11-03 09:59:01 +00:00
mutter_plugin_manager_event_simple (MutterPluginManager *plugin_mgr,
MutterWindow *actor,
unsigned long event)
{
GList *l = plugin_mgr->plugins;
gboolean retval = FALSE;
MetaDisplay *display = meta_screen_get_display (plugin_mgr->screen);
if (display->display_opening)
return FALSE;
while (l)
{
MutterPlugin *plugin = l->data;
2008-12-17 09:33:56 +00:00
MutterPluginClass *klass = MUTTER_PLUGIN_GET_CLASS (plugin);
2008-12-17 09:33:56 +00:00
if (!mutter_plugin_disabled (plugin) &&
(mutter_plugin_features (plugin) & event))
{
retval = TRUE;
switch (event)
{
case MUTTER_PLUGIN_MINIMIZE:
2008-12-17 09:33:56 +00:00
if (klass->minimize)
{
mutter_plugin_manager_kill_effect (
plugin_mgr,
actor,
ALL_BUT_SWITCH);
_mutter_plugin_effect_started (plugin);
2008-12-17 09:33:56 +00:00
klass->minimize (plugin, actor);
}
break;
case MUTTER_PLUGIN_MAP:
2008-12-17 09:33:56 +00:00
if (klass->map)
{
mutter_plugin_manager_kill_effect (
plugin_mgr,
actor,
ALL_BUT_SWITCH);
_mutter_plugin_effect_started (plugin);
2008-12-17 09:33:56 +00:00
klass->map (plugin, actor);
}
break;
case MUTTER_PLUGIN_DESTROY:
2008-12-17 09:33:56 +00:00
if (klass->destroy)
{
_mutter_plugin_effect_started (plugin);
2008-12-17 09:33:56 +00:00
klass->destroy (plugin, actor);
}
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 maximize and unmaximize
* events.
*
* 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
2008-11-03 09:59:01 +00:00
mutter_plugin_manager_event_maximize (MutterPluginManager *plugin_mgr,
MutterWindow *actor,
unsigned long event,
gint target_x,
gint target_y,
gint target_width,
gint target_height)
{
GList *l = plugin_mgr->plugins;
gboolean retval = FALSE;
MetaDisplay *display = meta_screen_get_display (plugin_mgr->screen);
if (display->display_opening)
return FALSE;
while (l)
{
MutterPlugin *plugin = l->data;
2008-12-17 09:33:56 +00:00
MutterPluginClass *klass = MUTTER_PLUGIN_GET_CLASS (plugin);
2008-12-17 09:33:56 +00:00
if (!mutter_plugin_disabled (plugin) &&
(mutter_plugin_features (plugin) & event))
{
retval = TRUE;
switch (event)
{
case MUTTER_PLUGIN_MAXIMIZE:
2008-12-17 09:33:56 +00:00
if (klass->maximize)
{
mutter_plugin_manager_kill_effect (
plugin_mgr,
actor,
ALL_BUT_SWITCH);
_mutter_plugin_effect_started (plugin);
2008-12-17 09:33:56 +00:00
klass->maximize (plugin, actor,
target_x, target_y,
target_width, target_height);
}
break;
case MUTTER_PLUGIN_UNMAXIMIZE:
2008-12-17 09:33:56 +00:00
if (klass->unmaximize)
{
mutter_plugin_manager_kill_effect (
plugin_mgr,
actor,
ALL_BUT_SWITCH);
_mutter_plugin_effect_started (plugin);
2008-12-17 09:33:56 +00:00
klass->unmaximize (plugin, actor,
target_x, target_y,
target_width, target_height);
}
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
2008-11-03 09:59:01 +00:00
mutter_plugin_manager_switch_workspace (MutterPluginManager *plugin_mgr,
const GList **actors,
gint from,
gint to,
MetaMotionDirection direction)
{
GList *l = plugin_mgr->plugins;
gboolean retval = FALSE;
MetaDisplay *display = meta_screen_get_display (plugin_mgr->screen);
if (display->display_opening)
return FALSE;
while (l)
{
MutterPlugin *plugin = l->data;
2008-12-17 09:33:56 +00:00
MutterPluginClass *klass = MUTTER_PLUGIN_GET_CLASS (plugin);
2008-12-17 09:33:56 +00:00
if (!mutter_plugin_disabled (plugin) &&
(mutter_plugin_features (plugin) & MUTTER_PLUGIN_SWITCH_WORKSPACE) &&
(actors && *actors))
{
2008-12-17 09:33:56 +00:00
if (klass->switch_workspace)
{
retval = TRUE;
mutter_plugin_manager_kill_effect (
plugin_mgr,
MUTTER_WINDOW ((*actors)->data),
MUTTER_PLUGIN_SWITCH_WORKSPACE);
_mutter_plugin_effect_started (plugin);
2008-12-17 09:33:56 +00:00
klass->switch_workspace (plugin, actors, from, to, direction);
}
}
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
2008-11-03 09:59:01 +00:00
mutter_plugin_manager_xevent_filter (MutterPluginManager *plugin_mgr,
XEvent *xev)
{
GList *l;
gboolean have_plugin_xevent_func;
if (!plugin_mgr)
return FALSE;
l = plugin_mgr->plugins;
/* We need to make sure that clutter gets certain events, like
* ConfigureNotify on the stage window. If there is a plugin that
* provides an xevent_filter function, then it's the responsibility
* of that plugin to pass events to Clutter. Otherwise, we send the
* event directly to Clutter ourselves.
*
* What happens if there are two plugins with xevent_filter functions
* is undefined; in general, multiple competing plugins are something
* we don't support well or care much about.
*
* FIXME: Really, we should just always handle sending the event to
* clutter if a plugin doesn't report the event as handled by
* returning TRUE, but it doesn't seem worth breaking compatibility
* of the plugin interface right now to achieve this; the way it is
* now works fine in practice.
*/
have_plugin_xevent_func = FALSE;
while (l)
{
2008-12-17 09:33:56 +00:00
MutterPlugin *plugin = l->data;
MutterPluginClass *klass = MUTTER_PLUGIN_GET_CLASS (plugin);
2008-12-17 09:33:56 +00:00
if (klass->xevent_filter)
{
have_plugin_xevent_func = TRUE;
2008-12-17 09:33:56 +00:00
if (klass->xevent_filter (plugin, xev) == TRUE)
return TRUE;
}
l = l->next;
}
if (!have_plugin_xevent_func)
return clutter_x11_handle_event (xev) != CLUTTER_X11_FILTER_CONTINUE;
return FALSE;
}