comparison audacious/pluginenum.c @ 0:cb178e5ad177 trunk

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