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 <limits.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 gboolean has_images, has_subtunes, can_write_tuple, has_infowin; 00057 } InputPluginData; 00058 00059 struct PluginHandle { 00060 gchar * path; 00061 gboolean confirmed, loaded; 00062 gint timestamp, type; 00063 Plugin * header; 00064 gchar * name; 00065 gint priority; 00066 gboolean 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 gchar * 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 gchar * 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 gboolean registry_locked = TRUE; 00098 static GStaticMutex mutex = G_STATIC_MUTEX_INIT; 00099 00100 static PluginHandle * plugin_new (gchar * path, gboolean confirmed, gboolean 00101 loaded, gint timestamp, gint 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 (gint 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 gchar * mode) 00174 { 00175 gchar path[PATH_MAX]; 00176 snprintf (path, sizeof path, "%s/" FILENAME, get_path (AUD_PATH_USER_DIR)); 00177 return fopen (path, mode); 00178 } 00179 00180 static void transport_plugin_save (PluginHandle * plugin, FILE * handle) 00181 { 00182 for (GList * node = plugin->u.t.schemes; node; node = node->next) 00183 fprintf (handle, "scheme %s\n", (const gchar *) node->data); 00184 } 00185 00186 static void playlist_plugin_save (PluginHandle * plugin, FILE * handle) 00187 { 00188 for (GList * node = plugin->u.p.exts; node; node = node->next) 00189 fprintf (handle, "ext %s\n", (const gchar *) node->data); 00190 } 00191 00192 static void input_plugin_save (PluginHandle * plugin, FILE * handle) 00193 { 00194 for (gint key = 0; key < INPUT_KEYS; key ++) 00195 { 00196 for (GList * node = plugin->u.i.keys[key]; node; node = node->next) 00197 fprintf (handle, "%s %s\n", input_key_names[key], (const gchar *) 00198 node->data); 00199 } 00200 00201 fprintf (handle, "images %d\n", plugin->u.i.has_images); 00202 fprintf (handle, "subtunes %d\n", plugin->u.i.has_subtunes); 00203 fprintf (handle, "writes %d\n", plugin->u.i.can_write_tuple); 00204 fprintf (handle, "infowin %d\n", plugin->u.i.has_infowin); 00205 } 00206 00207 static void plugin_save (PluginHandle * plugin, FILE * handle) 00208 { 00209 fprintf (handle, "%s %s\n", plugin_type_names[plugin->type], plugin->path); 00210 fprintf (handle, "stamp %d\n", plugin->timestamp); 00211 fprintf (handle, "name %s\n", plugin->name); 00212 fprintf (handle, "priority %d\n", plugin->priority); 00213 fprintf (handle, "about %d\n", plugin->has_about); 00214 fprintf (handle, "config %d\n", plugin->has_configure); 00215 fprintf (handle, "enabled %d\n", plugin->enabled); 00216 00217 if (plugin->type == PLUGIN_TYPE_TRANSPORT) 00218 transport_plugin_save (plugin, handle); 00219 else if (plugin->type == PLUGIN_TYPE_PLAYLIST) 00220 playlist_plugin_save (plugin, handle); 00221 else if (plugin->type == PLUGIN_TYPE_INPUT) 00222 input_plugin_save (plugin, handle); 00223 } 00224 00225 void plugin_registry_save (void) 00226 { 00227 FILE * handle = open_registry_file ("w"); 00228 g_return_if_fail (handle); 00229 00230 fprintf (handle, "format %d\n", FORMAT); 00231 00232 g_list_foreach (plugin_list, (GFunc) plugin_save, handle); 00233 fclose (handle); 00234 00235 g_list_foreach (plugin_list, (GFunc) plugin_free, NULL); 00236 registry_locked = TRUE; 00237 } 00238 00239 static gchar parse_key[512]; 00240 static gchar * parse_value; 00241 00242 static void parse_next (FILE * handle) 00243 { 00244 parse_value = NULL; 00245 00246 if (! fgets (parse_key, sizeof parse_key, handle)) 00247 return; 00248 00249 gchar * space = strchr (parse_key, ' '); 00250 if (! space) 00251 return; 00252 00253 * space = 0; 00254 parse_value = space + 1; 00255 00256 gchar * newline = strchr (parse_value, '\n'); 00257 if (newline) 00258 * newline = 0; 00259 } 00260 00261 static gboolean parse_integer (const gchar * key, gint * value) 00262 { 00263 return (parse_value && ! strcmp (parse_key, key) && sscanf (parse_value, 00264 "%d", value) == 1); 00265 } 00266 00267 static gchar * parse_string (const gchar * key) 00268 { 00269 return (parse_value && ! strcmp (parse_key, key)) ? g_strdup (parse_value) : 00270 NULL; 00271 } 00272 00273 static void transport_plugin_parse (PluginHandle * plugin, FILE * handle) 00274 { 00275 gchar * value; 00276 while ((value = parse_string ("scheme"))) 00277 { 00278 plugin->u.t.schemes = g_list_prepend (plugin->u.t.schemes, value); 00279 parse_next (handle); 00280 } 00281 } 00282 00283 static void playlist_plugin_parse (PluginHandle * plugin, FILE * handle) 00284 { 00285 gchar * value; 00286 while ((value = parse_string ("ext"))) 00287 { 00288 plugin->u.p.exts = g_list_prepend (plugin->u.p.exts, value); 00289 parse_next (handle); 00290 } 00291 } 00292 00293 static void input_plugin_parse (PluginHandle * plugin, FILE * handle) 00294 { 00295 for (gint key = 0; key < INPUT_KEYS; key ++) 00296 { 00297 gchar * value; 00298 while ((value = parse_string (input_key_names[key]))) 00299 { 00300 plugin->u.i.keys[key] = g_list_prepend (plugin->u.i.keys[key], 00301 value); 00302 parse_next (handle); 00303 } 00304 } 00305 00306 if (parse_integer ("images", & plugin->u.i.has_images)) 00307 parse_next (handle); 00308 if (parse_integer ("subtunes", & plugin->u.i.has_subtunes)) 00309 parse_next (handle); 00310 if (parse_integer ("writes", & plugin->u.i.can_write_tuple)) 00311 parse_next (handle); 00312 if (parse_integer ("infowin", & plugin->u.i.has_infowin)) 00313 parse_next (handle); 00314 } 00315 00316 static gboolean plugin_parse (FILE * handle) 00317 { 00318 gchar * path = NULL; 00319 00320 gint type; 00321 for (type = 0; type < PLUGIN_TYPES; type ++) 00322 { 00323 if ((path = parse_string (plugin_type_names[type]))) 00324 goto FOUND; 00325 } 00326 00327 return FALSE; 00328 00329 FOUND: 00330 parse_next (handle); 00331 00332 gint timestamp; 00333 if (! parse_integer ("stamp", & timestamp)) 00334 { 00335 g_free (path); 00336 return FALSE; 00337 } 00338 00339 PluginHandle * plugin = plugin_new (path, FALSE, FALSE, timestamp, type, 00340 NULL); 00341 parse_next (handle); 00342 00343 if ((plugin->name = parse_string ("name"))) 00344 parse_next (handle); 00345 if (parse_integer ("priority", & plugin->priority)) 00346 parse_next (handle); 00347 if (parse_integer ("about", & plugin->has_about)) 00348 parse_next (handle); 00349 if (parse_integer ("config", & plugin->has_configure)) 00350 parse_next (handle); 00351 if (parse_integer ("enabled", & plugin->enabled)) 00352 parse_next (handle); 00353 00354 if (type == PLUGIN_TYPE_TRANSPORT) 00355 transport_plugin_parse (plugin, handle); 00356 else if (type == PLUGIN_TYPE_PLAYLIST) 00357 playlist_plugin_parse (plugin, handle); 00358 else if (type == PLUGIN_TYPE_INPUT) 00359 input_plugin_parse (plugin, handle); 00360 00361 return TRUE; 00362 } 00363 00364 void plugin_registry_load (void) 00365 { 00366 FILE * handle = open_registry_file ("r"); 00367 if (! handle) 00368 goto UNLOCK; 00369 00370 parse_next (handle); 00371 00372 gint format; 00373 if (! parse_integer ("format", & format) || format != FORMAT) 00374 goto ERR; 00375 00376 parse_next (handle); 00377 00378 while (plugin_parse (handle)) 00379 ; 00380 00381 ERR: 00382 fclose (handle); 00383 UNLOCK: 00384 registry_locked = FALSE; 00385 } 00386 00387 static void plugin_prune (PluginHandle * plugin) 00388 { 00389 if (plugin->confirmed) 00390 return; 00391 00392 AUDDBG ("Plugin not found: %s\n", plugin->path); 00393 plugin_free (plugin); 00394 } 00395 00396 gint plugin_compare (PluginHandle * a, PluginHandle * b) 00397 { 00398 if (a->type < b->type) 00399 return -1; 00400 if (a->type > b->type) 00401 return 1; 00402 if (a->priority < b->priority) 00403 return -1; 00404 if (a->priority > b->priority) 00405 return 1; 00406 00407 gint diff; 00408 if ((diff = string_compare (a->name, b->name))) 00409 return diff; 00410 00411 return string_compare (a->path, b->path); 00412 } 00413 00414 void plugin_registry_prune (void) 00415 { 00416 g_list_foreach (plugin_list, (GFunc) plugin_prune, NULL); 00417 plugin_list = g_list_sort (plugin_list, (GCompareFunc) plugin_compare); 00418 registry_locked = TRUE; 00419 } 00420 00421 static gint plugin_lookup_cb (PluginHandle * plugin, const gchar * path) 00422 { 00423 return strcmp (plugin->path, path); 00424 } 00425 00426 PluginHandle * plugin_lookup (const gchar * path) 00427 { 00428 GList * node = g_list_find_custom (plugin_list, path, (GCompareFunc) 00429 plugin_lookup_cb); 00430 return node ? node->data : NULL; 00431 } 00432 00433 void plugin_register (const gchar * path) 00434 { 00435 PluginHandle * plugin = plugin_lookup (path); 00436 if (! plugin) 00437 { 00438 AUDDBG ("New plugin: %s\n", path); 00439 plugin_load (path); 00440 return; 00441 } 00442 00443 gint timestamp = file_get_mtime (path); 00444 g_return_if_fail (timestamp >= 0); 00445 00446 AUDDBG ("Register plugin: %s\n", path); 00447 plugin->confirmed = TRUE; 00448 00449 if (plugin->timestamp == timestamp) 00450 return; 00451 00452 AUDDBG ("Rescan plugin: %s\n", path); 00453 plugin->timestamp = timestamp; 00454 plugin_load (path); 00455 } 00456 00457 void plugin_register_loaded (const gchar * path, Plugin * header) 00458 { 00459 AUDDBG ("Loaded plugin: %s\n", path); 00460 PluginHandle * plugin = plugin_lookup (path); 00461 gboolean new = FALSE; 00462 00463 if (plugin) 00464 { 00465 g_return_if_fail (plugin->type == header->type); 00466 00467 plugin->loaded = TRUE; 00468 plugin->header = header; 00469 00470 if (registry_locked) 00471 return; 00472 } 00473 else 00474 { 00475 g_return_if_fail (! registry_locked); 00476 00477 gint timestamp = file_get_mtime (path); 00478 g_return_if_fail (timestamp >= 0); 00479 00480 plugin = plugin_new (g_strdup (path), TRUE, TRUE, timestamp, 00481 header->type, header); 00482 new = TRUE; 00483 } 00484 00485 g_free (plugin->name); 00486 plugin->name = g_strdup (header->name); 00487 plugin->has_about = PLUGIN_HAS_FUNC (header, about); 00488 plugin->has_configure = PLUGIN_HAS_FUNC (header, configure) || 00489 PLUGIN_HAS_FUNC (header, settings); 00490 00491 if (header->type == PLUGIN_TYPE_TRANSPORT) 00492 { 00493 TransportPlugin * tp = (TransportPlugin *) header; 00494 for (gint i = 0; tp->schemes[i]; i ++) 00495 plugin->u.t.schemes = g_list_prepend (plugin->u.t.schemes, g_strdup 00496 (tp->schemes[i])); 00497 } 00498 else if (header->type == PLUGIN_TYPE_PLAYLIST) 00499 { 00500 PlaylistPlugin * pp = (PlaylistPlugin *) header; 00501 for (gint i = 0; pp->extensions[i]; i ++) 00502 plugin->u.p.exts = g_list_prepend (plugin->u.p.exts, g_strdup 00503 (pp->extensions[i])); 00504 } 00505 else if (header->type == PLUGIN_TYPE_INPUT) 00506 { 00507 InputPlugin * ip = (InputPlugin *) header; 00508 plugin->priority = ip->priority; 00509 00510 for (gint key = 0; key < INPUT_KEYS; key ++) 00511 { 00512 g_list_foreach (plugin->u.i.keys[key], (GFunc) g_free, NULL); 00513 g_list_free (plugin->u.i.keys[key]); 00514 plugin->u.i.keys[key] = NULL; 00515 } 00516 00517 if (PLUGIN_HAS_FUNC (ip, extensions)) 00518 { 00519 for (gint i = 0; ip->extensions[i]; i ++) 00520 plugin->u.i.keys[INPUT_KEY_EXTENSION] = g_list_prepend 00521 (plugin->u.i.keys[INPUT_KEY_EXTENSION], g_strdup 00522 (ip->extensions[i])); 00523 } 00524 00525 if (PLUGIN_HAS_FUNC (ip, mimes)) 00526 { 00527 for (gint i = 0; ip->mimes[i]; i ++) 00528 plugin->u.i.keys[INPUT_KEY_MIME] = g_list_prepend 00529 (plugin->u.i.keys[INPUT_KEY_MIME], g_strdup (ip->mimes[i])); 00530 } 00531 00532 if (PLUGIN_HAS_FUNC (ip, schemes)) 00533 { 00534 for (gint i = 0; ip->schemes[i]; i ++) 00535 plugin->u.i.keys[INPUT_KEY_SCHEME] = g_list_prepend 00536 (plugin->u.i.keys[INPUT_KEY_SCHEME], g_strdup (ip->schemes[i])); 00537 } 00538 00539 plugin->u.i.has_images = PLUGIN_HAS_FUNC (ip, get_song_image); 00540 plugin->u.i.has_subtunes = ip->have_subtune; 00541 plugin->u.i.can_write_tuple = PLUGIN_HAS_FUNC (ip, update_song_tuple); 00542 plugin->u.i.has_infowin = PLUGIN_HAS_FUNC (ip, file_info_box); 00543 } 00544 else if (header->type == PLUGIN_TYPE_OUTPUT) 00545 { 00546 OutputPlugin * op = (OutputPlugin *) header; 00547 plugin->priority = 10 - op->probe_priority; 00548 } 00549 else if (header->type == PLUGIN_TYPE_EFFECT) 00550 { 00551 EffectPlugin * ep = (EffectPlugin *) header; 00552 plugin->priority = ep->order; 00553 } 00554 else if (header->type == PLUGIN_TYPE_GENERAL) 00555 { 00556 GeneralPlugin * gp = (GeneralPlugin *) header; 00557 if (new) 00558 plugin->enabled = gp->enabled_by_default; 00559 } 00560 } 00561 00562 gint plugin_get_type (PluginHandle * plugin) 00563 { 00564 return plugin->type; 00565 } 00566 00567 const gchar * plugin_get_filename (PluginHandle * plugin) 00568 { 00569 return plugin->path; 00570 } 00571 00572 const void * plugin_get_header (PluginHandle * plugin) 00573 { 00574 g_static_mutex_lock (& mutex); 00575 00576 if (! plugin->loaded) 00577 { 00578 plugin_load (plugin->path); 00579 plugin->loaded = TRUE; 00580 } 00581 00582 g_static_mutex_unlock (& mutex); 00583 return plugin->header; 00584 } 00585 00586 static gint plugin_by_header_cb (PluginHandle * plugin, const void * header) 00587 { 00588 return (plugin->header == header) ? 0 : -1; 00589 } 00590 00591 PluginHandle * plugin_by_header (const void * header) 00592 { 00593 GList * node = g_list_find_custom (plugin_list, header, (GCompareFunc) 00594 plugin_by_header_cb); 00595 return node ? node->data : NULL; 00596 } 00597 00598 void plugin_for_each (gint type, PluginForEachFunc func, void * data) 00599 { 00600 for (GList * node = plugin_list; node; node = node->next) 00601 { 00602 if (((PluginHandle *) node->data)->type != type) 00603 continue; 00604 if (! func (node->data, data)) 00605 break; 00606 } 00607 } 00608 00609 const gchar * plugin_get_name (PluginHandle * plugin) 00610 { 00611 return plugin->name; 00612 } 00613 00614 gboolean plugin_has_about (PluginHandle * plugin) 00615 { 00616 return plugin->has_about; 00617 } 00618 00619 gboolean plugin_has_configure (PluginHandle * plugin) 00620 { 00621 return plugin->has_configure; 00622 } 00623 00624 gboolean plugin_get_enabled (PluginHandle * plugin) 00625 { 00626 return plugin->enabled; 00627 } 00628 00629 static void plugin_call_watches (PluginHandle * plugin) 00630 { 00631 for (GList * node = plugin->watches; node; ) 00632 { 00633 GList * next = node->next; 00634 PluginWatch * watch = node->data; 00635 00636 if (! watch->func (plugin, watch->data)) 00637 { 00638 g_free (watch); 00639 plugin->watches = g_list_delete_link (plugin->watches, node); 00640 } 00641 00642 node = next; 00643 } 00644 } 00645 00646 void plugin_set_enabled (PluginHandle * plugin, gboolean enabled) 00647 { 00648 plugin->enabled = enabled; 00649 plugin_call_watches (plugin); 00650 } 00651 00652 typedef struct { 00653 PluginForEachFunc func; 00654 void * data; 00655 } PluginForEnabledState; 00656 00657 static gboolean plugin_for_enabled_cb (PluginHandle * plugin, 00658 PluginForEnabledState * state) 00659 { 00660 if (! plugin->enabled) 00661 return TRUE; 00662 return state->func (plugin, state->data); 00663 } 00664 00665 void plugin_for_enabled (gint type, PluginForEachFunc func, void * data) 00666 { 00667 PluginForEnabledState state = {func, data}; 00668 plugin_for_each (type, (PluginForEachFunc) plugin_for_enabled_cb, & state); 00669 } 00670 00671 void plugin_add_watch (PluginHandle * plugin, PluginForEachFunc func, void * 00672 data) 00673 { 00674 PluginWatch * watch = g_malloc (sizeof (PluginWatch)); 00675 watch->func = func; 00676 watch->data = data; 00677 plugin->watches = g_list_prepend (plugin->watches, watch); 00678 } 00679 00680 void plugin_remove_watch (PluginHandle * plugin, PluginForEachFunc func, void * 00681 data) 00682 { 00683 for (GList * node = plugin->watches; node; ) 00684 { 00685 GList * next = node->next; 00686 PluginWatch * watch = node->data; 00687 00688 if (watch->func == func && watch->data == data) 00689 { 00690 g_free (watch); 00691 plugin->watches = g_list_delete_link (plugin->watches, node); 00692 } 00693 00694 node = next; 00695 } 00696 } 00697 00698 typedef struct { 00699 const gchar * scheme; 00700 PluginHandle * plugin; 00701 } TransportPluginForSchemeState; 00702 00703 static gboolean transport_plugin_for_scheme_cb (PluginHandle * plugin, 00704 TransportPluginForSchemeState * state) 00705 { 00706 if (! g_list_find_custom (plugin->u.t.schemes, state->scheme, (GCompareFunc) 00707 strcasecmp)) 00708 return TRUE; 00709 00710 state->plugin = plugin; 00711 return FALSE; 00712 } 00713 00714 PluginHandle * transport_plugin_for_scheme (const gchar * scheme) 00715 { 00716 TransportPluginForSchemeState state = {scheme, NULL}; 00717 plugin_for_enabled (PLUGIN_TYPE_TRANSPORT, (PluginForEachFunc) 00718 transport_plugin_for_scheme_cb, & state); 00719 return state.plugin; 00720 } 00721 00722 typedef struct { 00723 const gchar * ext; 00724 PluginHandle * plugin; 00725 } PlaylistPluginForExtState; 00726 00727 static gboolean playlist_plugin_for_ext_cb (PluginHandle * plugin, 00728 PlaylistPluginForExtState * state) 00729 { 00730 if (! g_list_find_custom (plugin->u.p.exts, state->ext, (GCompareFunc) 00731 strcasecmp)) 00732 return TRUE; 00733 00734 state->plugin = plugin; 00735 return FALSE; 00736 } 00737 00738 PluginHandle * playlist_plugin_for_extension (const gchar * extension) 00739 { 00740 PlaylistPluginForExtState state = {extension, NULL}; 00741 plugin_for_enabled (PLUGIN_TYPE_PLAYLIST, (PluginForEachFunc) 00742 playlist_plugin_for_ext_cb, & state); 00743 return state.plugin; 00744 } 00745 00746 typedef struct { 00747 gint key; 00748 const gchar * value; 00749 PluginForEachFunc func; 00750 void * data; 00751 } InputPluginForKeyState; 00752 00753 static gboolean input_plugin_for_key_cb (PluginHandle * plugin, 00754 InputPluginForKeyState * state) 00755 { 00756 if (! g_list_find_custom (plugin->u.i.keys[state->key], state->value, 00757 (GCompareFunc) strcasecmp)) 00758 return TRUE; 00759 00760 return state->func (plugin, state->data); 00761 } 00762 00763 void input_plugin_for_key (gint key, const gchar * value, PluginForEachFunc 00764 func, void * data) 00765 { 00766 InputPluginForKeyState state = {key, value, func, data}; 00767 plugin_for_enabled (PLUGIN_TYPE_INPUT, (PluginForEachFunc) 00768 input_plugin_for_key_cb, & state); 00769 } 00770 00771 gboolean input_plugin_has_images (PluginHandle * plugin) 00772 { 00773 g_return_val_if_fail (plugin->type == PLUGIN_TYPE_INPUT, FALSE); 00774 return plugin->u.i.has_images; 00775 } 00776 00777 gboolean input_plugin_has_subtunes (PluginHandle * plugin) 00778 { 00779 g_return_val_if_fail (plugin->type == PLUGIN_TYPE_INPUT, FALSE); 00780 return plugin->u.i.has_subtunes; 00781 } 00782 00783 gboolean input_plugin_can_write_tuple (PluginHandle * plugin) 00784 { 00785 g_return_val_if_fail (plugin->type == PLUGIN_TYPE_INPUT, FALSE); 00786 return plugin->u.i.can_write_tuple; 00787 } 00788 00789 gboolean input_plugin_has_infowin (PluginHandle * plugin) 00790 { 00791 g_return_val_if_fail (plugin->type == PLUGIN_TYPE_INPUT, FALSE); 00792 return plugin->u.i.has_infowin; 00793 }