Audacious
$Id:Doxyfile42802007-03-2104:39:00Znenolod$
|
00001 /* 00002 * plugin-registry.c 00003 * Copyright 2009-2011 John Lindgren 00004 * 00005 * This file is part of Audacious. 00006 * 00007 * Audacious is free software: you can redistribute it and/or modify it under 00008 * the terms of the GNU General Public License as published by the Free Software 00009 * Foundation, version 2 or version 3 of the License. 00010 * 00011 * Audacious is distributed in the hope that it will be useful, but WITHOUT ANY 00012 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 00013 * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 00014 * 00015 * You should have received a copy of the GNU General Public License along with 00016 * Audacious. If not, see <http://www.gnu.org/licenses/>. 00017 * 00018 * The Audacious team does not consider modular code linking to Audacious or 00019 * using our public API to be a derived work. 00020 */ 00021 00022 /* While the registry is being built (during early startup) or destroyed (during 00023 * late shutdown), the registry_locked flag will be set. Once this flag is 00024 * cleared, the registry will not be modified and can be read by concurrent 00025 * threads. The one change that can happen during this time is that a plugin is 00026 * loaded; hence the mutex must be locked before checking that a plugin is 00027 * loaded and while loading it. */ 00028 00029 #include <glib.h> 00030 #include <pthread.h> 00031 #include <stdio.h> 00032 #include <string.h> 00033 00034 #include <libaudcore/audstrings.h> 00035 00036 #include "debug.h" 00037 #include "interface.h" 00038 #include "misc.h" 00039 #include "plugin.h" 00040 #include "plugins.h" 00041 #include "util.h" 00042 00043 #define FILENAME "plugin-registry" 00044 #define FORMAT 6 00045 00046 typedef struct { 00047 GList * schemes; 00048 } TransportPluginData; 00049 00050 typedef struct { 00051 GList * exts; 00052 } PlaylistPluginData; 00053 00054 typedef struct { 00055 GList * keys[INPUT_KEYS]; 00056 bool_t has_images, has_subtunes, can_write_tuple, has_infowin; 00057 } InputPluginData; 00058 00059 struct PluginHandle { 00060 char * path; 00061 bool_t confirmed, loaded; 00062 int timestamp, type; 00063 Plugin * header; 00064 char * name; 00065 int priority; 00066 bool_t has_about, has_configure, enabled; 00067 GList * watches; 00068 00069 union { 00070 TransportPluginData t; 00071 PlaylistPluginData p; 00072 InputPluginData i; 00073 } u; 00074 }; 00075 00076 typedef struct { 00077 PluginForEachFunc func; 00078 void * data; 00079 } PluginWatch; 00080 00081 static const char * plugin_type_names[] = { 00082 [PLUGIN_TYPE_TRANSPORT] = "transport", 00083 [PLUGIN_TYPE_PLAYLIST] = "playlist", 00084 [PLUGIN_TYPE_INPUT] = "input", 00085 [PLUGIN_TYPE_EFFECT] = "effect", 00086 [PLUGIN_TYPE_OUTPUT] = "output", 00087 [PLUGIN_TYPE_VIS] = "vis", 00088 [PLUGIN_TYPE_GENERAL] = "general", 00089 [PLUGIN_TYPE_IFACE] = "iface"}; 00090 00091 static const char * input_key_names[] = { 00092 [INPUT_KEY_SCHEME] = "scheme", 00093 [INPUT_KEY_EXTENSION] = "ext", 00094 [INPUT_KEY_MIME] = "mime"}; 00095 00096 static GList * plugin_list = NULL; 00097 static bool_t registry_locked = TRUE; 00098 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 00099 00100 static PluginHandle * plugin_new (char * path, bool_t confirmed, bool_t 00101 loaded, int timestamp, int type, Plugin * header) 00102 { 00103 PluginHandle * plugin = g_malloc (sizeof (PluginHandle)); 00104 00105 plugin->path = path; 00106 plugin->confirmed = confirmed; 00107 plugin->loaded = loaded; 00108 plugin->timestamp = timestamp; 00109 plugin->type = type; 00110 plugin->header = header; 00111 plugin->name = NULL; 00112 plugin->priority = 0; 00113 plugin->has_about = FALSE; 00114 plugin->has_configure = FALSE; 00115 plugin->enabled = FALSE; 00116 plugin->watches = NULL; 00117 00118 if (type == PLUGIN_TYPE_TRANSPORT) 00119 { 00120 plugin->enabled = TRUE; 00121 plugin->u.t.schemes = NULL; 00122 } 00123 else if (type == PLUGIN_TYPE_PLAYLIST) 00124 { 00125 plugin->enabled = TRUE; 00126 plugin->u.p.exts = NULL; 00127 } 00128 else if (type == PLUGIN_TYPE_INPUT) 00129 { 00130 plugin->enabled = TRUE; 00131 memset (plugin->u.i.keys, 0, sizeof plugin->u.i.keys); 00132 plugin->u.i.has_images = FALSE; 00133 plugin->u.i.has_subtunes = FALSE; 00134 plugin->u.i.can_write_tuple = FALSE; 00135 plugin->u.i.has_infowin = FALSE; 00136 } 00137 00138 plugin_list = g_list_prepend (plugin_list, plugin); 00139 return plugin; 00140 } 00141 00142 static void plugin_free (PluginHandle * plugin) 00143 { 00144 plugin_list = g_list_remove (plugin_list, plugin); 00145 00146 g_list_foreach (plugin->watches, (GFunc) g_free, NULL); 00147 g_list_free (plugin->watches); 00148 00149 if (plugin->type == PLUGIN_TYPE_TRANSPORT) 00150 { 00151 g_list_foreach (plugin->u.t.schemes, (GFunc) g_free, NULL); 00152 g_list_free (plugin->u.t.schemes); 00153 } 00154 else if (plugin->type == PLUGIN_TYPE_PLAYLIST) 00155 { 00156 g_list_foreach (plugin->u.p.exts, (GFunc) g_free, NULL); 00157 g_list_free (plugin->u.p.exts); 00158 } 00159 else if (plugin->type == PLUGIN_TYPE_INPUT) 00160 { 00161 for (int key = 0; key < INPUT_KEYS; key ++) 00162 { 00163 g_list_foreach (plugin->u.i.keys[key], (GFunc) g_free, NULL); 00164 g_list_free (plugin->u.i.keys[key]); 00165 } 00166 } 00167 00168 g_free (plugin->path); 00169 g_free (plugin->name); 00170 g_free (plugin); 00171 } 00172 00173 static FILE * open_registry_file (const char * mode) 00174 { 00175 char * path = g_strdup_printf ("%s/" FILENAME, get_path (AUD_PATH_USER_DIR)); 00176 FILE * file = fopen (path, mode); 00177 g_free (path); 00178 return file; 00179 } 00180 00181 static void transport_plugin_save (PluginHandle * plugin, FILE * handle) 00182 { 00183 for (GList * node = plugin->u.t.schemes; node; node = node->next) 00184 fprintf (handle, "scheme %s\n", (const char *) node->data); 00185 } 00186 00187 static void playlist_plugin_save (PluginHandle * plugin, FILE * handle) 00188 { 00189 for (GList * node = plugin->u.p.exts; node; node = node->next) 00190 fprintf (handle, "ext %s\n", (const char *) node->data); 00191 } 00192 00193 static void input_plugin_save (PluginHandle * plugin, FILE * handle) 00194 { 00195 for (int key = 0; key < INPUT_KEYS; key ++) 00196 { 00197 for (GList * node = plugin->u.i.keys[key]; node; node = node->next) 00198 fprintf (handle, "%s %s\n", input_key_names[key], (const char *) 00199 node->data); 00200 } 00201 00202 fprintf (handle, "images %d\n", plugin->u.i.has_images); 00203 fprintf (handle, "subtunes %d\n", plugin->u.i.has_subtunes); 00204 fprintf (handle, "writes %d\n", plugin->u.i.can_write_tuple); 00205 fprintf (handle, "infowin %d\n", plugin->u.i.has_infowin); 00206 } 00207 00208 static void plugin_save (PluginHandle * plugin, FILE * handle) 00209 { 00210 fprintf (handle, "%s %s\n", plugin_type_names[plugin->type], plugin->path); 00211 fprintf (handle, "stamp %d\n", plugin->timestamp); 00212 fprintf (handle, "name %s\n", plugin->name); 00213 fprintf (handle, "priority %d\n", plugin->priority); 00214 fprintf (handle, "about %d\n", plugin->has_about); 00215 fprintf (handle, "config %d\n", plugin->has_configure); 00216 fprintf (handle, "enabled %d\n", plugin->enabled); 00217 00218 if (plugin->type == PLUGIN_TYPE_TRANSPORT) 00219 transport_plugin_save (plugin, handle); 00220 else if (plugin->type == PLUGIN_TYPE_PLAYLIST) 00221 playlist_plugin_save (plugin, handle); 00222 else if (plugin->type == PLUGIN_TYPE_INPUT) 00223 input_plugin_save (plugin, handle); 00224 } 00225 00226 void plugin_registry_save (void) 00227 { 00228 FILE * handle = open_registry_file ("w"); 00229 g_return_if_fail (handle); 00230 00231 fprintf (handle, "format %d\n", FORMAT); 00232 00233 g_list_foreach (plugin_list, (GFunc) plugin_save, handle); 00234 fclose (handle); 00235 00236 g_list_foreach (plugin_list, (GFunc) plugin_free, NULL); 00237 registry_locked = TRUE; 00238 } 00239 00240 static char parse_key[512]; 00241 static char * parse_value; 00242 00243 static void parse_next (FILE * handle) 00244 { 00245 parse_value = NULL; 00246 00247 if (! fgets (parse_key, sizeof parse_key, handle)) 00248 return; 00249 00250 char * space = strchr (parse_key, ' '); 00251 if (! space) 00252 return; 00253 00254 * space = 0; 00255 parse_value = space + 1; 00256 00257 char * newline = strchr (parse_value, '\n'); 00258 if (newline) 00259 * newline = 0; 00260 } 00261 00262 static bool_t parse_integer (const char * key, int * value) 00263 { 00264 return (parse_value && ! strcmp (parse_key, key) && sscanf (parse_value, 00265 "%d", value) == 1); 00266 } 00267 00268 static char * parse_string (const char * key) 00269 { 00270 return (parse_value && ! strcmp (parse_key, key)) ? g_strdup (parse_value) : 00271 NULL; 00272 } 00273 00274 static void transport_plugin_parse (PluginHandle * plugin, FILE * handle) 00275 { 00276 char * value; 00277 while ((value = parse_string ("scheme"))) 00278 { 00279 plugin->u.t.schemes = g_list_prepend (plugin->u.t.schemes, value); 00280 parse_next (handle); 00281 } 00282 } 00283 00284 static void playlist_plugin_parse (PluginHandle * plugin, FILE * handle) 00285 { 00286 char * value; 00287 while ((value = parse_string ("ext"))) 00288 { 00289 plugin->u.p.exts = g_list_prepend (plugin->u.p.exts, value); 00290 parse_next (handle); 00291 } 00292 } 00293 00294 static void input_plugin_parse (PluginHandle * plugin, FILE * handle) 00295 { 00296 for (int key = 0; key < INPUT_KEYS; key ++) 00297 { 00298 char * value; 00299 while ((value = parse_string (input_key_names[key]))) 00300 { 00301 plugin->u.i.keys[key] = g_list_prepend (plugin->u.i.keys[key], 00302 value); 00303 parse_next (handle); 00304 } 00305 } 00306 00307 if (parse_integer ("images", & plugin->u.i.has_images)) 00308 parse_next (handle); 00309 if (parse_integer ("subtunes", & plugin->u.i.has_subtunes)) 00310 parse_next (handle); 00311 if (parse_integer ("writes", & plugin->u.i.can_write_tuple)) 00312 parse_next (handle); 00313 if (parse_integer ("infowin", & plugin->u.i.has_infowin)) 00314 parse_next (handle); 00315 } 00316 00317 static bool_t plugin_parse (FILE * handle) 00318 { 00319 char * path = NULL; 00320 00321 int type; 00322 for (type = 0; type < PLUGIN_TYPES; type ++) 00323 { 00324 if ((path = parse_string (plugin_type_names[type]))) 00325 goto FOUND; 00326 } 00327 00328 return FALSE; 00329 00330 FOUND: 00331 parse_next (handle); 00332 00333 int timestamp; 00334 if (! parse_integer ("stamp", & timestamp)) 00335 { 00336 g_free (path); 00337 return FALSE; 00338 } 00339 00340 PluginHandle * plugin = plugin_new (path, FALSE, FALSE, timestamp, type, 00341 NULL); 00342 parse_next (handle); 00343 00344 if ((plugin->name = parse_string ("name"))) 00345 parse_next (handle); 00346 if (parse_integer ("priority", & plugin->priority)) 00347 parse_next (handle); 00348 if (parse_integer ("about", & plugin->has_about)) 00349 parse_next (handle); 00350 if (parse_integer ("config", & plugin->has_configure)) 00351 parse_next (handle); 00352 if (parse_integer ("enabled", & plugin->enabled)) 00353 parse_next (handle); 00354 00355 if (type == PLUGIN_TYPE_TRANSPORT) 00356 transport_plugin_parse (plugin, handle); 00357 else if (type == PLUGIN_TYPE_PLAYLIST) 00358 playlist_plugin_parse (plugin, handle); 00359 else if (type == PLUGIN_TYPE_INPUT) 00360 input_plugin_parse (plugin, handle); 00361 00362 return TRUE; 00363 } 00364 00365 void plugin_registry_load (void) 00366 { 00367 FILE * handle = open_registry_file ("r"); 00368 if (! handle) 00369 goto UNLOCK; 00370 00371 parse_next (handle); 00372 00373 int format; 00374 if (! parse_integer ("format", & format) || format != FORMAT) 00375 goto ERR; 00376 00377 parse_next (handle); 00378 00379 while (plugin_parse (handle)) 00380 ; 00381 00382 ERR: 00383 fclose (handle); 00384 UNLOCK: 00385 registry_locked = FALSE; 00386 } 00387 00388 static void plugin_prune (PluginHandle * plugin) 00389 { 00390 if (plugin->confirmed) 00391 return; 00392 00393 AUDDBG ("Plugin not found: %s\n", plugin->path); 00394 plugin_free (plugin); 00395 } 00396 00397 int plugin_compare (PluginHandle * a, PluginHandle * b) 00398 { 00399 if (a->type < b->type) 00400 return -1; 00401 if (a->type > b->type) 00402 return 1; 00403 if (a->priority < b->priority) 00404 return -1; 00405 if (a->priority > b->priority) 00406 return 1; 00407 00408 int diff; 00409 if ((diff = string_compare (a->name, b->name))) 00410 return diff; 00411 00412 return string_compare (a->path, b->path); 00413 } 00414 00415 void plugin_registry_prune (void) 00416 { 00417 g_list_foreach (plugin_list, (GFunc) plugin_prune, NULL); 00418 plugin_list = g_list_sort (plugin_list, (GCompareFunc) plugin_compare); 00419 registry_locked = TRUE; 00420 } 00421 00422 static int plugin_lookup_cb (PluginHandle * plugin, const char * path) 00423 { 00424 return strcmp (plugin->path, path); 00425 } 00426 00427 PluginHandle * plugin_lookup (const char * path) 00428 { 00429 GList * node = g_list_find_custom (plugin_list, path, (GCompareFunc) 00430 plugin_lookup_cb); 00431 return node ? node->data : NULL; 00432 } 00433 00434 static int plugin_lookup_basename_cb (PluginHandle * plugin, const char * basename) 00435 { 00436 char * test = g_path_get_basename (plugin->path); 00437 00438 char * dot = strrchr (test, '.'); 00439 if (dot) 00440 * dot = 0; 00441 00442 int ret = strcmp (test, basename); 00443 00444 g_free (test); 00445 return ret; 00446 } 00447 00448 /* Note: If there are multiple plugins with the same basename, this returns only 00449 * one of them. So give different plugins different basenames. --jlindgren */ 00450 PluginHandle * plugin_lookup_basename (const char * basename) 00451 { 00452 GList * node = g_list_find_custom (plugin_list, basename, (GCompareFunc) 00453 plugin_lookup_basename_cb); 00454 return node ? node->data : NULL; 00455 } 00456 00457 void plugin_register (const char * path) 00458 { 00459 PluginHandle * plugin = plugin_lookup (path); 00460 if (! plugin) 00461 { 00462 AUDDBG ("New plugin: %s\n", path); 00463 plugin_load (path); 00464 return; 00465 } 00466 00467 int timestamp = file_get_mtime (path); 00468 g_return_if_fail (timestamp >= 0); 00469 00470 AUDDBG ("Register plugin: %s\n", path); 00471 plugin->confirmed = TRUE; 00472 00473 if (plugin->timestamp == timestamp) 00474 return; 00475 00476 AUDDBG ("Rescan plugin: %s\n", path); 00477 plugin->timestamp = timestamp; 00478 plugin_load (path); 00479 } 00480 00481 void plugin_register_loaded (const char * path, Plugin * header) 00482 { 00483 AUDDBG ("Loaded plugin: %s\n", path); 00484 PluginHandle * plugin = plugin_lookup (path); 00485 bool_t new = FALSE; 00486 00487 if (plugin) 00488 { 00489 g_return_if_fail (plugin->type == header->type); 00490 00491 plugin->loaded = TRUE; 00492 plugin->header = header; 00493 00494 if (registry_locked) 00495 return; 00496 } 00497 else 00498 { 00499 g_return_if_fail (! registry_locked); 00500 00501 int timestamp = file_get_mtime (path); 00502 g_return_if_fail (timestamp >= 0); 00503 00504 plugin = plugin_new (g_strdup (path), TRUE, TRUE, timestamp, 00505 header->type, header); 00506 new = TRUE; 00507 } 00508 00509 g_free (plugin->name); 00510 plugin->name = g_strdup (header->name); 00511 plugin->has_about = PLUGIN_HAS_FUNC (header, about); 00512 plugin->has_configure = PLUGIN_HAS_FUNC (header, configure) || 00513 PLUGIN_HAS_FUNC (header, settings); 00514 00515 if (header->type == PLUGIN_TYPE_TRANSPORT) 00516 { 00517 TransportPlugin * tp = (TransportPlugin *) header; 00518 for (int i = 0; tp->schemes[i]; i ++) 00519 plugin->u.t.schemes = g_list_prepend (plugin->u.t.schemes, g_strdup 00520 (tp->schemes[i])); 00521 } 00522 else if (header->type == PLUGIN_TYPE_PLAYLIST) 00523 { 00524 PlaylistPlugin * pp = (PlaylistPlugin *) header; 00525 for (int i = 0; pp->extensions[i]; i ++) 00526 plugin->u.p.exts = g_list_prepend (plugin->u.p.exts, g_strdup 00527 (pp->extensions[i])); 00528 } 00529 else if (header->type == PLUGIN_TYPE_INPUT) 00530 { 00531 InputPlugin * ip = (InputPlugin *) header; 00532 plugin->priority = ip->priority; 00533 00534 for (int key = 0; key < INPUT_KEYS; key ++) 00535 { 00536 g_list_foreach (plugin->u.i.keys[key], (GFunc) g_free, NULL); 00537 g_list_free (plugin->u.i.keys[key]); 00538 plugin->u.i.keys[key] = NULL; 00539 } 00540 00541 if (PLUGIN_HAS_FUNC (ip, extensions)) 00542 { 00543 for (int i = 0; ip->extensions[i]; i ++) 00544 plugin->u.i.keys[INPUT_KEY_EXTENSION] = g_list_prepend 00545 (plugin->u.i.keys[INPUT_KEY_EXTENSION], g_strdup 00546 (ip->extensions[i])); 00547 } 00548 00549 if (PLUGIN_HAS_FUNC (ip, mimes)) 00550 { 00551 for (int i = 0; ip->mimes[i]; i ++) 00552 plugin->u.i.keys[INPUT_KEY_MIME] = g_list_prepend 00553 (plugin->u.i.keys[INPUT_KEY_MIME], g_strdup (ip->mimes[i])); 00554 } 00555 00556 if (PLUGIN_HAS_FUNC (ip, schemes)) 00557 { 00558 for (int i = 0; ip->schemes[i]; i ++) 00559 plugin->u.i.keys[INPUT_KEY_SCHEME] = g_list_prepend 00560 (plugin->u.i.keys[INPUT_KEY_SCHEME], g_strdup (ip->schemes[i])); 00561 } 00562 00563 plugin->u.i.has_images = PLUGIN_HAS_FUNC (ip, get_song_image); 00564 plugin->u.i.has_subtunes = ip->have_subtune; 00565 plugin->u.i.can_write_tuple = PLUGIN_HAS_FUNC (ip, update_song_tuple); 00566 plugin->u.i.has_infowin = PLUGIN_HAS_FUNC (ip, file_info_box); 00567 } 00568 else if (header->type == PLUGIN_TYPE_OUTPUT) 00569 { 00570 OutputPlugin * op = (OutputPlugin *) header; 00571 plugin->priority = 10 - op->probe_priority; 00572 } 00573 else if (header->type == PLUGIN_TYPE_EFFECT) 00574 { 00575 EffectPlugin * ep = (EffectPlugin *) header; 00576 plugin->priority = ep->order; 00577 } 00578 else if (header->type == PLUGIN_TYPE_GENERAL) 00579 { 00580 GeneralPlugin * gp = (GeneralPlugin *) header; 00581 if (new) 00582 plugin->enabled = gp->enabled_by_default; 00583 } 00584 } 00585 00586 int plugin_get_type (PluginHandle * plugin) 00587 { 00588 return plugin->type; 00589 } 00590 00591 const char * plugin_get_filename (PluginHandle * plugin) 00592 { 00593 return plugin->path; 00594 } 00595 00596 const void * plugin_get_header (PluginHandle * plugin) 00597 { 00598 pthread_mutex_lock (& mutex); 00599 00600 if (! plugin->loaded) 00601 { 00602 plugin_load (plugin->path); 00603 plugin->loaded = TRUE; 00604 } 00605 00606 pthread_mutex_unlock (& mutex); 00607 return plugin->header; 00608 } 00609 00610 static int plugin_by_header_cb (PluginHandle * plugin, const void * header) 00611 { 00612 return (plugin->header == header) ? 0 : -1; 00613 } 00614 00615 PluginHandle * plugin_by_header (const void * header) 00616 { 00617 GList * node = g_list_find_custom (plugin_list, header, (GCompareFunc) 00618 plugin_by_header_cb); 00619 return node ? node->data : NULL; 00620 } 00621 00622 void plugin_for_each (int type, PluginForEachFunc func, void * data) 00623 { 00624 for (GList * node = plugin_list; node; node = node->next) 00625 { 00626 if (((PluginHandle *) node->data)->type != type) 00627 continue; 00628 if (! func (node->data, data)) 00629 break; 00630 } 00631 } 00632 00633 const char * plugin_get_name (PluginHandle * plugin) 00634 { 00635 return plugin->name; 00636 } 00637 00638 bool_t plugin_has_about (PluginHandle * plugin) 00639 { 00640 return plugin->has_about; 00641 } 00642 00643 bool_t plugin_has_configure (PluginHandle * plugin) 00644 { 00645 return plugin->has_configure; 00646 } 00647 00648 bool_t plugin_get_enabled (PluginHandle * plugin) 00649 { 00650 return plugin->enabled; 00651 } 00652 00653 static void plugin_call_watches (PluginHandle * plugin) 00654 { 00655 for (GList * node = plugin->watches; node; ) 00656 { 00657 GList * next = node->next; 00658 PluginWatch * watch = node->data; 00659 00660 if (! watch->func (plugin, watch->data)) 00661 { 00662 g_free (watch); 00663 plugin->watches = g_list_delete_link (plugin->watches, node); 00664 } 00665 00666 node = next; 00667 } 00668 } 00669 00670 void plugin_set_enabled (PluginHandle * plugin, bool_t enabled) 00671 { 00672 plugin->enabled = enabled; 00673 plugin_call_watches (plugin); 00674 } 00675 00676 typedef struct { 00677 PluginForEachFunc func; 00678 void * data; 00679 } PluginForEnabledState; 00680 00681 static bool_t plugin_for_enabled_cb (PluginHandle * plugin, 00682 PluginForEnabledState * state) 00683 { 00684 if (! plugin->enabled) 00685 return TRUE; 00686 return state->func (plugin, state->data); 00687 } 00688 00689 void plugin_for_enabled (int type, PluginForEachFunc func, void * data) 00690 { 00691 PluginForEnabledState state = {func, data}; 00692 plugin_for_each (type, (PluginForEachFunc) plugin_for_enabled_cb, & state); 00693 } 00694 00695 void plugin_add_watch (PluginHandle * plugin, PluginForEachFunc func, void * 00696 data) 00697 { 00698 PluginWatch * watch = g_malloc (sizeof (PluginWatch)); 00699 watch->func = func; 00700 watch->data = data; 00701 plugin->watches = g_list_prepend (plugin->watches, watch); 00702 } 00703 00704 void plugin_remove_watch (PluginHandle * plugin, PluginForEachFunc func, void * 00705 data) 00706 { 00707 for (GList * node = plugin->watches; node; ) 00708 { 00709 GList * next = node->next; 00710 PluginWatch * watch = node->data; 00711 00712 if (watch->func == func && watch->data == data) 00713 { 00714 g_free (watch); 00715 plugin->watches = g_list_delete_link (plugin->watches, node); 00716 } 00717 00718 node = next; 00719 } 00720 } 00721 00722 typedef struct { 00723 const char * scheme; 00724 PluginHandle * plugin; 00725 } TransportPluginForSchemeState; 00726 00727 static bool_t transport_plugin_for_scheme_cb (PluginHandle * plugin, 00728 TransportPluginForSchemeState * state) 00729 { 00730 if (! g_list_find_custom (plugin->u.t.schemes, state->scheme, 00731 (GCompareFunc) g_ascii_strcasecmp)) 00732 return TRUE; 00733 00734 state->plugin = plugin; 00735 return FALSE; 00736 } 00737 00738 PluginHandle * transport_plugin_for_scheme (const char * scheme) 00739 { 00740 TransportPluginForSchemeState state = {scheme, NULL}; 00741 plugin_for_enabled (PLUGIN_TYPE_TRANSPORT, (PluginForEachFunc) 00742 transport_plugin_for_scheme_cb, & state); 00743 return state.plugin; 00744 } 00745 00746 typedef struct { 00747 const char * ext; 00748 PluginHandle * plugin; 00749 } PlaylistPluginForExtState; 00750 00751 static bool_t playlist_plugin_for_ext_cb (PluginHandle * plugin, 00752 PlaylistPluginForExtState * state) 00753 { 00754 if (! g_list_find_custom (plugin->u.p.exts, state->ext, 00755 (GCompareFunc) g_ascii_strcasecmp)) 00756 return TRUE; 00757 00758 state->plugin = plugin; 00759 return FALSE; 00760 } 00761 00762 PluginHandle * playlist_plugin_for_extension (const char * extension) 00763 { 00764 PlaylistPluginForExtState state = {extension, NULL}; 00765 plugin_for_enabled (PLUGIN_TYPE_PLAYLIST, (PluginForEachFunc) 00766 playlist_plugin_for_ext_cb, & state); 00767 return state.plugin; 00768 } 00769 00770 typedef struct { 00771 int key; 00772 const char * value; 00773 PluginForEachFunc func; 00774 void * data; 00775 } InputPluginForKeyState; 00776 00777 static bool_t input_plugin_for_key_cb (PluginHandle * plugin, 00778 InputPluginForKeyState * state) 00779 { 00780 if (! g_list_find_custom (plugin->u.i.keys[state->key], state->value, 00781 (GCompareFunc) g_ascii_strcasecmp)) 00782 return TRUE; 00783 00784 return state->func (plugin, state->data); 00785 } 00786 00787 void input_plugin_for_key (int key, const char * value, PluginForEachFunc 00788 func, void * data) 00789 { 00790 InputPluginForKeyState state = {key, value, func, data}; 00791 plugin_for_enabled (PLUGIN_TYPE_INPUT, (PluginForEachFunc) 00792 input_plugin_for_key_cb, & state); 00793 } 00794 00795 bool_t input_plugin_has_images (PluginHandle * plugin) 00796 { 00797 g_return_val_if_fail (plugin->type == PLUGIN_TYPE_INPUT, FALSE); 00798 return plugin->u.i.has_images; 00799 } 00800 00801 bool_t input_plugin_has_subtunes (PluginHandle * plugin) 00802 { 00803 g_return_val_if_fail (plugin->type == PLUGIN_TYPE_INPUT, FALSE); 00804 return plugin->u.i.has_subtunes; 00805 } 00806 00807 bool_t input_plugin_can_write_tuple (PluginHandle * plugin) 00808 { 00809 g_return_val_if_fail (plugin->type == PLUGIN_TYPE_INPUT, FALSE); 00810 return plugin->u.i.can_write_tuple; 00811 } 00812 00813 bool_t input_plugin_has_infowin (PluginHandle * plugin) 00814 { 00815 g_return_val_if_fail (plugin->type == PLUGIN_TYPE_INPUT, FALSE); 00816 return plugin->u.i.has_infowin; 00817 }