diff src/audacious/pluginenum.c @ 2313:3149d4b1a9a9 trunk

[svn] - objective-make autodepend fixes - move all sourcecode into src/ and adjust Makefiles accordingly
author nenolod
date Fri, 12 Jan 2007 11:43:40 -0800
parents
children ad1d7687814c
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/audacious/pluginenum.c	Fri Jan 12 11:43:40 2007 -0800
@@ -0,0 +1,504 @@
+/*  Audacious - Cross-platform multimedia player
+ *  Copyright (C) 2005-2007  Audacious development team
+ *
+ *  Based on BMP:
+ *  Copyright (C) 2003-2004  BMP development team
+ *
+ *  Based on XMMS:
+ *  Copyright (C) 1998-2003  XMMS development team
+ *
+ *  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; under version 2 of the License.
+ *
+ *  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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#ifndef SHARED_SUFFIX
+# define SHARED_SUFFIX G_MODULE_SUFFIX
+#endif
+
+#include "pluginenum.h"
+
+#include <glib.h>
+#include <gmodule.h>
+#include <glib/gprintf.h>
+#include <string.h>
+
+#include "controlsocket.h"
+#include "main.h"
+#include "ui_main.h"
+#include "playback.h"
+#include "playlist.h"
+#include "util.h"
+
+#include "effect.h"
+#include "general.h"
+#include "input.h"
+#include "output.h"
+#include "visualization.h"
+
+const gchar *plugin_dir_list[] = {
+    PLUGINSUBS,
+    NULL
+};
+
+GHashTable *plugin_matrix = NULL;
+GList *lowlevel_list = NULL;
+
+static gint
+inputlist_compare_func(gconstpointer a, gconstpointer b)
+{
+    const InputPlugin *ap = a, *bp = b;
+    return strcasecmp(ap->description, bp->description);
+}
+
+static gint
+outputlist_compare_func(gconstpointer a, gconstpointer b)
+{
+    const OutputPlugin *ap = a, *bp = b;
+    return strcasecmp(ap->description, bp->description);
+}
+
+static gint
+effectlist_compare_func(gconstpointer a, gconstpointer b)
+{
+    const EffectPlugin *ap = a, *bp = b;
+    return strcasecmp(ap->description, bp->description);
+}
+
+static gint
+generallist_compare_func(gconstpointer a, gconstpointer b)
+{
+    const GeneralPlugin *ap = a, *bp = b;
+    return strcasecmp(ap->description, bp->description);
+}
+
+static gint
+vislist_compare_func(gconstpointer a, gconstpointer b)
+{
+    const VisPlugin *ap = a, *bp = b;
+    return strcasecmp(ap->description, bp->description);
+}
+
+static gboolean
+plugin_is_duplicate(const gchar * filename)
+{
+    GList *l;
+    const gchar *basename = g_basename(filename);
+
+    /* FIXME: messy stuff */
+
+    for (l = ip_data.input_list; l; l = g_list_next(l))
+        if (!strcmp(basename, g_basename(INPUT_PLUGIN(l->data)->filename)))
+            return TRUE;
+
+    for (l = op_data.output_list; l; l = g_list_next(l))
+        if (!strcmp(basename, g_basename(OUTPUT_PLUGIN(l->data)->filename)))
+            return TRUE;
+
+    for (l = ep_data.effect_list; l; l = g_list_next(l))
+        if (!strcmp(basename, g_basename(EFFECT_PLUGIN(l->data)->filename)))
+            return TRUE;
+
+    for (l = gp_data.general_list; l; l = g_list_next(l))
+        if (!strcmp(basename, g_basename(GENERAL_PLUGIN(l->data)->filename)))
+            return TRUE;
+
+    for (l = vp_data.vis_list; l; l = g_list_next(l))
+        if (!strcmp(basename, g_basename(VIS_PLUGIN(l->data)->filename)))
+            return TRUE;
+
+    for (l = lowlevel_list; l; l = g_list_next(l))
+        if (!strcmp(basename, g_basename(VIS_PLUGIN(l->data)->filename)))
+            return TRUE;
+
+    return FALSE;
+}
+
+
+#define PLUGIN_GET_INFO(x) ((PluginGetInfoFunc)(x))()
+typedef Plugin * (*PluginGetInfoFunc) (void);
+
+static void
+input_plugin_init(Plugin * plugin)
+{
+    InputPlugin *p = INPUT_PLUGIN(plugin);
+
+    p->get_vis_type = input_get_vis_type;
+    p->add_vis_pcm = input_add_vis_pcm;
+    
+    /* Pretty const casts courtesy of XMMS's plugin.h legacy. Anyone
+       else thinks we could use a CONST macro to solve the warnings?
+       - descender */
+    p->set_info = (void (*)(gchar *, gint, gint, gint, gint)) playlist_set_info_old_abi;
+    p->set_info_text = (void (*)(gchar *)) input_set_info_text;
+    p->set_status_buffering = (void (*)(gboolean)) input_set_status_buffering;     
+
+    ip_data.input_list = g_list_append(ip_data.input_list, p);
+    
+    g_hash_table_replace(plugin_matrix, g_path_get_basename(p->filename),
+                         GINT_TO_POINTER(1));
+}
+
+static void
+output_plugin_init(Plugin * plugin)
+{
+    OutputPlugin *p = OUTPUT_PLUGIN(plugin);
+    op_data.output_list = g_list_append(op_data.output_list, p);    
+}
+
+static void
+effect_plugin_init(Plugin * plugin)
+{
+    EffectPlugin *p = EFFECT_PLUGIN(plugin);
+    ep_data.effect_list = g_list_append(ep_data.effect_list, p);
+}
+
+static void
+general_plugin_init(Plugin * plugin)
+{
+    GeneralPlugin *p = GENERAL_PLUGIN(plugin);
+    p->xmms_session = ctrlsocket_get_session_id();
+    gp_data.general_list = g_list_append(gp_data.general_list, p);
+}
+
+static void
+vis_plugin_init(Plugin * plugin)
+{
+    VisPlugin *p = VIS_PLUGIN(plugin);
+    p->xmms_session = ctrlsocket_get_session_id();
+    p->disable_plugin = vis_disable_plugin;
+    vp_data.vis_list = g_list_append(vp_data.vis_list, p);
+}
+
+static void
+lowlevel_plugin_init(Plugin * plugin)
+{
+    LowlevelPlugin *p = LOWLEVEL_PLUGIN(plugin);
+    lowlevel_list = g_list_append(lowlevel_list, p);    
+}
+
+/* FIXME: Placed here (hopefully) temporarily - descender */
+
+typedef struct {
+    const gchar *name;
+    const gchar *id;
+    void (*init)(Plugin *);
+} PluginType;
+
+static PluginType plugin_types[] = {
+    { "input"        , "get_iplugin_info", input_plugin_init },
+    { "output"       , "get_oplugin_info", output_plugin_init },
+    { "effect"       , "get_eplugin_info", effect_plugin_init },
+    { "general"      , "get_gplugin_info", general_plugin_init },
+    { "visualization", "get_vplugin_info", vis_plugin_init },
+    { "lowlevel"     , "get_lplugin_info", lowlevel_plugin_init },
+    { NULL, NULL, NULL }
+};
+
+static void
+add_plugin(const gchar * filename)
+{
+    PluginType *type;
+    GModule *module;
+    gpointer func;
+
+    if (plugin_is_duplicate(filename))
+        return;
+
+    if (!(module = g_module_open(filename, G_MODULE_BIND_LOCAL))) {
+        printf("Failed to load plugin (%s): %s\n", 
+                  filename, g_module_error());
+        return;
+    }
+
+    for (type = plugin_types; type->name; type++)
+    {
+        if (g_module_symbol(module, type->id, &func)) {
+            Plugin *plugin = PLUGIN_GET_INFO(func);
+
+            plugin->handle = module;
+            plugin->filename = g_strdup(filename);
+            type->init(PLUGIN_GET_INFO(func));
+
+            return;
+        }
+    }
+
+    printf("Invalid plugin (%s)\n", filename);
+    g_module_close(module);
+}
+
+static gboolean
+scan_plugin_func(const gchar * path, const gchar * basename, gpointer data)
+{
+    if (!str_has_suffix_nocase(basename, SHARED_SUFFIX))
+        return FALSE;
+
+    if (!g_file_test(path, G_FILE_TEST_IS_REGULAR))
+        return FALSE;
+
+    add_plugin(path);
+
+    return FALSE;
+}
+
+static void
+scan_plugins(const gchar * path)
+{
+    dir_foreach(path, scan_plugin_func, NULL, NULL);
+}
+
+void
+plugin_system_init(void)
+{
+    gchar *dir, **disabled;
+    GList *node;
+    OutputPlugin *op;
+    InputPlugin *ip;
+    LowlevelPlugin *lp;
+    gint dirsel = 0, i = 0;
+
+    if (!g_module_supported()) {
+        report_error("Module loading not supported! Plugins will not be loaded.\n");
+        return;
+    }
+
+    plugin_matrix = g_hash_table_new_full(g_str_hash, g_int_equal, g_free,
+                                          NULL);
+
+#ifndef DISABLE_USER_PLUGIN_DIR
+    scan_plugins(bmp_paths[BMP_PATH_USER_PLUGIN_DIR]);
+    /*
+     * This is in a separate loop so if the user puts them in the
+     * wrong dir we'll still get them in the right order (home dir
+     * first)                                                - Zinx
+     */
+    while (plugin_dir_list[dirsel]) {
+        dir = g_build_filename(bmp_paths[BMP_PATH_USER_PLUGIN_DIR],
+                               plugin_dir_list[dirsel++], NULL);
+        scan_plugins(dir);
+        g_free(dir);
+    }
+    dirsel = 0;
+#endif
+
+    while (plugin_dir_list[dirsel]) {
+        dir = g_build_filename(PLUGIN_DIR, plugin_dir_list[dirsel++], NULL);
+        scan_plugins(dir);
+        g_free(dir);
+    }
+
+    op_data.output_list = g_list_sort(op_data.output_list, outputlist_compare_func);
+    if (!op_data.current_output_plugin
+        && g_list_length(op_data.output_list)) {
+        op_data.current_output_plugin = op_data.output_list->data;
+    }
+
+    ip_data.input_list = g_list_sort(ip_data.input_list, inputlist_compare_func);
+
+    ep_data.effect_list = g_list_sort(ep_data.effect_list, effectlist_compare_func);
+    ep_data.enabled_list = NULL;
+
+    gp_data.general_list = g_list_sort(gp_data.general_list, generallist_compare_func);
+    gp_data.enabled_list = NULL;
+
+    vp_data.vis_list = g_list_sort(vp_data.vis_list, vislist_compare_func);
+    vp_data.enabled_list = NULL;
+
+    general_enable_from_stringified_list(cfg.enabled_gplugins);
+    vis_enable_from_stringified_list(cfg.enabled_vplugins);
+    effect_enable_from_stringified_list(cfg.enabled_eplugins);
+
+    g_free(cfg.enabled_gplugins);
+    cfg.enabled_gplugins = NULL;
+
+    g_free(cfg.enabled_vplugins);
+    cfg.enabled_vplugins = NULL;
+
+    g_free(cfg.enabled_eplugins);
+    cfg.enabled_eplugins = NULL;
+
+    for (node = op_data.output_list; node; node = g_list_next(node)) {
+        op = OUTPUT_PLUGIN(node->data);
+        /*
+         * Only test basename to avoid problems when changing
+         * prefix.  We will only see one plugin with the same
+         * basename, so this is usually what the user want.
+         */
+        if (!strcmp(g_basename(cfg.outputplugin), g_basename(op->filename)))
+            op_data.current_output_plugin = op;
+        if (op->init)
+            op->init();
+    }
+
+    for (node = ip_data.input_list; node; node = g_list_next(node)) {
+        ip = INPUT_PLUGIN(node->data);
+        if (ip->init)
+            ip->init();
+    }
+
+    for (node = lowlevel_list; node; node = g_list_next(node)) {
+        lp = LOWLEVEL_PLUGIN(node->data);
+        if (lp->init)
+            lp->init();
+    }
+
+    if (cfg.disabled_iplugins) {
+        disabled = g_strsplit(cfg.disabled_iplugins, ":", 0);
+        while (disabled[i]) {
+            g_hash_table_replace(plugin_matrix, disabled[i],
+                                 GINT_TO_POINTER(FALSE));
+            i++;
+        }
+
+        g_free(disabled);
+
+        g_free(cfg.disabled_iplugins);
+        cfg.disabled_iplugins = NULL;
+    }
+}
+
+void
+plugin_system_cleanup(void)
+{
+    InputPlugin *ip;
+    OutputPlugin *op;
+    EffectPlugin *ep;
+    GeneralPlugin *gp;
+    VisPlugin *vp;
+    LowlevelPlugin *lp;
+    GList *node;
+
+    g_message("Shutting down plugin system");
+
+    if (playback_get_playing()) {
+        ip_data.stop = TRUE;
+        playback_stop();
+        ip_data.stop = FALSE;
+    }
+
+    for (node = get_input_list(); node; node = g_list_next(node)) {
+        ip = INPUT_PLUGIN(node->data);
+        if (ip && ip->cleanup) {
+            ip->cleanup();
+            GDK_THREADS_LEAVE();
+            while (g_main_context_iteration(NULL, FALSE));
+            GDK_THREADS_ENTER();
+        }
+        g_module_close(ip->handle);
+    }
+
+    if (ip_data.input_list)
+        g_list_free(ip_data.input_list);
+
+    for (node = get_output_list(); node; node = g_list_next(node)) {
+        op = OUTPUT_PLUGIN(node->data);
+        if (op && op->cleanup) {
+            op->cleanup();
+            GDK_THREADS_LEAVE();
+            while (g_main_context_iteration(NULL, FALSE));
+            GDK_THREADS_ENTER();
+        }
+        g_module_close(op->handle);
+    }
+    
+    if (op_data.output_list)
+        g_list_free(op_data.output_list);
+
+    for (node = get_effect_list(); node; node = g_list_next(node)) {
+        ep = EFFECT_PLUGIN(node->data);
+        if (ep && ep->cleanup) {
+            ep->cleanup();
+            GDK_THREADS_LEAVE();
+            while (g_main_context_iteration(NULL, FALSE));
+            GDK_THREADS_ENTER();
+        }
+        g_module_close(ep->handle);
+    }
+
+    if (ep_data.effect_list)
+        g_list_free(ep_data.effect_list);
+
+#if 0
+    for (node = get_general_enabled_list(); node; node = g_list_next(node)) {
+        gp = GENERAL_PLUGIN(node->data);
+        enable_general_plugin(g_list_index(gp_data.general_list, gp), FALSE);
+    }
+
+    if (gp_data.enabled_list)
+        g_list_free(gp_data.enabled_list);
+
+    GDK_THREADS_LEAVE();
+    while (g_main_context_iteration(NULL, FALSE));
+    GDK_THREADS_ENTER();
+#endif
+
+    for (node = get_general_list(); node; node = g_list_next(node)) {
+        gp = GENERAL_PLUGIN(node->data);
+        if (gp && gp->cleanup) {
+            gp->cleanup();
+            GDK_THREADS_LEAVE();
+            while (g_main_context_iteration(NULL, FALSE));
+            GDK_THREADS_ENTER();
+        }
+        g_module_close(gp->handle);
+    }
+
+    if (gp_data.general_list)
+        g_list_free(gp_data.general_list);
+
+#if 0
+    for (node = get_vis_enabled_list(); node; node = g_list_next(node)) {
+        vp = VIS_PLUGIN(node->data);
+        enable_vis_plugin(g_list_index(vp_data.vis_list, vp), FALSE);
+    }
+
+    if (vp_data.enabled_list)
+        g_list_free(vp_data.enabled_list);
+
+    GDK_THREADS_LEAVE();
+    while (g_main_context_iteration(NULL, FALSE));
+    GDK_THREADS_ENTER();
+#endif
+
+    for (node = get_vis_list(); node; node = g_list_next(node)) {
+        vp = VIS_PLUGIN(node->data);
+        if (vp && vp->cleanup) {
+            vp->cleanup();
+            GDK_THREADS_LEAVE();
+            while (g_main_context_iteration(NULL, FALSE));
+            GDK_THREADS_ENTER();
+        }
+        g_module_close(vp->handle);
+    }
+
+    if (vp_data.vis_list)
+        g_list_free(vp_data.vis_list);
+
+    for (node = lowlevel_list; node; node = g_list_next(node)) {
+        lp = LOWLEVEL_PLUGIN(node->data);
+        if (lp && lp->cleanup) {
+            lp->cleanup();
+            GDK_THREADS_LEAVE();
+            while (g_main_context_iteration(NULL, FALSE));
+            GDK_THREADS_ENTER();
+        }
+        g_module_close(lp->handle);
+    }
+
+    if (lowlevel_list)
+        g_list_free(lowlevel_list);
+}