Audacious $Id:Doxyfile42802007-03-2104:39:00Znenolod$
pluginenum.c
Go to the documentation of this file.
00001 /*  Audacious - Cross-platform multimedia player
00002  *  Copyright (C) 2005-2009  Audacious development team
00003  *
00004  *  Based on BMP:
00005  *  Copyright (C) 2003-2004  BMP development team
00006  *
00007  *  Based on XMMS:
00008  *  Copyright (C) 1998-2003  XMMS development team
00009  *
00010  *  This program is free software; you can redistribute it and/or modify
00011  *  it under the terms of the GNU General Public License as published by
00012  *  the Free Software Foundation; under version 3 of the License.
00013  *
00014  *  This program is distributed in the hope that it will be useful,
00015  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  *  GNU General Public License for more details.
00018  *
00019  *  You should have received a copy of the GNU General Public License
00020  *  along with this program.  If not, see <http://www.gnu.org/licenses>.
00021  *
00022  *  The Audacious team does not consider modular code linking to
00023  *  Audacious or using our public API to be a derived work.
00024  */
00025 
00026 #ifdef HAVE_CONFIG_H
00027 #  include "config.h"
00028 #endif
00029 
00030 #ifndef SHARED_SUFFIX
00031 # define SHARED_SUFFIX G_MODULE_SUFFIX
00032 #endif
00033 
00034 #include <glib.h>
00035 #include <gmodule.h>
00036 #include <gtk/gtk.h>
00037 #include <string.h>
00038 
00039 #include <libaudcore/audstrings.h>
00040 #include <libaudgui/init.h>
00041 
00042 #include "pluginenum.h"
00043 #include "plugins.h"
00044 
00045 #include "audconfig.h"
00046 #include "debug.h"
00047 #include "effect.h"
00048 #include "general.h"
00049 #include "i18n.h"
00050 #include "interface.h"
00051 #include "main.h"
00052 #include "output.h"
00053 #include "playback.h"
00054 #include "util.h"
00055 #include "visualization.h"
00056 
00057 #define AUD_API_DECLARE
00058 #include "configdb.h"
00059 #include "drct.h"
00060 #include "misc.h"
00061 #include "playlist.h"
00062 #include "plugins.h"
00063 #undef AUD_API_DECLARE
00064 
00065 const gchar *plugin_dir_list[] = {
00066     PLUGINSUBS,
00067     NULL
00068 };
00069 
00070 static AudAPITable api_table = {
00071  .configdb_api = & configdb_api,
00072  .drct_api = & drct_api,
00073  .misc_api = & misc_api,
00074  .playlist_api = & playlist_api,
00075  .plugins_api = & plugins_api,
00076  .cfg = & cfg};
00077 
00078 extern GList *vfs_transports;
00079 static mowgli_list_t *headers_list = NULL;
00080 
00081 static void input_plugin_init(Plugin * plugin)
00082 {
00083     InputPlugin *p = INPUT_PLUGIN(plugin);
00084 
00085     if (p->init != NULL)
00086         p->init ();
00087 }
00088 
00089 static void effect_plugin_init(Plugin * plugin)
00090 {
00091     EffectPlugin *p = EFFECT_PLUGIN(plugin);
00092 
00093     if (p->init != NULL)
00094         p->init ();
00095 }
00096 
00097 static void vis_plugin_disable_by_header (VisPlugin * header)
00098 {
00099     vis_plugin_enable (plugin_by_header (header), FALSE);
00100 }
00101 
00102 static void vis_plugin_init(Plugin * plugin)
00103 {
00104     ((VisPlugin *) plugin)->disable_plugin = vis_plugin_disable_by_header;
00105 }
00106 
00107 /*******************************************************************/
00108 
00109 static void plugin2_dispose(GModule * module, const gchar * str, ...)
00110 {
00111     gchar *buf;
00112     va_list va;
00113 
00114     va_start(va, str);
00115     buf = g_strdup_vprintf(str, va);
00116     va_end(va);
00117 
00118     AUDDBG ("*** %s\n", buf);
00119     g_free(buf);
00120 
00121     g_module_close(module);
00122 }
00123 
00124 void plugin2_process(PluginHeader * header, GModule * module, const gchar * filename)
00125 {
00126     gint i, n;
00127     mowgli_node_t *hlist_node;
00128 
00129     if (header->magic != PLUGIN_MAGIC)
00130     {
00131         plugin2_dispose (module, "plugin <%s> discarded, invalid module magic",
00132          filename);
00133         return;
00134     }
00135 
00136     if (header->api_version != __AUDACIOUS_PLUGIN_API__)
00137     {
00138         plugin2_dispose (module, "plugin <%s> discarded, wanting API version "
00139          "%d, we implement API version %d", filename, header->api_version,
00140          __AUDACIOUS_PLUGIN_API__);
00141         return;
00142     }
00143 
00144     hlist_node = mowgli_node_create();
00145     mowgli_node_add(header, hlist_node, headers_list);
00146 
00147     if (header->init)
00148     {
00149         plugin_register (filename, PLUGIN_TYPE_BASIC, 0, NULL);
00150         header->init();
00151     }
00152 
00153     header->priv_assoc = g_new0(Plugin, 1);
00154     header->priv_assoc->handle = module;
00155     header->priv_assoc->filename = g_strdup(filename);
00156 
00157     n = 0;
00158 
00159     if (header->ip_list)
00160     {
00161         for (i = 0; (header->ip_list)[i] != NULL; i++, n++)
00162         {
00163             plugin_register (filename, PLUGIN_TYPE_INPUT, i, header->ip_list[i]);
00164             PLUGIN((header->ip_list)[i])->filename = g_strdup_printf("%s (#%d)", filename, n);
00165             input_plugin_init(PLUGIN((header->ip_list)[i]));
00166         }
00167     }
00168 
00169     if (header->op_list)
00170     {
00171         for (i = 0; (header->op_list)[i] != NULL; i++, n++)
00172         {
00173             OutputPlugin * plugin = header->op_list[i];
00174 
00175             plugin->filename = g_strdup_printf ("%s (#%d)", filename, n);
00176             plugin_register (filename, PLUGIN_TYPE_OUTPUT, i, plugin);
00177         }
00178     }
00179 
00180     if (header->ep_list)
00181     {
00182         for (i = 0; (header->ep_list)[i] != NULL; i++, n++)
00183         {
00184             plugin_register (filename, PLUGIN_TYPE_EFFECT, i, header->ep_list[i]);
00185             PLUGIN((header->ep_list)[i])->filename = g_strdup_printf("%s (#%d)", filename, n);
00186             effect_plugin_init(PLUGIN((header->ep_list)[i]));
00187         }
00188     }
00189 
00190 
00191     if (header->gp_list)
00192     {
00193         for (i = 0; (header->gp_list)[i] != NULL; i++, n++)
00194         {
00195             plugin_register (filename, PLUGIN_TYPE_GENERAL, i, header->gp_list[i]);
00196             PLUGIN((header->gp_list)[i])->filename = g_strdup_printf("%s (#%d)", filename, n);
00197         }
00198     }
00199 
00200     if (header->vp_list)
00201     {
00202         for (i = 0; (header->vp_list)[i] != NULL; i++, n++)
00203         {
00204             plugin_register (filename, PLUGIN_TYPE_VIS, i, header->vp_list[i]);
00205             PLUGIN((header->vp_list)[i])->filename = g_strdup_printf("%s (#%d)", filename, n);
00206             vis_plugin_init(PLUGIN((header->vp_list)[i]));
00207         }
00208     }
00209 
00210     if (header->interface)
00211         plugin_register (filename, PLUGIN_TYPE_IFACE, 0, header->interface);
00212 }
00213 
00214 void plugin2_unload(PluginHeader * header, mowgli_node_t * hlist_node)
00215 {
00216     GModule *module;
00217 
00218     g_return_if_fail(header->priv_assoc != NULL);
00219 
00220     if (header->ip_list != NULL)
00221     {
00222         for (gint i = 0; header->ip_list[i] != NULL; i ++)
00223         {
00224             if (header->ip_list[i]->cleanup != NULL)
00225                 header->ip_list[i]->cleanup ();
00226 
00227             g_free (header->ip_list[i]->filename);
00228         }
00229     }
00230 
00231     if (header->op_list != NULL)
00232     {
00233         for (gint i = 0; header->op_list[i] != NULL; i ++)
00234             g_free (header->op_list[i]->filename);
00235     }
00236 
00237     if (header->ep_list != NULL)
00238     {
00239         for (gint i = 0; header->ep_list[i] != NULL; i ++)
00240         {
00241             if (header->ep_list[i]->cleanup != NULL)
00242                 header->ep_list[i]->cleanup ();
00243 
00244             g_free (header->ep_list[i]->filename);
00245         }
00246     }
00247 
00248     if (header->vp_list != NULL)
00249     {
00250         for (gint i = 0; header->vp_list[i] != NULL; i ++)
00251             g_free (header->vp_list[i]->filename);
00252     }
00253 
00254     if (header->gp_list != NULL)
00255     {
00256         for (gint i = 0; header->gp_list[i] != NULL; i ++)
00257             g_free (header->gp_list[i]->filename);
00258     }
00259 
00260     module = header->priv_assoc->handle;
00261 
00262     g_free(header->priv_assoc->filename);
00263     g_free(header->priv_assoc);
00264 
00265     if (header->fini)
00266         header->fini();
00267 
00268     mowgli_node_delete(hlist_node, headers_list);
00269     mowgli_node_free(hlist_node);
00270 
00271     g_module_close(module);
00272 }
00273 
00274 /******************************************************************/
00275 
00276 void module_load (const gchar * filename)
00277 {
00278     GModule *module;
00279     PluginHeader * (* func) (AudAPITable * table);
00280 
00281     AUDDBG ("Loading plugin: %s.\n", filename);
00282 
00283     if (!(module = g_module_open(filename, G_MODULE_BIND_LAZY | G_MODULE_BIND_LOCAL)))
00284     {
00285         printf("Failed to load plugin (%s): %s\n", filename, g_module_error());
00286         return;
00287     }
00288 
00289     /* v2 plugin loading */
00290     if (g_module_symbol (module, "get_plugin_info", (void *) & func))
00291     {
00292         PluginHeader * header = func (& api_table);
00293         g_return_if_fail (header != NULL);
00294         plugin2_process(header, module, filename);
00295         return;
00296     }
00297 
00298     printf("Invalid plugin (%s)\n", filename);
00299     g_module_close(module);
00300 }
00301 
00302 static gboolean scan_plugin_func(const gchar * path, const gchar * basename, gpointer data)
00303 {
00304     if (!str_has_suffix_nocase(basename, SHARED_SUFFIX))
00305         return FALSE;
00306 
00307     if (!g_file_test(path, G_FILE_TEST_IS_REGULAR))
00308         return FALSE;
00309 
00310     module_register (path);
00311 
00312     return FALSE;
00313 }
00314 
00315 static void scan_plugins(const gchar * path)
00316 {
00317     dir_foreach(path, scan_plugin_func, NULL, NULL);
00318 }
00319 
00320 static OutputPlugin * output_load_selected (void)
00321 {
00322     if (cfg.output_path == NULL)
00323         return NULL;
00324 
00325     PluginHandle * handle = plugin_by_path (cfg.output_path, PLUGIN_TYPE_OUTPUT,
00326      cfg.output_number);
00327     if (handle == NULL)
00328         return NULL;
00329 
00330     OutputPlugin * plugin = plugin_get_header (handle);
00331     if (plugin == NULL || plugin->init () != OUTPUT_PLUGIN_INIT_FOUND_DEVICES)
00332         return NULL;
00333 
00334     return plugin;
00335 }
00336 
00337 static gboolean output_probe_func (PluginHandle * handle, OutputPlugin * * result)
00338 {
00339     AUDDBG ("Probing output plugin %s.\n", plugin_get_name (handle));
00340     OutputPlugin * plugin = plugin_get_header (handle);
00341 
00342     if (plugin == NULL || plugin->init == NULL || plugin->init () !=
00343      OUTPUT_PLUGIN_INIT_FOUND_DEVICES)
00344         return TRUE;
00345 
00346     * result = plugin;
00347     return FALSE;
00348 }
00349 
00350 static OutputPlugin * output_probe (void)
00351 {
00352     OutputPlugin * plugin = NULL;
00353     plugin_for_each (PLUGIN_TYPE_OUTPUT, (PluginForEachFunc) output_probe_func,
00354      & plugin);
00355 
00356     if (plugin == NULL)
00357         fprintf (stderr, "ALL OUTPUT PLUGINS FAILED TO INITIALIZE.\n");
00358 
00359     return plugin;
00360 }
00361 
00362 void plugin_system_init(void)
00363 {
00364     gchar *dir;
00365     GtkWidget *dialog;
00366     gint dirsel = 0;
00367 
00368     audgui_init (& api_table);
00369 
00370     if (!g_module_supported())
00371     {
00372         dialog = gtk_message_dialog_new(NULL, GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, _("Module loading not supported! Plugins will not be loaded.\n"));
00373         gtk_dialog_run(GTK_DIALOG(dialog));
00374         gtk_widget_destroy(dialog);
00375         return;
00376     }
00377 
00378     plugin_registry_load ();
00379 
00380     headers_list = mowgli_list_create();
00381 
00382 #ifndef DISABLE_USER_PLUGIN_DIR
00383     scan_plugins(aud_paths[BMP_PATH_USER_PLUGIN_DIR]);
00384     /*
00385      * This is in a separate loop so if the user puts them in the
00386      * wrong dir we'll still get them in the right order (home dir
00387      * first)                                                - Zinx
00388      */
00389     while (plugin_dir_list[dirsel])
00390     {
00391         dir = g_build_filename(aud_paths[BMP_PATH_USER_PLUGIN_DIR], plugin_dir_list[dirsel++], NULL);
00392         scan_plugins(dir);
00393         g_free(dir);
00394     }
00395     dirsel = 0;
00396 #endif
00397 
00398     while (plugin_dir_list[dirsel])
00399     {
00400         dir = g_build_filename(PLUGIN_DIR, plugin_dir_list[dirsel++], NULL);
00401         scan_plugins(dir);
00402         g_free(dir);
00403     }
00404 
00405     plugin_registry_prune ();
00406 
00407     current_output_plugin = output_load_selected ();
00408 
00409     if (current_output_plugin == NULL)
00410         current_output_plugin = output_probe ();
00411 
00412     general_init ();
00413 }
00414 
00415 void plugin_system_cleanup(void)
00416 {
00417     mowgli_node_t *hlist_node;
00418 
00419     AUDDBG ("Shutting down plugin system.\n");
00420 
00421     if (current_output_plugin != NULL)
00422     {
00423         if (current_output_plugin->cleanup != NULL)
00424             current_output_plugin->cleanup ();
00425 
00426         current_output_plugin = NULL;
00427     }
00428 
00429     general_cleanup ();
00430 
00431     plugin_registry_save ();
00432 
00433     /* XXX: vfs will crash otherwise. -nenolod */
00434     if (vfs_transports != NULL)
00435     {
00436         g_list_free(vfs_transports);
00437         vfs_transports = NULL;
00438     }
00439 
00440     MOWGLI_LIST_FOREACH(hlist_node, headers_list->head) plugin2_unload(hlist_node->data, hlist_node);
00441 
00442     mowgli_list_free(headers_list);
00443 }