00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <glib.h>
00023 #include <limits.h>
00024 #include <stdio.h>
00025 #include <string.h>
00026
00027 #include <libaudcore/audstrings.h>
00028
00029 #include "debug.h"
00030 #include "interface.h"
00031 #include "misc.h"
00032 #include "plugin.h"
00033 #include "plugins.h"
00034 #include "util.h"
00035
00036 #define FILENAME "plugin-registry"
00037 #define FORMAT 5
00038
00039 typedef struct {
00040 gchar * path;
00041 gboolean confirmed;
00042 gint timestamp;
00043 gboolean loaded;
00044 GList * plugin_list;
00045 } ModuleData;
00046
00047 typedef struct {
00048 GList * schemes;
00049 } TransportPluginData;
00050
00051 typedef struct {
00052 GList * exts;
00053 } PlaylistPluginData;
00054
00055 typedef struct {
00056 GList * keys[INPUT_KEYS];
00057 gboolean has_images, has_subtunes, can_write_tuple, has_infowin;
00058 } InputPluginData;
00059
00060 struct PluginHandle {
00061 ModuleData * module;
00062 gint type, number;
00063 gboolean confirmed;
00064 const void * header;
00065 gchar * name;
00066 gint priority;
00067 gboolean has_about, has_configure, enabled;
00068 GList * watches;
00069
00070 union {
00071 TransportPluginData t;
00072 PlaylistPluginData p;
00073 InputPluginData i;
00074 } u;
00075 };
00076
00077 typedef struct {
00078 PluginForEachFunc func;
00079 void * data;
00080 } PluginWatch;
00081
00082 static const gchar * plugin_type_names[] = {
00083 [PLUGIN_TYPE_LOWLEVEL] = NULL,
00084 [PLUGIN_TYPE_TRANSPORT] = "transport",
00085 [PLUGIN_TYPE_PLAYLIST] = "playlist",
00086 [PLUGIN_TYPE_INPUT] = "input",
00087 [PLUGIN_TYPE_EFFECT] = "effect",
00088 [PLUGIN_TYPE_OUTPUT] = "output",
00089 [PLUGIN_TYPE_VIS] = "vis",
00090 [PLUGIN_TYPE_GENERAL] = "general",
00091 [PLUGIN_TYPE_IFACE] = "iface"};
00092 static const gchar * input_key_names[] = {
00093 [INPUT_KEY_SCHEME] = "scheme",
00094 [INPUT_KEY_EXTENSION] = "ext",
00095 [INPUT_KEY_MIME] = "mime"};
00096 static GList * module_list = NULL;
00097 static GList * plugin_list = NULL;
00098 static gboolean registry_locked = TRUE;
00099
00100 static ModuleData * module_new (gchar * path, gboolean confirmed, gint
00101 timestamp, gboolean loaded)
00102 {
00103 ModuleData * module = g_malloc (sizeof (ModuleData));
00104
00105 module->path = path;
00106 module->confirmed = confirmed;
00107 module->timestamp = timestamp;
00108 module->loaded = loaded;
00109 module->plugin_list = NULL;
00110
00111 module_list = g_list_prepend (module_list, module);
00112
00113 return module;
00114 }
00115
00116 static PluginHandle * plugin_new (ModuleData * module, gint type, gint number,
00117 gboolean confirmed, const void * header)
00118 {
00119 PluginHandle * plugin = g_malloc (sizeof (PluginHandle));
00120
00121 plugin->module = module;
00122 plugin->type = type;
00123 plugin->number = number;
00124 plugin->confirmed = confirmed;
00125 plugin->header = header;
00126 plugin->name = NULL;
00127 plugin->priority = 0;
00128 plugin->has_about = FALSE;
00129 plugin->has_configure = FALSE;
00130 plugin->enabled = FALSE;
00131 plugin->watches = NULL;
00132
00133 if (type == PLUGIN_TYPE_TRANSPORT)
00134 {
00135 plugin->enabled = TRUE;
00136 plugin->u.t.schemes = NULL;
00137 }
00138 else if (type == PLUGIN_TYPE_PLAYLIST)
00139 {
00140 plugin->enabled = TRUE;
00141 plugin->u.p.exts = NULL;
00142 }
00143 else if (type == PLUGIN_TYPE_INPUT)
00144 {
00145 plugin->enabled = TRUE;
00146 memset (plugin->u.i.keys, 0, sizeof plugin->u.i.keys);
00147 plugin->u.i.has_images = FALSE;
00148 plugin->u.i.has_subtunes = FALSE;
00149 plugin->u.i.can_write_tuple = FALSE;
00150 plugin->u.i.has_infowin = FALSE;
00151 }
00152
00153 plugin_list = g_list_prepend (plugin_list, plugin);
00154 module->plugin_list = g_list_prepend (module->plugin_list, plugin);
00155
00156 return plugin;
00157 }
00158
00159 static void plugin_free (PluginHandle * plugin, ModuleData * module)
00160 {
00161 plugin_list = g_list_remove (plugin_list, plugin);
00162 module->plugin_list = g_list_remove (module->plugin_list, plugin);
00163
00164 g_list_foreach (plugin->watches, (GFunc) g_free, NULL);
00165 g_list_free (plugin->watches);
00166
00167 if (plugin->type == PLUGIN_TYPE_TRANSPORT)
00168 {
00169 g_list_foreach (plugin->u.t.schemes, (GFunc) g_free, NULL);
00170 g_list_free (plugin->u.t.schemes);
00171 }
00172 else if (plugin->type == PLUGIN_TYPE_PLAYLIST)
00173 {
00174 g_list_foreach (plugin->u.p.exts, (GFunc) g_free, NULL);
00175 g_list_free (plugin->u.p.exts);
00176 }
00177 else if (plugin->type == PLUGIN_TYPE_INPUT)
00178 {
00179 for (gint key = 0; key < INPUT_KEYS; key ++)
00180 {
00181 g_list_foreach (plugin->u.i.keys[key], (GFunc) g_free, NULL);
00182 g_list_free (plugin->u.i.keys[key]);
00183 }
00184 }
00185
00186 g_free (plugin->name);
00187 g_free (plugin);
00188 }
00189
00190 static void module_free (ModuleData * module)
00191 {
00192 module_list = g_list_remove (module_list, module);
00193
00194 g_list_foreach (module->plugin_list, (GFunc) plugin_free, module);
00195
00196 g_free (module->path);
00197 g_free (module);
00198 }
00199
00200 static FILE * open_registry_file (const gchar * mode)
00201 {
00202 gchar path[PATH_MAX];
00203 snprintf (path, sizeof path, "%s/" FILENAME, get_path (AUD_PATH_USER_DIR));
00204 return fopen (path, mode);
00205 }
00206
00207 static void transport_plugin_save (PluginHandle * plugin, FILE * handle)
00208 {
00209 for (GList * node = plugin->u.t.schemes; node; node = node->next)
00210 fprintf (handle, "scheme %s\n", (const gchar *) node->data);
00211 }
00212
00213 static void playlist_plugin_save (PluginHandle * plugin, FILE * handle)
00214 {
00215 for (GList * node = plugin->u.p.exts; node; node = node->next)
00216 fprintf (handle, "ext %s\n", (const gchar *) node->data);
00217 }
00218
00219 static void input_plugin_save (PluginHandle * plugin, FILE * handle)
00220 {
00221 for (gint key = 0; key < INPUT_KEYS; key ++)
00222 {
00223 for (GList * node = plugin->u.i.keys[key]; node != NULL; node =
00224 node->next)
00225 fprintf (handle, "%s %s\n", input_key_names[key], (const gchar *)
00226 node->data);
00227 }
00228
00229 fprintf (handle, "images %d\n", plugin->u.i.has_images);
00230 fprintf (handle, "subtunes %d\n", plugin->u.i.has_subtunes);
00231 fprintf (handle, "writes %d\n", plugin->u.i.can_write_tuple);
00232 fprintf (handle, "infowin %d\n", plugin->u.i.has_infowin);
00233 }
00234
00235 static void plugin_save (PluginHandle * plugin, FILE * handle)
00236 {
00237 fprintf (handle, "%s %d\n", plugin_type_names[plugin->type], plugin->number);
00238 fprintf (handle, "name %s\n", plugin->name);
00239 fprintf (handle, "priority %d\n", plugin->priority);
00240 fprintf (handle, "about %d\n", plugin->has_about);
00241 fprintf (handle, "config %d\n", plugin->has_configure);
00242 fprintf (handle, "enabled %d\n", plugin->enabled);
00243
00244 if (plugin->type == PLUGIN_TYPE_TRANSPORT)
00245 transport_plugin_save (plugin, handle);
00246 else if (plugin->type == PLUGIN_TYPE_PLAYLIST)
00247 playlist_plugin_save (plugin, handle);
00248 else if (plugin->type == PLUGIN_TYPE_INPUT)
00249 input_plugin_save (plugin, handle);
00250 }
00251
00252 static gint plugin_not_handled_cb (PluginHandle * plugin)
00253 {
00254 return (plugin_type_names[plugin->type] == NULL) ? 0 : -1;
00255 }
00256
00257 static void module_save (ModuleData * module, FILE * handle)
00258 {
00259
00260
00261
00262
00263 if (! module->plugin_list || g_list_find_custom (module->plugin_list, NULL,
00264 (GCompareFunc) plugin_not_handled_cb) != NULL)
00265 return;
00266
00267 fprintf (handle, "module %s\n", module->path);
00268 fprintf (handle, "stamp %d\n", module->timestamp);
00269
00270 g_list_foreach (module->plugin_list, (GFunc) plugin_save, handle);
00271 }
00272
00273 void plugin_registry_save (void)
00274 {
00275 FILE * handle = open_registry_file ("w");
00276 g_return_if_fail (handle != NULL);
00277
00278 fprintf (handle, "format %d\n", FORMAT);
00279
00280 g_list_foreach (module_list, (GFunc) module_save, handle);
00281 fclose (handle);
00282
00283 g_list_foreach (module_list, (GFunc) module_free, NULL);
00284 registry_locked = TRUE;
00285 }
00286
00287 static gchar parse_key[512];
00288 static gchar * parse_value;
00289
00290 static void parse_next (FILE * handle)
00291 {
00292 parse_value = NULL;
00293
00294 if (fgets (parse_key, sizeof parse_key, handle) == NULL)
00295 return;
00296
00297 gchar * space = strchr (parse_key, ' ');
00298 if (space == NULL)
00299 return;
00300
00301 * space = 0;
00302 parse_value = space + 1;
00303
00304 gchar * newline = strchr (parse_value, '\n');
00305 if (newline != NULL)
00306 * newline = 0;
00307 }
00308
00309 static gboolean parse_integer (const gchar * key, gint * value)
00310 {
00311 return (parse_value != NULL && ! strcmp (parse_key, key) && sscanf
00312 (parse_value, "%d", value) == 1);
00313 }
00314
00315 static gchar * parse_string (const gchar * key)
00316 {
00317 return (parse_value != NULL && ! strcmp (parse_key, key)) ? g_strdup
00318 (parse_value) : NULL;
00319 }
00320
00321 static void transport_plugin_parse (PluginHandle * plugin, FILE * handle)
00322 {
00323 gchar * value;
00324 while ((value = parse_string ("scheme")))
00325 {
00326 plugin->u.t.schemes = g_list_prepend (plugin->u.t.schemes, value);
00327 parse_next (handle);
00328 }
00329 }
00330
00331 static void playlist_plugin_parse (PluginHandle * plugin, FILE * handle)
00332 {
00333 gchar * value;
00334 while ((value = parse_string ("ext")))
00335 {
00336 plugin->u.p.exts = g_list_prepend (plugin->u.p.exts, value);
00337 parse_next (handle);
00338 }
00339 }
00340
00341 static void input_plugin_parse (PluginHandle * plugin, FILE * handle)
00342 {
00343 for (gint key = 0; key < INPUT_KEYS; key ++)
00344 {
00345 gchar * value;
00346 while ((value = parse_string (input_key_names[key])) != NULL)
00347 {
00348 plugin->u.i.keys[key] = g_list_prepend (plugin->u.i.keys[key],
00349 value);
00350 parse_next (handle);
00351 }
00352 }
00353
00354 if (parse_integer ("images", & plugin->u.i.has_images))
00355 parse_next (handle);
00356 if (parse_integer ("subtunes", & plugin->u.i.has_subtunes))
00357 parse_next (handle);
00358 if (parse_integer ("writes", & plugin->u.i.can_write_tuple))
00359 parse_next (handle);
00360 if (parse_integer ("infowin", & plugin->u.i.has_infowin))
00361 parse_next (handle);
00362 }
00363
00364 static gboolean plugin_parse (ModuleData * module, FILE * handle)
00365 {
00366 gint type, number;
00367 for (type = 0; type < PLUGIN_TYPES; type ++)
00368 {
00369 if (plugin_type_names[type] != NULL && parse_integer
00370 (plugin_type_names[type], & number))
00371 goto FOUND;
00372 }
00373
00374 return FALSE;
00375
00376 FOUND:;
00377 PluginHandle * plugin = plugin_new (module, type, number, FALSE, NULL);
00378 parse_next (handle);
00379
00380 if ((plugin->name = parse_string ("name")) != NULL)
00381 parse_next (handle);
00382 if (parse_integer ("priority", & plugin->priority))
00383 parse_next (handle);
00384 if (parse_integer ("about", & plugin->has_about))
00385 parse_next (handle);
00386 if (parse_integer ("config", & plugin->has_configure))
00387 parse_next (handle);
00388 if (parse_integer ("enabled", & plugin->enabled))
00389 parse_next (handle);
00390
00391 if (type == PLUGIN_TYPE_TRANSPORT)
00392 transport_plugin_parse (plugin, handle);
00393 else if (type == PLUGIN_TYPE_PLAYLIST)
00394 playlist_plugin_parse (plugin, handle);
00395 else if (type == PLUGIN_TYPE_INPUT)
00396 input_plugin_parse (plugin, handle);
00397
00398 return TRUE;
00399 }
00400
00401 static gboolean module_parse (FILE * handle)
00402 {
00403 gchar * path = parse_string ("module");
00404 if (path == NULL)
00405 return FALSE;
00406
00407 parse_next (handle);
00408
00409 gint timestamp;
00410 if (! parse_integer ("stamp", & timestamp))
00411 {
00412 g_free (path);
00413 return FALSE;
00414 }
00415
00416 ModuleData * module = module_new (path, FALSE, timestamp, FALSE);
00417 parse_next (handle);
00418
00419 while (plugin_parse (module, handle))
00420 ;
00421
00422 return TRUE;
00423 }
00424
00425 void plugin_registry_load (void)
00426 {
00427 FILE * handle = open_registry_file ("r");
00428 if (handle == NULL)
00429 goto UNLOCK;
00430
00431 parse_next (handle);
00432
00433 gint format;
00434 if (! parse_integer ("format", & format) || format != FORMAT)
00435 goto ERR;
00436
00437 parse_next (handle);
00438
00439 while (module_parse (handle))
00440 ;
00441
00442 ERR:
00443 fclose (handle);
00444 UNLOCK:
00445 registry_locked = FALSE;
00446 }
00447
00448 static void plugin_prune (PluginHandle * plugin, ModuleData * module)
00449 {
00450 if (plugin->confirmed)
00451 return;
00452
00453 AUDDBG ("Plugin not found: %s %d:%d\n", plugin->module->path, plugin->type,
00454 plugin->number);
00455 plugin_free (plugin, module);
00456 }
00457
00458 static void module_prune (ModuleData * module)
00459 {
00460 if (! module->confirmed)
00461 {
00462 AUDDBG ("Module not found: %s\n", module->path);
00463 module_free (module);
00464 return;
00465 }
00466
00467 if (module->loaded)
00468 g_list_foreach (module->plugin_list, (GFunc) plugin_prune, module);
00469 }
00470
00471 gint plugin_compare (PluginHandle * a, PluginHandle * b)
00472 {
00473 if (a->type < b->type)
00474 return -1;
00475 if (a->type > b->type)
00476 return 1;
00477 if (a->priority < b->priority)
00478 return -1;
00479 if (a->priority > b->priority)
00480 return 1;
00481
00482 gint diff;
00483 if ((diff = string_compare (a->name, b->name)))
00484 return diff;
00485 if ((diff = string_compare (a->module->path, b->module->path)))
00486 return diff;
00487
00488 if (a->number < b->number)
00489 return -1;
00490 if (a->number > b->number)
00491 return 1;
00492
00493 return 0;
00494 }
00495
00496 void plugin_registry_prune (void)
00497 {
00498 g_list_foreach (module_list, (GFunc) module_prune, NULL);
00499 plugin_list = g_list_sort (plugin_list, (GCompareFunc) plugin_compare);
00500 registry_locked = TRUE;
00501 }
00502
00503 static gint module_lookup_cb (ModuleData * module, const gchar * path)
00504 {
00505 return strcmp (module->path, path);
00506 }
00507
00508 static ModuleData * module_lookup (const gchar * path)
00509 {
00510 GList * node = g_list_find_custom (module_list, path, (GCompareFunc)
00511 module_lookup_cb);
00512 return (node != NULL) ? node->data : NULL;
00513 }
00514
00515 void module_register (const gchar * path)
00516 {
00517 gint timestamp = file_get_mtime (path);
00518 g_return_if_fail (timestamp >= 0);
00519
00520 ModuleData * module = module_lookup (path);
00521 if (module == NULL)
00522 {
00523 AUDDBG ("New module: %s\n", path);
00524 g_return_if_fail (! registry_locked);
00525 module = module_new (g_strdup (path), TRUE, timestamp, TRUE);
00526 module_load (path);
00527 module->loaded = TRUE;
00528 return;
00529 }
00530
00531 AUDDBG ("Register module: %s\n", path);
00532 module->confirmed = TRUE;
00533 if (module->timestamp == timestamp)
00534 return;
00535
00536 AUDDBG ("Rescan module: %s\n", path);
00537 module->timestamp = timestamp;
00538 module_load (path);
00539 module->loaded = TRUE;
00540 }
00541
00542 typedef struct {
00543 gint type, number;
00544 } PluginLookupState;
00545
00546 static gint plugin_lookup_cb (PluginHandle * plugin, PluginLookupState * state)
00547 {
00548 return (plugin->type == state->type && plugin->number == state->number) ? 0
00549 : -1;
00550 }
00551
00552 static PluginHandle * plugin_lookup_real (ModuleData * module, gint type, gint
00553 number)
00554 {
00555 PluginLookupState state = {type, number};
00556 GList * node = g_list_find_custom (module->plugin_list, & state,
00557 (GCompareFunc) plugin_lookup_cb);
00558 return (node != NULL) ? node->data : NULL;
00559 }
00560
00561 void plugin_register (gint type, const gchar * path, gint number, const void *
00562 header)
00563 {
00564 ModuleData * module = module_lookup (path);
00565 g_return_if_fail (module != NULL);
00566
00567 PluginHandle * plugin = plugin_lookup_real (module, type, number);
00568 if (plugin == NULL)
00569 {
00570 AUDDBG ("New plugin: %s %d:%d\n", path, type, number);
00571 g_return_if_fail (! registry_locked);
00572 plugin = plugin_new (module, type, number, TRUE, header);
00573 if (type == PLUGIN_TYPE_GENERAL) {
00574 if (path && g_strrstr(path,"gnomeshortcuts.so")!=NULL) {
00575 plugin->enabled = TRUE;
00576 }
00577 }
00578 }
00579
00580 AUDDBG ("Register plugin: %s %d:%d\n", path, type, number);
00581 plugin->confirmed = TRUE;
00582 plugin->header = header;
00583
00584 if (type != PLUGIN_TYPE_LOWLEVEL && type != PLUGIN_TYPE_IFACE)
00585 {
00586 Plugin * gp = header;
00587 g_free (plugin->name);
00588 plugin->name = g_strdup (gp->description);
00589 plugin->has_about = (gp->about != NULL);
00590 plugin->has_configure = (gp->configure != NULL || gp->settings != NULL);
00591 }
00592
00593 if (type == PLUGIN_TYPE_TRANSPORT)
00594 {
00595 TransportPlugin * tp = header;
00596 for (gint i = 0; tp->schemes[i]; i ++)
00597 plugin->u.t.schemes = g_list_prepend (plugin->u.t.schemes, g_strdup
00598 (tp->schemes[i]));
00599 }
00600 else if (type == PLUGIN_TYPE_PLAYLIST)
00601 {
00602 PlaylistPlugin * pp = header;
00603 for (gint i = 0; pp->extensions[i]; i ++)
00604 plugin->u.p.exts = g_list_prepend (plugin->u.p.exts, g_strdup
00605 (pp->extensions[i]));
00606 }
00607 else if (type == PLUGIN_TYPE_INPUT)
00608 {
00609 InputPlugin * ip = header;
00610 plugin->priority = ip->priority;
00611
00612 for (gint key = 0; key < INPUT_KEYS; key ++)
00613 {
00614 g_list_foreach (plugin->u.i.keys[key], (GFunc) g_free, NULL);
00615 g_list_free (plugin->u.i.keys[key]);
00616 plugin->u.i.keys[key] = NULL;
00617 }
00618
00619 if (ip->vfs_extensions != NULL)
00620 {
00621 for (gint i = 0; ip->vfs_extensions[i] != NULL; i ++)
00622 plugin->u.i.keys[INPUT_KEY_EXTENSION] = g_list_prepend
00623 (plugin->u.i.keys[INPUT_KEY_EXTENSION], g_strdup
00624 (ip->vfs_extensions[i]));
00625 }
00626
00627 plugin->u.i.has_images = (ip->get_song_image != NULL);
00628 plugin->u.i.has_subtunes = ip->have_subtune;
00629 plugin->u.i.can_write_tuple = (ip->update_song_tuple != NULL);
00630 plugin->u.i.has_infowin = (ip->file_info_box != NULL);
00631 }
00632 else if (type == PLUGIN_TYPE_OUTPUT)
00633 {
00634 OutputPlugin * op = header;
00635 plugin->priority = 10 - op->probe_priority;
00636 }
00637 else if (type == PLUGIN_TYPE_EFFECT)
00638 {
00639 EffectPlugin * ep = header;
00640 plugin->priority = ep->order;
00641 }
00642 else if (type == PLUGIN_TYPE_IFACE)
00643 {
00644 Iface * i = (void *) header;
00645 g_free (plugin->name);
00646 plugin->name = g_strdup (i->desc);
00647 }
00648 }
00649
00650 gint plugin_get_type (PluginHandle * plugin)
00651 {
00652 return plugin->type;
00653 }
00654
00655 const gchar * plugin_get_filename (PluginHandle * plugin)
00656 {
00657 return plugin->module->path;
00658 }
00659
00660 gint plugin_get_number (PluginHandle * plugin)
00661 {
00662 return plugin->number;
00663 }
00664
00665 PluginHandle * plugin_lookup (gint type, const gchar * path, gint number)
00666 {
00667 ModuleData * module = module_lookup (path);
00668 if (module == NULL)
00669 return NULL;
00670
00671 return plugin_lookup_real (module, type, number);
00672 }
00673
00674 const void * plugin_get_header (PluginHandle * plugin)
00675 {
00676 if (! plugin->module->loaded)
00677 {
00678 module_load (plugin->module->path);
00679 plugin->module->loaded = TRUE;
00680 }
00681
00682 return plugin->header;
00683 }
00684
00685 static gint plugin_by_header_cb (PluginHandle * plugin, const void * header)
00686 {
00687 return (plugin->header == header) ? 0 : -1;
00688 }
00689
00690 PluginHandle * plugin_by_header (const void * header)
00691 {
00692 GList * node = g_list_find_custom (plugin_list, header, (GCompareFunc)
00693 plugin_by_header_cb);
00694 return (node != NULL) ? node->data : NULL;
00695 }
00696
00697 void plugin_for_each (gint type, PluginForEachFunc func, void * data)
00698 {
00699 for (GList * node = plugin_list; node != NULL; node = node->next)
00700 {
00701 if (((PluginHandle *) node->data)->type != type)
00702 continue;
00703 if (! func (node->data, data))
00704 break;
00705 }
00706 }
00707
00708 const gchar * plugin_get_name (PluginHandle * plugin)
00709 {
00710 return plugin->name;
00711 }
00712
00713 gboolean plugin_has_about (PluginHandle * plugin)
00714 {
00715 return plugin->has_about;
00716 }
00717
00718 gboolean plugin_has_configure (PluginHandle * plugin)
00719 {
00720 return plugin->has_configure;
00721 }
00722
00723 gboolean plugin_get_enabled (PluginHandle * plugin)
00724 {
00725 return plugin->enabled;
00726 }
00727
00728 static void plugin_call_watches (PluginHandle * plugin)
00729 {
00730 for (GList * node = plugin->watches; node != NULL; )
00731 {
00732 GList * next = node->next;
00733 PluginWatch * watch = node->data;
00734
00735 if (! watch->func (plugin, watch->data))
00736 {
00737 g_free (watch);
00738 plugin->watches = g_list_delete_link (plugin->watches, node);
00739 }
00740
00741 node = next;
00742 }
00743 }
00744
00745 void plugin_set_enabled (PluginHandle * plugin, gboolean enabled)
00746 {
00747 plugin->enabled = enabled;
00748 plugin_call_watches (plugin);
00749 }
00750
00751 typedef struct {
00752 PluginForEachFunc func;
00753 void * data;
00754 } PluginForEnabledState;
00755
00756 static gboolean plugin_for_enabled_cb (PluginHandle * plugin,
00757 PluginForEnabledState * state)
00758 {
00759 if (! plugin->enabled)
00760 return TRUE;
00761 return state->func (plugin, state->data);
00762 }
00763
00764 void plugin_for_enabled (gint type, PluginForEachFunc func, void * data)
00765 {
00766 PluginForEnabledState state = {func, data};
00767 plugin_for_each (type, (PluginForEachFunc) plugin_for_enabled_cb, & state);
00768 }
00769
00770 void plugin_add_watch (PluginHandle * plugin, PluginForEachFunc func, void *
00771 data)
00772 {
00773 PluginWatch * watch = g_malloc (sizeof (PluginWatch));
00774 watch->func = func;
00775 watch->data = data;
00776 plugin->watches = g_list_prepend (plugin->watches, watch);
00777 }
00778
00779 void plugin_remove_watch (PluginHandle * plugin, PluginForEachFunc func, void *
00780 data)
00781 {
00782 for (GList * node = plugin->watches; node != NULL; )
00783 {
00784 GList * next = node->next;
00785 PluginWatch * watch = node->data;
00786
00787 if (watch->func == func && watch->data == data)
00788 {
00789 g_free (watch);
00790 plugin->watches = g_list_delete_link (plugin->watches, node);
00791 }
00792
00793 node = next;
00794 }
00795 }
00796
00797
00798 typedef struct {
00799 const gchar * scheme;
00800 PluginHandle * plugin;
00801 } TransportPluginForSchemeState;
00802
00803 static gboolean transport_plugin_for_scheme_cb (PluginHandle * plugin,
00804 TransportPluginForSchemeState * state)
00805 {
00806 if (! g_list_find_custom (plugin->u.t.schemes, state->scheme, (GCompareFunc)
00807 strcasecmp))
00808 return TRUE;
00809
00810 state->plugin = plugin;
00811 return FALSE;
00812 }
00813
00814 PluginHandle * transport_plugin_for_scheme (const gchar * scheme)
00815 {
00816 TransportPluginForSchemeState state = {scheme, NULL};
00817 plugin_for_enabled (PLUGIN_TYPE_TRANSPORT, (PluginForEachFunc)
00818 transport_plugin_for_scheme_cb, & state);
00819 return state.plugin;
00820 }
00821
00822 typedef struct {
00823 const gchar * ext;
00824 PluginHandle * plugin;
00825 } PlaylistPluginForExtState;
00826
00827 static gboolean playlist_plugin_for_ext_cb (PluginHandle * plugin,
00828 PlaylistPluginForExtState * state)
00829 {
00830 if (! g_list_find_custom (plugin->u.p.exts, state->ext, (GCompareFunc)
00831 strcasecmp))
00832 return TRUE;
00833
00834 state->plugin = plugin;
00835 return FALSE;
00836 }
00837
00838 PluginHandle * playlist_plugin_for_extension (const gchar * extension)
00839 {
00840 PlaylistPluginForExtState state = {extension, NULL};
00841 plugin_for_enabled (PLUGIN_TYPE_PLAYLIST, (PluginForEachFunc)
00842 playlist_plugin_for_ext_cb, & state);
00843 return state.plugin;
00844 }
00845
00846 typedef struct {
00847 gint key;
00848 const gchar * value;
00849 PluginForEachFunc func;
00850 void * data;
00851 } InputPluginForKeyState;
00852
00853 static gboolean input_plugin_for_key_cb (PluginHandle * plugin,
00854 InputPluginForKeyState * state)
00855 {
00856 if (g_list_find_custom (plugin->u.i.keys[state->key], state->value,
00857 (GCompareFunc) strcasecmp) == NULL)
00858 return TRUE;
00859
00860 return state->func (plugin, state->data);
00861 }
00862
00863 void input_plugin_for_key (gint key, const gchar * value, PluginForEachFunc
00864 func, void * data)
00865 {
00866 InputPluginForKeyState state = {key, value, func, data};
00867 plugin_for_enabled (PLUGIN_TYPE_INPUT, (PluginForEachFunc)
00868 input_plugin_for_key_cb, & state);
00869 }
00870
00871 static void input_plugin_add_key (InputPlugin * header, gint key, const gchar *
00872 value)
00873 {
00874 PluginHandle * plugin = plugin_by_header (header);
00875 g_return_if_fail (plugin != NULL);
00876 plugin->u.i.keys[key] = g_list_prepend (plugin->u.i.keys[key], g_strdup
00877 (value));
00878 }
00879
00880 void uri_set_plugin (const gchar * scheme, InputPlugin * header)
00881 {
00882 input_plugin_add_key (header, INPUT_KEY_SCHEME, scheme);
00883 }
00884
00885 void mime_set_plugin (const gchar * mime, InputPlugin * header)
00886 {
00887 input_plugin_add_key (header, INPUT_KEY_MIME, mime);
00888 }
00889
00890 gboolean input_plugin_has_images (PluginHandle * plugin)
00891 {
00892 g_return_val_if_fail (plugin->type == PLUGIN_TYPE_INPUT, FALSE);
00893 return plugin->u.i.has_images;
00894 }
00895
00896 gboolean input_plugin_has_subtunes (PluginHandle * plugin)
00897 {
00898 g_return_val_if_fail (plugin->type == PLUGIN_TYPE_INPUT, FALSE);
00899 return plugin->u.i.has_subtunes;
00900 }
00901
00902 gboolean input_plugin_can_write_tuple (PluginHandle * plugin)
00903 {
00904 g_return_val_if_fail (plugin->type == PLUGIN_TYPE_INPUT, FALSE);
00905 return plugin->u.i.can_write_tuple;
00906 }
00907
00908 gboolean input_plugin_has_infowin (PluginHandle * plugin)
00909 {
00910 g_return_val_if_fail (plugin->type == PLUGIN_TYPE_INPUT, FALSE);
00911 return plugin->u.i.has_infowin;
00912 }