comparison 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
comparison
equal deleted inserted replaced
2312:e1a5a66fb9cc 2313:3149d4b1a9a9
1 /* Audacious - Cross-platform multimedia player
2 * Copyright (C) 2005-2007 Audacious development team
3 *
4 * Based on BMP:
5 * Copyright (C) 2003-2004 BMP development team
6 *
7 * Based on XMMS:
8 * Copyright (C) 1998-2003 XMMS development team
9 *
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; under version 2 of the License.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 */
23
24 #ifdef HAVE_CONFIG_H
25 # include "config.h"
26 #endif
27
28 #ifndef SHARED_SUFFIX
29 # define SHARED_SUFFIX G_MODULE_SUFFIX
30 #endif
31
32 #include "pluginenum.h"
33
34 #include <glib.h>
35 #include <gmodule.h>
36 #include <glib/gprintf.h>
37 #include <string.h>
38
39 #include "controlsocket.h"
40 #include "main.h"
41 #include "ui_main.h"
42 #include "playback.h"
43 #include "playlist.h"
44 #include "util.h"
45
46 #include "effect.h"
47 #include "general.h"
48 #include "input.h"
49 #include "output.h"
50 #include "visualization.h"
51
52 const gchar *plugin_dir_list[] = {
53 PLUGINSUBS,
54 NULL
55 };
56
57 GHashTable *plugin_matrix = NULL;
58 GList *lowlevel_list = NULL;
59
60 static gint
61 inputlist_compare_func(gconstpointer a, gconstpointer b)
62 {
63 const InputPlugin *ap = a, *bp = b;
64 return strcasecmp(ap->description, bp->description);
65 }
66
67 static gint
68 outputlist_compare_func(gconstpointer a, gconstpointer b)
69 {
70 const OutputPlugin *ap = a, *bp = b;
71 return strcasecmp(ap->description, bp->description);
72 }
73
74 static gint
75 effectlist_compare_func(gconstpointer a, gconstpointer b)
76 {
77 const EffectPlugin *ap = a, *bp = b;
78 return strcasecmp(ap->description, bp->description);
79 }
80
81 static gint
82 generallist_compare_func(gconstpointer a, gconstpointer b)
83 {
84 const GeneralPlugin *ap = a, *bp = b;
85 return strcasecmp(ap->description, bp->description);
86 }
87
88 static gint
89 vislist_compare_func(gconstpointer a, gconstpointer b)
90 {
91 const VisPlugin *ap = a, *bp = b;
92 return strcasecmp(ap->description, bp->description);
93 }
94
95 static gboolean
96 plugin_is_duplicate(const gchar * filename)
97 {
98 GList *l;
99 const gchar *basename = g_basename(filename);
100
101 /* FIXME: messy stuff */
102
103 for (l = ip_data.input_list; l; l = g_list_next(l))
104 if (!strcmp(basename, g_basename(INPUT_PLUGIN(l->data)->filename)))
105 return TRUE;
106
107 for (l = op_data.output_list; l; l = g_list_next(l))
108 if (!strcmp(basename, g_basename(OUTPUT_PLUGIN(l->data)->filename)))
109 return TRUE;
110
111 for (l = ep_data.effect_list; l; l = g_list_next(l))
112 if (!strcmp(basename, g_basename(EFFECT_PLUGIN(l->data)->filename)))
113 return TRUE;
114
115 for (l = gp_data.general_list; l; l = g_list_next(l))
116 if (!strcmp(basename, g_basename(GENERAL_PLUGIN(l->data)->filename)))
117 return TRUE;
118
119 for (l = vp_data.vis_list; l; l = g_list_next(l))
120 if (!strcmp(basename, g_basename(VIS_PLUGIN(l->data)->filename)))
121 return TRUE;
122
123 for (l = lowlevel_list; l; l = g_list_next(l))
124 if (!strcmp(basename, g_basename(VIS_PLUGIN(l->data)->filename)))
125 return TRUE;
126
127 return FALSE;
128 }
129
130
131 #define PLUGIN_GET_INFO(x) ((PluginGetInfoFunc)(x))()
132 typedef Plugin * (*PluginGetInfoFunc) (void);
133
134 static void
135 input_plugin_init(Plugin * plugin)
136 {
137 InputPlugin *p = INPUT_PLUGIN(plugin);
138
139 p->get_vis_type = input_get_vis_type;
140 p->add_vis_pcm = input_add_vis_pcm;
141
142 /* Pretty const casts courtesy of XMMS's plugin.h legacy. Anyone
143 else thinks we could use a CONST macro to solve the warnings?
144 - descender */
145 p->set_info = (void (*)(gchar *, gint, gint, gint, gint)) playlist_set_info_old_abi;
146 p->set_info_text = (void (*)(gchar *)) input_set_info_text;
147 p->set_status_buffering = (void (*)(gboolean)) input_set_status_buffering;
148
149 ip_data.input_list = g_list_append(ip_data.input_list, p);
150
151 g_hash_table_replace(plugin_matrix, g_path_get_basename(p->filename),
152 GINT_TO_POINTER(1));
153 }
154
155 static void
156 output_plugin_init(Plugin * plugin)
157 {
158 OutputPlugin *p = OUTPUT_PLUGIN(plugin);
159 op_data.output_list = g_list_append(op_data.output_list, p);
160 }
161
162 static void
163 effect_plugin_init(Plugin * plugin)
164 {
165 EffectPlugin *p = EFFECT_PLUGIN(plugin);
166 ep_data.effect_list = g_list_append(ep_data.effect_list, p);
167 }
168
169 static void
170 general_plugin_init(Plugin * plugin)
171 {
172 GeneralPlugin *p = GENERAL_PLUGIN(plugin);
173 p->xmms_session = ctrlsocket_get_session_id();
174 gp_data.general_list = g_list_append(gp_data.general_list, p);
175 }
176
177 static void
178 vis_plugin_init(Plugin * plugin)
179 {
180 VisPlugin *p = VIS_PLUGIN(plugin);
181 p->xmms_session = ctrlsocket_get_session_id();
182 p->disable_plugin = vis_disable_plugin;
183 vp_data.vis_list = g_list_append(vp_data.vis_list, p);
184 }
185
186 static void
187 lowlevel_plugin_init(Plugin * plugin)
188 {
189 LowlevelPlugin *p = LOWLEVEL_PLUGIN(plugin);
190 lowlevel_list = g_list_append(lowlevel_list, p);
191 }
192
193 /* FIXME: Placed here (hopefully) temporarily - descender */
194
195 typedef struct {
196 const gchar *name;
197 const gchar *id;
198 void (*init)(Plugin *);
199 } PluginType;
200
201 static PluginType plugin_types[] = {
202 { "input" , "get_iplugin_info", input_plugin_init },
203 { "output" , "get_oplugin_info", output_plugin_init },
204 { "effect" , "get_eplugin_info", effect_plugin_init },
205 { "general" , "get_gplugin_info", general_plugin_init },
206 { "visualization", "get_vplugin_info", vis_plugin_init },
207 { "lowlevel" , "get_lplugin_info", lowlevel_plugin_init },
208 { NULL, NULL, NULL }
209 };
210
211 static void
212 add_plugin(const gchar * filename)
213 {
214 PluginType *type;
215 GModule *module;
216 gpointer func;
217
218 if (plugin_is_duplicate(filename))
219 return;
220
221 if (!(module = g_module_open(filename, G_MODULE_BIND_LOCAL))) {
222 printf("Failed to load plugin (%s): %s\n",
223 filename, g_module_error());
224 return;
225 }
226
227 for (type = plugin_types; type->name; type++)
228 {
229 if (g_module_symbol(module, type->id, &func)) {
230 Plugin *plugin = PLUGIN_GET_INFO(func);
231
232 plugin->handle = module;
233 plugin->filename = g_strdup(filename);
234 type->init(PLUGIN_GET_INFO(func));
235
236 return;
237 }
238 }
239
240 printf("Invalid plugin (%s)\n", filename);
241 g_module_close(module);
242 }
243
244 static gboolean
245 scan_plugin_func(const gchar * path, const gchar * basename, gpointer data)
246 {
247 if (!str_has_suffix_nocase(basename, SHARED_SUFFIX))
248 return FALSE;
249
250 if (!g_file_test(path, G_FILE_TEST_IS_REGULAR))
251 return FALSE;
252
253 add_plugin(path);
254
255 return FALSE;
256 }
257
258 static void
259 scan_plugins(const gchar * path)
260 {
261 dir_foreach(path, scan_plugin_func, NULL, NULL);
262 }
263
264 void
265 plugin_system_init(void)
266 {
267 gchar *dir, **disabled;
268 GList *node;
269 OutputPlugin *op;
270 InputPlugin *ip;
271 LowlevelPlugin *lp;
272 gint dirsel = 0, i = 0;
273
274 if (!g_module_supported()) {
275 report_error("Module loading not supported! Plugins will not be loaded.\n");
276 return;
277 }
278
279 plugin_matrix = g_hash_table_new_full(g_str_hash, g_int_equal, g_free,
280 NULL);
281
282 #ifndef DISABLE_USER_PLUGIN_DIR
283 scan_plugins(bmp_paths[BMP_PATH_USER_PLUGIN_DIR]);
284 /*
285 * This is in a separate loop so if the user puts them in the
286 * wrong dir we'll still get them in the right order (home dir
287 * first) - Zinx
288 */
289 while (plugin_dir_list[dirsel]) {
290 dir = g_build_filename(bmp_paths[BMP_PATH_USER_PLUGIN_DIR],
291 plugin_dir_list[dirsel++], NULL);
292 scan_plugins(dir);
293 g_free(dir);
294 }
295 dirsel = 0;
296 #endif
297
298 while (plugin_dir_list[dirsel]) {
299 dir = g_build_filename(PLUGIN_DIR, plugin_dir_list[dirsel++], NULL);
300 scan_plugins(dir);
301 g_free(dir);
302 }
303
304 op_data.output_list = g_list_sort(op_data.output_list, outputlist_compare_func);
305 if (!op_data.current_output_plugin
306 && g_list_length(op_data.output_list)) {
307 op_data.current_output_plugin = op_data.output_list->data;
308 }
309
310 ip_data.input_list = g_list_sort(ip_data.input_list, inputlist_compare_func);
311
312 ep_data.effect_list = g_list_sort(ep_data.effect_list, effectlist_compare_func);
313 ep_data.enabled_list = NULL;
314
315 gp_data.general_list = g_list_sort(gp_data.general_list, generallist_compare_func);
316 gp_data.enabled_list = NULL;
317
318 vp_data.vis_list = g_list_sort(vp_data.vis_list, vislist_compare_func);
319 vp_data.enabled_list = NULL;
320
321 general_enable_from_stringified_list(cfg.enabled_gplugins);
322 vis_enable_from_stringified_list(cfg.enabled_vplugins);
323 effect_enable_from_stringified_list(cfg.enabled_eplugins);
324
325 g_free(cfg.enabled_gplugins);
326 cfg.enabled_gplugins = NULL;
327
328 g_free(cfg.enabled_vplugins);
329 cfg.enabled_vplugins = NULL;
330
331 g_free(cfg.enabled_eplugins);
332 cfg.enabled_eplugins = NULL;
333
334 for (node = op_data.output_list; node; node = g_list_next(node)) {
335 op = OUTPUT_PLUGIN(node->data);
336 /*
337 * Only test basename to avoid problems when changing
338 * prefix. We will only see one plugin with the same
339 * basename, so this is usually what the user want.
340 */
341 if (!strcmp(g_basename(cfg.outputplugin), g_basename(op->filename)))
342 op_data.current_output_plugin = op;
343 if (op->init)
344 op->init();
345 }
346
347 for (node = ip_data.input_list; node; node = g_list_next(node)) {
348 ip = INPUT_PLUGIN(node->data);
349 if (ip->init)
350 ip->init();
351 }
352
353 for (node = lowlevel_list; node; node = g_list_next(node)) {
354 lp = LOWLEVEL_PLUGIN(node->data);
355 if (lp->init)
356 lp->init();
357 }
358
359 if (cfg.disabled_iplugins) {
360 disabled = g_strsplit(cfg.disabled_iplugins, ":", 0);
361 while (disabled[i]) {
362 g_hash_table_replace(plugin_matrix, disabled[i],
363 GINT_TO_POINTER(FALSE));
364 i++;
365 }
366
367 g_free(disabled);
368
369 g_free(cfg.disabled_iplugins);
370 cfg.disabled_iplugins = NULL;
371 }
372 }
373
374 void
375 plugin_system_cleanup(void)
376 {
377 InputPlugin *ip;
378 OutputPlugin *op;
379 EffectPlugin *ep;
380 GeneralPlugin *gp;
381 VisPlugin *vp;
382 LowlevelPlugin *lp;
383 GList *node;
384
385 g_message("Shutting down plugin system");
386
387 if (playback_get_playing()) {
388 ip_data.stop = TRUE;
389 playback_stop();
390 ip_data.stop = FALSE;
391 }
392
393 for (node = get_input_list(); node; node = g_list_next(node)) {
394 ip = INPUT_PLUGIN(node->data);
395 if (ip && ip->cleanup) {
396 ip->cleanup();
397 GDK_THREADS_LEAVE();
398 while (g_main_context_iteration(NULL, FALSE));
399 GDK_THREADS_ENTER();
400 }
401 g_module_close(ip->handle);
402 }
403
404 if (ip_data.input_list)
405 g_list_free(ip_data.input_list);
406
407 for (node = get_output_list(); node; node = g_list_next(node)) {
408 op = OUTPUT_PLUGIN(node->data);
409 if (op && op->cleanup) {
410 op->cleanup();
411 GDK_THREADS_LEAVE();
412 while (g_main_context_iteration(NULL, FALSE));
413 GDK_THREADS_ENTER();
414 }
415 g_module_close(op->handle);
416 }
417
418 if (op_data.output_list)
419 g_list_free(op_data.output_list);
420
421 for (node = get_effect_list(); node; node = g_list_next(node)) {
422 ep = EFFECT_PLUGIN(node->data);
423 if (ep && ep->cleanup) {
424 ep->cleanup();
425 GDK_THREADS_LEAVE();
426 while (g_main_context_iteration(NULL, FALSE));
427 GDK_THREADS_ENTER();
428 }
429 g_module_close(ep->handle);
430 }
431
432 if (ep_data.effect_list)
433 g_list_free(ep_data.effect_list);
434
435 #if 0
436 for (node = get_general_enabled_list(); node; node = g_list_next(node)) {
437 gp = GENERAL_PLUGIN(node->data);
438 enable_general_plugin(g_list_index(gp_data.general_list, gp), FALSE);
439 }
440
441 if (gp_data.enabled_list)
442 g_list_free(gp_data.enabled_list);
443
444 GDK_THREADS_LEAVE();
445 while (g_main_context_iteration(NULL, FALSE));
446 GDK_THREADS_ENTER();
447 #endif
448
449 for (node = get_general_list(); node; node = g_list_next(node)) {
450 gp = GENERAL_PLUGIN(node->data);
451 if (gp && gp->cleanup) {
452 gp->cleanup();
453 GDK_THREADS_LEAVE();
454 while (g_main_context_iteration(NULL, FALSE));
455 GDK_THREADS_ENTER();
456 }
457 g_module_close(gp->handle);
458 }
459
460 if (gp_data.general_list)
461 g_list_free(gp_data.general_list);
462
463 #if 0
464 for (node = get_vis_enabled_list(); node; node = g_list_next(node)) {
465 vp = VIS_PLUGIN(node->data);
466 enable_vis_plugin(g_list_index(vp_data.vis_list, vp), FALSE);
467 }
468
469 if (vp_data.enabled_list)
470 g_list_free(vp_data.enabled_list);
471
472 GDK_THREADS_LEAVE();
473 while (g_main_context_iteration(NULL, FALSE));
474 GDK_THREADS_ENTER();
475 #endif
476
477 for (node = get_vis_list(); node; node = g_list_next(node)) {
478 vp = VIS_PLUGIN(node->data);
479 if (vp && vp->cleanup) {
480 vp->cleanup();
481 GDK_THREADS_LEAVE();
482 while (g_main_context_iteration(NULL, FALSE));
483 GDK_THREADS_ENTER();
484 }
485 g_module_close(vp->handle);
486 }
487
488 if (vp_data.vis_list)
489 g_list_free(vp_data.vis_list);
490
491 for (node = lowlevel_list; node; node = g_list_next(node)) {
492 lp = LOWLEVEL_PLUGIN(node->data);
493 if (lp && lp->cleanup) {
494 lp->cleanup();
495 GDK_THREADS_LEAVE();
496 while (g_main_context_iteration(NULL, FALSE));
497 GDK_THREADS_ENTER();
498 }
499 g_module_close(lp->handle);
500 }
501
502 if (lowlevel_list)
503 g_list_free(lowlevel_list);
504 }