1 /* Gnome Music Player Client (GMPC)
2 * Copyright (C) 2004-2012 Qball Cow <qball@gmpclient.org>
3 * Project homepage: http://gmpclient.org/
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include "gmpc-extras.h"
28 #define PLUGIN_LOG_DOMAIN "Plugin"
30 gmpcPluginParent
**plugins
= NULL
;
33 gmpcPluginParent
*plugin_get_from_id(int id
)
35 int pos
= plugin_get_pos(id
);
36 g_assert_cmpint(pos
, <, num_plugins
);
40 static GQuark
plugin_quark(void)
42 return g_quark_from_static_string("gmpc_plugin");
45 int plugin_get_pos(int id
)
47 return id
& (PLUGIN_ID_MARK
- 1);
51 typedef struct _Blacklist
{
52 const char *plugin_name
;
56 static const int num_blacklisted_plugins
= 7;
57 static Blacklist blacklist
[] =
59 {"Lyrdb.com lyric source", "Plugin is intergrated into GMPC"},
60 {"Extra Playlist View", "Plugin is intergrated into GMPC"},
61 {"Statistics", "Plugin is intergrated into GMPC"},
62 {"DiscoGS Artist and Album Image Fetcher", "Plugin is intergrated into GMPC"},
63 {"Last FM metadata fetcher", "Plugin is intergrated into GMPC"},
64 {"Fullscreen Info", "Plugin is intergrated into GMPC"},
65 {"WikiPedia", "Plugin ddos'es Wikipedia"}
68 static int plugin_manager_blacklist(gmpcPluginParent
*p
, GError
**error
)
71 const char *name
= gmpc_plugin_get_name(p
);
72 g_assert(name
!= NULL
);
73 for(i
= 0; i
<num_blacklisted_plugins
; i
++) {
74 if(strcmp(name
, blacklist
[i
].plugin_name
) == 0) {
75 g_set_error(error
, plugin_quark(), 0,
76 "pluging has with name: %s is blacklisted: '%s'", name
,
78 g_log(PLUGIN_LOG_DOMAIN
, G_LOG_LEVEL_CRITICAL
,
79 "pluging has with name: %s is blacklisted: '%s'", name
,
87 static int plugin_validate(gmpcPlugin
* plug
, GError
** error
)
90 if (plug
->name
== NULL
)
92 g_set_error(error
, plugin_quark(), 0, "%s: %s",
93 _("Failed to load plugin"), _("plugin has no name"));
94 g_log(PLUGIN_LOG_DOMAIN
, G_LOG_LEVEL_CRITICAL
,
95 "pluging has no name: %s", plug
->path
);
98 for (i
= 0; i
< num_plugins
; i
++)
100 if (strcmp(gmpc_plugin_get_name(plugins
[i
]), plug
->name
) == 0)
102 g_set_error(error
, plugin_quark(), 0, "%s '%s': %s",
103 _("Failed to load plugin"), plug
->name
,
104 _("plugin with same name already exists"));
105 g_log(PLUGIN_LOG_DOMAIN
, G_LOG_LEVEL_CRITICAL
,
106 "pluging with same name already exists: %s", plug
->name
);
110 if (plug
->set_enabled
== NULL
|| plug
->get_enabled
== NULL
)
112 g_set_error(error
, plugin_quark(), 0, "%s: %s",
113 _("Failed to load plugin"),
114 _("plugin is missing set/get enable function"));
115 g_log(PLUGIN_LOG_DOMAIN
, G_LOG_LEVEL_CRITICAL
,
116 "%s: set_enabled == NULL || get_enabled == NULL failed",
120 if (plug
->plugin_type
& GMPC_PLUGIN_PL_BROWSER
)
122 if (plug
->browser
== NULL
)
124 g_set_error(error
, plugin_quark(), 0, "%s: %s",
125 _("Failed to load plugin"),
126 _("plugin browser structure is incorrect"));
127 g_log(PLUGIN_LOG_DOMAIN
, G_LOG_LEVEL_CRITICAL
,
128 "%s: plugin_type&GMPC_PLUGIN_PL_BROWSER && plugin->browser != NULL Failed",
133 /* if there is a browser field, check validity */
136 if ((plug
->browser
->selected
&& plug
->browser
->unselected
== NULL
)
137 || (plug
->browser
->selected
== NULL
&& plug
->browser
->unselected
))
139 g_set_error(error
, plugin_quark(), 0, "%s: %s",
140 _("Failed to load plugin"),
141 _("plugin browser structure is incorrect"));
142 g_log(PLUGIN_LOG_DOMAIN
, G_LOG_LEVEL_CRITICAL
,
143 "%s: If a plugin provides a browser pane, it needs both selected and unselected",
148 /* if there is a pref window withouth both construct/destroy, give an error */
151 if (!(plug
->pref
->construct
&& plug
->pref
->destroy
))
153 g_set_error(error
, plugin_quark(), 0, "%s: %s",
154 _("Failed to load plugin"),
155 _("plugin preferences structure is incorrect"));
156 g_log(PLUGIN_LOG_DOMAIN
, G_LOG_LEVEL_CRITICAL
,
157 "%s: If a plugin has a preferences pane, it needs both construct and destroy",
165 void plugin_add(gmpcPlugin
* plug
, int plugin
, GError
** error
)
167 gmpcPluginParent
*parent
= g_malloc0(sizeof(*parent
));
172 if(plugin_manager_blacklist(parent
, error
))
174 if(error
&& *error
!= NULL
)
176 g_log(PLUGIN_LOG_DOMAIN
, G_LOG_LEVEL_CRITICAL
,
177 "%s: Not loading plugin.",
184 plug
->id
= num_plugins
| ((plugin
) ? PLUGIN_ID_MARK
: PLUGIN_ID_INTERNALL
);
185 /* put it in the list */
187 plugins
= g_realloc(plugins
, (num_plugins
+ 1) * sizeof(gmpcPlugin
**));
188 plugins
[num_plugins
- 1] = parent
;
189 plugins
[num_plugins
] = NULL
;
192 void plugin_add_new(GmpcPluginBase
* plug
, int plugin
, GError
** error
)
194 gmpcPluginParent
*parent
= g_malloc0(sizeof(*parent
));
198 if(plugin_manager_blacklist(parent
, error
))
203 plug
->id
= num_plugins
| ((plugin
) ? PLUGIN_ID_MARK
: PLUGIN_ID_INTERNALL
);
204 /* put it in the list */
206 plugins
= g_realloc(plugins
, (num_plugins
+ 1) * sizeof(gmpcPlugin
**));
207 plugins
[num_plugins
- 1] = parent
;
208 plugins
[num_plugins
] = NULL
;
211 static int plugin_load(const char *path
, const char *file
, GError
** error
)
215 int *api_version
= 0;
216 gmpcPlugin
*plug
= NULL
;
217 gchar
*string
= NULL
;
218 gchar
*full_path
= NULL
;
223 full_path
= g_strdup_printf("%s%c%s", path
, G_DIR_SEPARATOR
, file
);
225 g_log(PLUGIN_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
,
226 "plugin_load: trying to load plugin %s", full_path
);
228 handle
= g_module_open(full_path
, G_MODULE_BIND_LOCAL
);
232 g_log(PLUGIN_LOG_DOMAIN
, G_LOG_LEVEL_CRITICAL
,
233 "plugin_load: module failed to load: %s:%s\n", file
,
235 g_set_error(error
, plugin_quark(), 0, "%s %s: '%s'",
236 _("Failed to load plugin"), file
, g_module_error());
240 if (g_module_symbol(handle
, "plugin_get_type", (gpointer
) & function
))
243 GType(*get_type
) (void);
245 new = g_object_newv(get_type(), 0, NULL
);
249 g_set_error(error
, plugin_quark(), 0, "%s %s: '%s'",
250 _("Failed to create plugin instance"), file
,
254 new->path
= g_strdup(path
);
255 plugin_add_new(new, 1, error
);
259 (handle
, "plugin_api_version", (gpointer
) & api_version
))
262 g_log(PLUGIN_LOG_DOMAIN
, G_LOG_LEVEL_CRITICAL
,
263 "plugin_load: symbol failed to bind: %s:%s\n", file
,
266 g_set_error(error
, plugin_quark(), 0, "%s %s: '%s'",
267 _("Failed to bind symbol in plugin"), file
,
271 g_module_close(handle
);
274 if (*api_version
!= PLUGIN_API_VERSION
)
276 g_log(PLUGIN_LOG_DOMAIN
, G_LOG_LEVEL_CRITICAL
,
277 "Plugin '%s' has the wrong api version.\nPlugin api is %i, but we need %i",
278 file
, *api_version
, PLUGIN_API_VERSION
);
280 g_set_error(error
, plugin_quark(), 0,
281 _("Plugin %s has wrong api version: %i"), file
,
285 g_module_close(handle
);
288 if (!g_module_symbol(handle
, "plugin", (gpointer
) & (plug
)))
290 g_log(PLUGIN_LOG_DOMAIN
, G_LOG_LEVEL_CRITICAL
,
291 "plugin_load: symbol failed to bind: %s:%s\n", file
,
294 g_set_error(error
, plugin_quark(), 0, "%s %s: '%s'",
295 _("Plugin %s has wrong no plugin structure: %s"), file
,
298 g_module_close(handle
);
303 g_set_error(error
, plugin_quark(), 0, "%s %s: '%s'",
304 _("Plugin %s has wrong no plugin structure: %s"), file
,
306 g_log(PLUGIN_LOG_DOMAIN
, G_LOG_LEVEL_CRITICAL
,
307 "%s: plugin load: unknown type of plugin.\n", file
);
308 g_module_close(handle
);
311 /* set path, plugins might want this for images and glade files. */
312 plug
->path
= g_strdup(path
);
313 if (!plugin_validate(plug
, error
))
315 g_module_close(handle
);
318 /* add the plugin to the list */
319 plugin_add(plug
, 1, error
);
323 static gboolean
__show_plugin_load_error(gpointer data
)
325 playlist3_show_error_message(_
326 ("One or more plugins failed to load, see help->messages for more information"),
331 void plugin_load_dir(const gchar
* path
)
333 GDir
*dir
= g_dir_open(path
, 0, NULL
);
337 const gchar
*dirname
= NULL
;
338 while ((dirname
= g_dir_read_name(dir
)) != NULL
)
341 g_strdup_printf("%s%c%s", path
, G_DIR_SEPARATOR
, dirname
);
342 /* Make sure only to load plugins */
343 if (g_str_has_suffix(dirname
, G_MODULE_SUFFIX
))
345 GError
*error
= NULL
;
346 if (plugin_load(path
, dirname
, &error
))
349 playlist3_show_error_message(error
->message
,
351 g_log(PLUGIN_LOG_DOMAIN
, G_LOG_LEVEL_CRITICAL
,
352 "Failed to load plugin: %s: %s\n", dirname
,
359 g_log(PLUGIN_LOG_DOMAIN
, G_LOG_LEVEL_INFO
,
360 "%s not loaded, wrong extension, should be: '%s'",
361 dirname
, G_MODULE_SUFFIX
);
370 g_idle_add(__show_plugin_load_error
, NULL
);
371 //gtk_init_add(__show_plugin_load_error, NULL);
379 void gmpc_plugin_destroy(gmpcPluginParent
* plug
)
383 g_object_unref(plug
->new);
386 if (plug
->old
->destroy
)
388 plug
->old
->destroy();
392 void gmpc_plugin_init(gmpcPluginParent
* plug
)
404 void gmpc_plugin_status_changed(gmpcPluginParent
* plug
, MpdObj
* mi
,
405 ChangedStatusType what
)
409 if (plug
->old
->mpd_status_changed
)
411 plug
->old
->mpd_status_changed(mi
, what
, NULL
);
415 const char *gmpc_plugin_get_name(gmpcPluginParent
* plug
)
417 /* if new plugin, use that method */
420 return gmpc_plugin_base_get_name(plug
->new);
422 g_assert(plug
->old
->name
!= NULL
);
423 return plug
->old
->name
;
426 void gmpc_plugin_save_yourself(gmpcPluginParent
* plug
)
430 gmpc_plugin_base_save_yourself(plug
->new);
433 if (plug
->old
->save_yourself
)
435 plug
->old
->save_yourself();
439 gboolean
gmpc_plugin_get_enabled(const gmpcPluginParent
* plug
)
443 return gmpc_plugin_base_get_enabled(plug
->new);
445 if (plug
->old
->get_enabled
)
447 return plug
->old
->get_enabled();
452 void gmpc_plugin_set_enabled(gmpcPluginParent
* plug
, gboolean enabled
)
456 return gmpc_plugin_base_set_enabled(plug
->new, enabled
);
458 if (plug
->old
->set_enabled
)
460 plug
->old
->set_enabled(enabled
);
464 const gchar
*gmpc_plugin_get_translation_domain(gmpcPluginParent
* plug
)
468 return plug
->new->translation_domain
;
470 if (plug
->old
&& plug
->old
->get_translation_domain
)
472 return plug
->old
->get_translation_domain();
477 gchar
*gmpc_plugin_get_data_path(gmpcPlugin
* plug
)
480 gchar
*url
= g_win32_get_package_installation_directory_of_module(NULL
);
482 g_build_path(G_DIR_SEPARATOR_S
, url
, "share", "gmpc", "plugins", NULL
);
483 return retv
; //g_strdup(plug->path);
486 const gchar
*const *paths
= g_get_system_data_dirs();
488 gchar
*homedir
= gmpc_get_user_path("");
489 if (strncmp(plug
->path
, homedir
, strlen(homedir
)) == 0)
491 url
= g_strdup(plug
->path
);
494 /* Ok it is a homedir */
496 g_build_path(G_DIR_SEPARATOR_S
, PACKAGE_DATA_DIR
, "gmpc", "plugins",
504 if (g_file_test(url
, G_FILE_TEST_EXISTS
| G_FILE_TEST_IS_DIR
))
512 for (i
= 0; paths
&& paths
[i
]; i
++)
514 url
= g_build_filename(paths
[i
], "gmpc", "plugins", NULL
);
515 if (g_file_test(url
, G_FILE_TEST_EXISTS
| G_FILE_TEST_IS_DIR
))
526 void gmpc_plugin_mpd_connection_changed(gmpcPluginParent
* plug
, MpdObj
* mi
,
527 int connected
, gpointer data
)
529 g_assert(plug
!= NULL
);
533 if (plug
->old
->mpd_connection_changed
!= NULL
)
535 plug
->old
->mpd_connection_changed(mi
, connected
, data
);
538 gboolean
gmpc_plugin_is_sidebar(gmpcPluginParent
*plug
)
540 g_assert(plug
!= NULL
);
543 return GMPC_PLUGIN_IS_SIDEBAR_IFACE(plug
->new);
547 void gmpc_plugin_sidebar_init(gmpcPluginParent
*plug
)
549 if(gmpc_plugin_is_sidebar(plug
))
551 gmpc_sidebar_plugins_init(GMPC_PLUGIN_SIDEBAR_IFACE(plug
->new));
555 void gmpc_plugin_sidebar_set_state(gmpcPluginParent
*plug
, GmpcPluginSidebarState state
)
557 if(gmpc_plugin_is_sidebar(plug
))
559 gmpc_plugin_sidebar_iface_sidebar_set_state(GMPC_PLUGIN_SIDEBAR_IFACE(plug
->new), state
);
563 gboolean
gmpc_plugin_is_browser(gmpcPluginParent
* plug
)
567 return GMPC_PLUGIN_IS_BROWSER_IFACE(plug
->new);
569 return ((plug
->old
->plugin_type
& GMPC_PLUGIN_PL_BROWSER
) != 0);
572 void gmpc_plugin_browser_unselected(gmpcPluginParent
* plug
,
573 GtkWidget
* container
)
575 if (gmpc_plugin_is_browser(plug
))
579 gmpc_plugin_browser_iface_browser_unselected((GmpcPluginBrowserIface
585 g_assert(plug
->old
->browser
!= NULL
);
586 g_assert(plug
->old
->browser
->unselected
!= NULL
);
587 plug
->old
->browser
->unselected(container
);
591 void gmpc_plugin_browser_selected(gmpcPluginParent
* plug
,
592 GtkWidget
* container
)
594 if (gmpc_plugin_is_browser(plug
))
598 gmpc_plugin_browser_iface_browser_selected((GmpcPluginBrowserIface
604 g_assert(plug
->old
->browser
!= NULL
);
605 g_assert(plug
->old
->browser
->selected
!= NULL
);
606 plug
->old
->browser
->selected(container
);
610 void gmpc_plugin_browser_add(gmpcPluginParent
* plug
, GtkWidget
* cat_tree
)
612 if (gmpc_plugin_is_browser(plug
))
616 gmpc_plugin_browser_iface_browser_add((GmpcPluginBrowserIface
*)
617 plug
->new, cat_tree
);
620 g_assert(plug
->old
->browser
!= NULL
);
621 if (plug
->old
->browser
->add
)
623 plug
->old
->browser
->add(cat_tree
);
628 int gmpc_plugin_browser_cat_right_mouse_menu(gmpcPluginParent
* plug
,
629 GtkWidget
* menu
, int type
,
631 GdkEventButton
* event
)
633 if (gmpc_plugin_is_browser(plug
))
637 if (type
== plug
->new->id
)
639 gmpc_plugin_browser_iface_browser_option_menu((GmpcPluginBrowserIface
*) plug
->new, GTK_MENU(menu
));
642 g_assert(plug
->old
->browser
!= NULL
);
643 if (plug
->old
->browser
->cat_right_mouse_menu
!= NULL
)
645 return plug
->old
->browser
->cat_right_mouse_menu(menu
, type
, tree
,
652 int gmpc_plugin_browser_key_press_event(gmpcPluginParent
* plug
, GtkWidget
* mw
,
653 GdkEventKey
* event
, int type
)
655 if (gmpc_plugin_is_browser(plug
))
659 /* not going to be implemented */
662 g_assert(plug
->old
->browser
!= NULL
);
663 if (plug
->old
->browser
->key_press_event
!= NULL
)
665 return plug
->old
->browser
->key_press_event(mw
, event
, type
);
671 int gmpc_plugin_browser_add_go_menu(gmpcPluginParent
* plug
, GtkWidget
* menu
)
673 if (gmpc_plugin_is_browser(plug
))
678 gmpc_plugin_browser_iface_browser_add_go_menu((GmpcPluginBrowserIface
*) plug
->new, GTK_MENU(menu
));
680 g_assert(plug
->old
->browser
!= NULL
);
681 if (plug
->old
->browser
->add_go_menu
!= NULL
)
683 return plug
->old
->browser
->add_go_menu(menu
);
689 int gmpc_plugin_browser_song_list_option_menu(gmpcPluginParent
* plug
,
690 GmpcMpdDataTreeview
* tree
,
695 if (GMPC_PLUGIN_IS_SONG_LIST_IFACE(plug
->new))
698 gmpc_plugin_song_list_iface_song_list
699 (GMPC_PLUGIN_SONG_LIST_IFACE(plug
->new), GTK_WIDGET(tree
),
704 if (gmpc_plugin_is_browser(plug
))
706 g_assert(plug
->old
->browser
!= NULL
);
707 if (plug
->old
->browser
->song_list_option_menu
)
709 return plug
->old
->browser
->song_list_option_menu(tree
, menu
);
715 gboolean
gmpc_plugin_browser_has_integrate_search(gmpcPluginParent
* plug
)
719 return GMPC_PLUGIN_IS_INTEGRATE_SEARCH_IFACE(plug
->new);
721 if (gmpc_plugin_is_browser(plug
))
723 return plug
->old
->browser
->integrate_search
!= NULL
;
728 MpdData
*gmpc_plugin_browser_integrate_search(gmpcPluginParent
* plug
,
729 const int search_field
,
733 if (!gmpc_plugin_browser_has_integrate_search(plug
))
737 gmpc_plugin_integrate_search_iface_search
738 (GMPC_PLUGIN_INTEGRATE_SEARCH_IFACE(plug
->new), search_field
,
740 return plug
->old
->browser
->integrate_search(search_field
, query
, error
);
743 gboolean
gmpc_plugin_browser_integrate_search_field_supported(gmpcPluginParent
*
748 if (!gmpc_plugin_browser_has_integrate_search(plug
))
753 gmpc_plugin_integrate_search_iface_field_supported
754 (GMPC_PLUGIN_INTEGRATE_SEARCH_IFACE(plug
->new), search_field
);
756 if (plug
->old
->browser
->integrate_search_field_supported
== NULL
)
758 return plug
->old
->browser
->integrate_search_field_supported(search_field
);
761 gboolean
gmpc_plugin_has_preferences(gmpcPluginParent
* plug
)
765 return GMPC_PLUGIN_IS_PREFERENCES_IFACE(plug
->new);
767 return (plug
->old
->pref
!= NULL
);
770 void gmpc_plugin_preferences_construct(gmpcPluginParent
* plug
, GtkWidget
* wid
)
772 if (gmpc_plugin_has_preferences(plug
))
776 gmpc_plugin_preferences_iface_preferences_pane_construct
777 (GMPC_PLUGIN_PREFERENCES_IFACE(plug
->new), GTK_CONTAINER(wid
));
780 g_assert(plug
->old
->pref
!= NULL
);
781 g_assert(plug
->old
->pref
->construct
);
782 plug
->old
->pref
->construct(wid
);
786 void gmpc_plugin_preferences_destroy(gmpcPluginParent
* plug
, GtkWidget
* wid
)
788 if (gmpc_plugin_has_preferences(plug
))
792 gmpc_plugin_preferences_iface_preferences_pane_destroy
793 (GMPC_PLUGIN_PREFERENCES_IFACE(plug
->new), GTK_CONTAINER(wid
));
796 g_assert(plug
->old
->pref
!= NULL
);
797 g_assert(plug
->old
->pref
->destroy
);
798 plug
->old
->pref
->destroy(wid
);
802 gboolean
gmpc_plugin_is_internal(gmpcPluginParent
* plug
)
806 return (((plug
->new->plugin_type
) & GMPC_INTERNALL
) != 0);
809 return (((plug
->old
->plugin_type
) & GMPC_INTERNALL
) != 0);
812 const int *gmpc_plugin_get_version(gmpcPluginParent
* plug
)
817 return (const int *)gmpc_plugin_base_get_version(plug
->new, &length
);
819 return (const int *)plug
->old
->version
;
822 int gmpc_plugin_get_type(gmpcPluginParent
* plug
)
826 return plug
->new->plugin_type
;
828 return plug
->old
->plugin_type
;
831 int gmpc_plugin_get_id(gmpcPluginParent
* plug
)
835 return plug
->new->id
;
837 return plug
->old
->id
;
840 gint
gmpc_plugin_tool_menu_integration(gmpcPluginParent
* plug
, GtkMenu
* menu
)
844 if (GMPC_PLUGIN_IS_TOOL_MENU_IFACE(plug
->new))
847 gmpc_plugin_tool_menu_iface_tool_menu_integration
848 (GMPC_PLUGIN_TOOL_MENU_IFACE(plug
->new), menu
);
854 if (plug
->old
->tool_menu_integration
)
855 return plug
->old
->tool_menu_integration(menu
);
860 gboolean
gmpc_plugin_has_enabled(gmpcPluginParent
* plug
)
866 if (plug
->old
&& plug
->old
->get_enabled
&& plug
->old
->set_enabled
)