view audacious/pluginenum.c @ 1938:1d9c1026d9f8 trunk

[svn] - DoubleSize support. This has bugs, the most notable one being that DoubleSize only works right if you restart the player. The second bug is rather obvious too. No osmosis skinengine. No TinyPlayer. Classic-esque skinengine only. This is because the doublesize algorithm hates you and wants you to go die in a fire.
author nenolod
date Sun, 05 Nov 2006 04:43:16 -0800
parents a6e6d3500c13
children 94a720c9bfef
line wrap: on
line source

/*  BMP - Cross-platform multimedia player
 *  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; 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., 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 "mainwin.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;
    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, 0))) {
        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 (bmp_playback_get_playing()) {
        ip_data.stop = TRUE;
        bmp_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_iteration(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_iteration(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_iteration(FALSE));
            GDK_THREADS_ENTER();
        }
        g_module_close(ep->handle);
    }

    if (ep_data.effect_list)
        g_list_free(ep_data.effect_list);

    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_iteration(FALSE));
    GDK_THREADS_ENTER();

    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_iteration(FALSE));
            GDK_THREADS_ENTER();
        }
        g_module_close(gp->handle);
    }

    if (gp_data.general_list)
        g_list_free(gp_data.general_list);

    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_iteration(FALSE));
    GDK_THREADS_ENTER();

    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_iteration(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_iteration(FALSE));
            GDK_THREADS_ENTER();
        }
        g_module_close(lp->handle);
    }

    if (lowlevel_list)
        g_list_free(lowlevel_list);
}