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 Blacklist blacklist
[] =
58 {"Lyrdb.com lyric source", "Plugin is integrated into GMPC"},
59 {"Extra Playlist View", "Plugin is integrated into GMPC"},
60 {"Statistics", "Plugin is integrated into GMPC"},
61 {"DiscoGS Artist and Album Image Fetcher", "Plugin is integrated into GMPC"},
62 {"Last FM metadata fetcher", "Plugin is integrated into GMPC"},
63 {"Fullscreen Info", "Plugin is integrated into GMPC"},
64 {"WikiPedia", "Plugin ddos'es Wikipedia"},
65 {"Libnotify Plugin", "Plugin is integrated into GMPC"}
67 static const int num_blacklisted_plugins
= sizeof(blacklist
)/sizeof(Blacklist
);
69 static int plugin_manager_blacklist(gmpcPluginParent
*p
, GError
**error
)
72 const char *name
= gmpc_plugin_get_name(p
);
73 g_assert(name
!= NULL
);
74 for(i
= 0; i
<num_blacklisted_plugins
; i
++) {
75 if(strcmp(name
, blacklist
[i
].plugin_name
) == 0) {
76 g_set_error(error
, plugin_quark(), 0,
77 "pluging has with name: %s is blacklisted: '%s'", name
,
79 g_log(PLUGIN_LOG_DOMAIN
, G_LOG_LEVEL_CRITICAL
,
80 "pluging has with name: %s is blacklisted: '%s'", name
,
88 static int plugin_validate(gmpcPlugin
* plug
, GError
** error
)
91 if (plug
->name
== NULL
)
93 g_set_error(error
, plugin_quark(), 0, "%s: %s",
94 _("Failed to load plugin"), _("plugin has no name"));
95 g_log(PLUGIN_LOG_DOMAIN
, G_LOG_LEVEL_CRITICAL
,
96 "pluging has no name: %s", plug
->path
);
99 for (i
= 0; i
< num_plugins
; i
++)
101 if (strcmp(gmpc_plugin_get_name(plugins
[i
]), plug
->name
) == 0)
103 g_set_error(error
, plugin_quark(), 0, "%s '%s': %s",
104 _("Failed to load plugin"), plug
->name
,
105 _("plugin with same name already exists"));
106 g_log(PLUGIN_LOG_DOMAIN
, G_LOG_LEVEL_CRITICAL
,
107 "pluging with same name already exists: %s", plug
->name
);
111 if (plug
->set_enabled
== NULL
|| plug
->get_enabled
== NULL
)
113 g_set_error(error
, plugin_quark(), 0, "%s: %s",
114 _("Failed to load plugin"),
115 _("plugin is missing set/get enable function"));
116 g_log(PLUGIN_LOG_DOMAIN
, G_LOG_LEVEL_CRITICAL
,
117 "%s: set_enabled == NULL || get_enabled == NULL failed",
121 if (plug
->plugin_type
& GMPC_PLUGIN_PL_BROWSER
)
123 if (plug
->browser
== NULL
)
125 g_set_error(error
, plugin_quark(), 0, "%s: %s",
126 _("Failed to load plugin"),
127 _("plugin browser structure is incorrect"));
128 g_log(PLUGIN_LOG_DOMAIN
, G_LOG_LEVEL_CRITICAL
,
129 "%s: plugin_type&GMPC_PLUGIN_PL_BROWSER && plugin->browser != NULL Failed",
134 /* if there is a browser field, check validity */
137 if ((plug
->browser
->selected
&& plug
->browser
->unselected
== NULL
)
138 || (plug
->browser
->selected
== NULL
&& plug
->browser
->unselected
))
140 g_set_error(error
, plugin_quark(), 0, "%s: %s",
141 _("Failed to load plugin"),
142 _("plugin browser structure is incorrect"));
143 g_log(PLUGIN_LOG_DOMAIN
, G_LOG_LEVEL_CRITICAL
,
144 "%s: If a plugin provides a browser pane, it needs both selected and unselected",
149 /* if there is a pref window withouth both construct/destroy, give an error */
152 if (!(plug
->pref
->construct
&& plug
->pref
->destroy
))
154 g_set_error(error
, plugin_quark(), 0, "%s: %s",
155 _("Failed to load plugin"),
156 _("plugin preferences structure is incorrect"));
157 g_log(PLUGIN_LOG_DOMAIN
, G_LOG_LEVEL_CRITICAL
,
158 "%s: If a plugin has a preferences pane, it needs both construct and destroy",
166 void plugin_add(gmpcPlugin
* plug
, int plugin
, GError
** error
)
168 gmpcPluginParent
*parent
= g_malloc0(sizeof(*parent
));
173 if(plugin_manager_blacklist(parent
, error
))
175 if(error
&& *error
!= NULL
)
177 g_log(PLUGIN_LOG_DOMAIN
, G_LOG_LEVEL_CRITICAL
,
178 "%s: Not loading plugin.",
185 plug
->id
= num_plugins
| ((plugin
) ? PLUGIN_ID_MARK
: PLUGIN_ID_INTERNALL
);
186 /* put it in the list */
188 plugins
= g_realloc(plugins
, (num_plugins
+ 1) * sizeof(gmpcPlugin
**));
189 plugins
[num_plugins
- 1] = parent
;
190 plugins
[num_plugins
] = NULL
;
193 void plugin_add_new(GmpcPluginBase
* plug
, int plugin
, GError
** error
)
195 gmpcPluginParent
*parent
= g_malloc0(sizeof(*parent
));
199 if(plugin_manager_blacklist(parent
, error
))
204 plug
->id
= num_plugins
| ((plugin
) ? PLUGIN_ID_MARK
: PLUGIN_ID_INTERNALL
);
205 /* put it in the list */
207 plugins
= g_realloc(plugins
, (num_plugins
+ 1) * sizeof(gmpcPlugin
**));
208 plugins
[num_plugins
- 1] = parent
;
209 plugins
[num_plugins
] = NULL
;
212 static int plugin_load(const char *path
, const char *file
, GError
** error
)
216 int *api_version
= 0;
217 gmpcPlugin
*plug
= NULL
;
218 gchar
*string
= NULL
;
219 gchar
*full_path
= NULL
;
224 full_path
= g_strdup_printf("%s%c%s", path
, G_DIR_SEPARATOR
, file
);
226 g_log(PLUGIN_LOG_DOMAIN
, G_LOG_LEVEL_DEBUG
,
227 "plugin_load: trying to load plugin %s", full_path
);
229 handle
= g_module_open(full_path
, G_MODULE_BIND_LOCAL
);
233 g_log(PLUGIN_LOG_DOMAIN
, G_LOG_LEVEL_CRITICAL
,
234 "plugin_load: module failed to load: %s:%s\n", file
,
236 g_set_error(error
, plugin_quark(), 0, "%s %s: '%s'",
237 _("Failed to load plugin"), file
, g_module_error());
241 if (g_module_symbol(handle
, "plugin_get_type", (gpointer
) & function
))
244 GType(*get_type
) (void);
246 new = g_object_newv(get_type(), 0, NULL
);
250 g_set_error(error
, plugin_quark(), 0, "%s %s: '%s'",
251 _("Failed to create plugin instance"), file
,
255 new->path
= g_strdup(path
);
256 plugin_add_new(new, 1, error
);
260 (handle
, "plugin_api_version", (gpointer
) & api_version
))
263 g_log(PLUGIN_LOG_DOMAIN
, G_LOG_LEVEL_CRITICAL
,
264 "plugin_load: symbol failed to bind: %s:%s\n", file
,
267 g_set_error(error
, plugin_quark(), 0, "%s %s: '%s'",
268 _("Failed to bind symbol in plugin"), file
,
272 g_module_close(handle
);
275 if (*api_version
!= PLUGIN_API_VERSION
)
277 g_log(PLUGIN_LOG_DOMAIN
, G_LOG_LEVEL_CRITICAL
,
278 "Plugin '%s' has the wrong api version.\nPlugin api is %i, but we need %i",
279 file
, *api_version
, PLUGIN_API_VERSION
);
281 g_set_error(error
, plugin_quark(), 0,
282 _("Plugin %s has wrong api version: %i"), file
,
286 g_module_close(handle
);
289 if (!g_module_symbol(handle
, "plugin", (gpointer
) & (plug
)))
291 g_log(PLUGIN_LOG_DOMAIN
, G_LOG_LEVEL_CRITICAL
,
292 "plugin_load: symbol failed to bind: %s:%s\n", file
,
295 g_set_error(error
, plugin_quark(), 0, "%s %s: '%s'",
296 _("Plugin %s has wrong no plugin structure: %s"), file
,
299 g_module_close(handle
);
304 g_set_error(error
, plugin_quark(), 0, "%s %s: '%s'",
305 _("Plugin %s has wrong no plugin structure: %s"), file
,
307 g_log(PLUGIN_LOG_DOMAIN
, G_LOG_LEVEL_CRITICAL
,
308 "%s: plugin load: unknown type of plugin.\n", file
);
309 g_module_close(handle
);
312 /* set path, plugins might want this for images and glade files. */
313 plug
->path
= g_strdup(path
);
314 if (!plugin_validate(plug
, error
))
316 g_module_close(handle
);
319 /* add the plugin to the list */
320 plugin_add(plug
, 1, error
);
324 static gboolean
__show_plugin_load_error(gpointer data
)
326 playlist3_show_error_message(_
327 ("One or more plugins failed to load, see help->messages for more information"),
332 void plugin_load_dir(const gchar
* path
)
334 GDir
*dir
= g_dir_open(path
, 0, NULL
);
338 const gchar
*dirname
= NULL
;
339 while ((dirname
= g_dir_read_name(dir
)) != NULL
)
342 g_strdup_printf("%s%c%s", path
, G_DIR_SEPARATOR
, dirname
);
343 /* Make sure only to load plugins */
344 if (g_str_has_suffix(dirname
, G_MODULE_SUFFIX
))
346 GError
*error
= NULL
;
347 if (plugin_load(path
, dirname
, &error
))
350 playlist3_show_error_message(error
->message
,
352 g_log(PLUGIN_LOG_DOMAIN
, G_LOG_LEVEL_CRITICAL
,
353 "Failed to load plugin: %s: %s\n", dirname
,
360 g_log(PLUGIN_LOG_DOMAIN
, G_LOG_LEVEL_INFO
,
361 "%s not loaded, wrong extension, should be: '%s'",
362 dirname
, G_MODULE_SUFFIX
);
371 g_idle_add(__show_plugin_load_error
, NULL
);
372 //gtk_init_add(__show_plugin_load_error, NULL);
380 void gmpc_plugin_destroy(gmpcPluginParent
* plug
)
384 g_object_unref(plug
->new);
387 if (plug
->old
->destroy
)
389 plug
->old
->destroy();
393 void gmpc_plugin_init(gmpcPluginParent
* plug
)
405 void gmpc_plugin_status_changed(gmpcPluginParent
* plug
, MpdObj
* mi
,
406 ChangedStatusType what
)
410 if (plug
->old
->mpd_status_changed
)
412 plug
->old
->mpd_status_changed(mi
, what
, NULL
);
416 const char *gmpc_plugin_get_name(gmpcPluginParent
* plug
)
418 /* if new plugin, use that method */
421 return gmpc_plugin_base_get_name(plug
->new);
423 g_assert(plug
->old
->name
!= NULL
);
424 return plug
->old
->name
;
427 void gmpc_plugin_save_yourself(gmpcPluginParent
* plug
)
431 gmpc_plugin_base_save_yourself(plug
->new);
434 if (plug
->old
->save_yourself
)
436 plug
->old
->save_yourself();
440 gboolean
gmpc_plugin_get_enabled(const gmpcPluginParent
* plug
)
444 return gmpc_plugin_base_get_enabled(plug
->new);
446 if (plug
->old
->get_enabled
)
448 return plug
->old
->get_enabled();
453 void gmpc_plugin_set_enabled(gmpcPluginParent
* plug
, gboolean enabled
)
457 return gmpc_plugin_base_set_enabled(plug
->new, enabled
);
459 if (plug
->old
->set_enabled
)
461 plug
->old
->set_enabled(enabled
);
465 const gchar
*gmpc_plugin_get_translation_domain(gmpcPluginParent
* plug
)
469 return plug
->new->translation_domain
;
471 if (plug
->old
&& plug
->old
->get_translation_domain
)
473 return plug
->old
->get_translation_domain();
478 gchar
*gmpc_plugin_get_data_path(gmpcPlugin
* plug
)
481 gchar
*url
= g_win32_get_package_installation_directory_of_module(NULL
);
483 g_build_path(G_DIR_SEPARATOR_S
, url
, "share", "gmpc", "plugins", NULL
);
484 return retv
; //g_strdup(plug->path);
487 const gchar
*const *paths
= g_get_system_data_dirs();
489 gchar
*homedir
= gmpc_get_user_path("");
490 if (strncmp(plug
->path
, homedir
, strlen(homedir
)) == 0)
492 url
= g_strdup(plug
->path
);
495 /* Ok it is a homedir */
497 g_build_path(G_DIR_SEPARATOR_S
, PACKAGE_DATA_DIR
, "gmpc", "plugins",
505 if (g_file_test(url
, G_FILE_TEST_EXISTS
| G_FILE_TEST_IS_DIR
))
513 for (i
= 0; paths
&& paths
[i
]; i
++)
515 url
= g_build_filename(paths
[i
], "gmpc", "plugins", NULL
);
516 if (g_file_test(url
, G_FILE_TEST_EXISTS
| G_FILE_TEST_IS_DIR
))
527 void gmpc_plugin_mpd_connection_changed(gmpcPluginParent
* plug
, MpdObj
* mi
,
528 int connected
, gpointer data
)
530 g_assert(plug
!= NULL
);
534 if (plug
->old
->mpd_connection_changed
!= NULL
)
536 plug
->old
->mpd_connection_changed(mi
, connected
, data
);
539 gboolean
gmpc_plugin_is_sidebar(gmpcPluginParent
*plug
)
541 g_assert(plug
!= NULL
);
544 return GMPC_PLUGIN_IS_SIDEBAR_IFACE(plug
->new);
548 void gmpc_plugin_sidebar_init(gmpcPluginParent
*plug
)
550 if(gmpc_plugin_is_sidebar(plug
))
552 gmpc_sidebar_plugins_init(GMPC_PLUGIN_SIDEBAR_IFACE(plug
->new));
556 void gmpc_plugin_sidebar_set_state(gmpcPluginParent
*plug
, GmpcPluginSidebarState state
)
558 if(gmpc_plugin_is_sidebar(plug
))
560 gmpc_plugin_sidebar_iface_sidebar_set_state(GMPC_PLUGIN_SIDEBAR_IFACE(plug
->new), state
);
564 gboolean
gmpc_plugin_is_browser(gmpcPluginParent
* plug
)
568 return GMPC_PLUGIN_IS_BROWSER_IFACE(plug
->new);
570 return ((plug
->old
->plugin_type
& GMPC_PLUGIN_PL_BROWSER
) != 0);
573 void gmpc_plugin_browser_unselected(gmpcPluginParent
* plug
,
574 GtkWidget
* container
)
576 if (gmpc_plugin_is_browser(plug
))
580 gmpc_plugin_browser_iface_browser_unselected((GmpcPluginBrowserIface
586 g_assert(plug
->old
->browser
!= NULL
);
587 g_assert(plug
->old
->browser
->unselected
!= NULL
);
588 plug
->old
->browser
->unselected(container
);
592 void gmpc_plugin_browser_selected(gmpcPluginParent
* plug
,
593 GtkWidget
* container
)
595 if (gmpc_plugin_is_browser(plug
))
599 gmpc_plugin_browser_iface_browser_selected((GmpcPluginBrowserIface
605 g_assert(plug
->old
->browser
!= NULL
);
606 g_assert(plug
->old
->browser
->selected
!= NULL
);
607 plug
->old
->browser
->selected(container
);
611 void gmpc_plugin_browser_add(gmpcPluginParent
* plug
, GtkWidget
* cat_tree
)
613 if (gmpc_plugin_is_browser(plug
))
617 gmpc_plugin_browser_iface_browser_add((GmpcPluginBrowserIface
*)
618 plug
->new, cat_tree
);
621 g_assert(plug
->old
->browser
!= NULL
);
622 if (plug
->old
->browser
->add
)
624 plug
->old
->browser
->add(cat_tree
);
629 int gmpc_plugin_browser_cat_right_mouse_menu(gmpcPluginParent
* plug
,
630 GtkWidget
* menu
, int type
,
632 GdkEventButton
* event
)
634 if (gmpc_plugin_is_browser(plug
))
638 if (type
== plug
->new->id
)
640 gmpc_plugin_browser_iface_browser_option_menu((GmpcPluginBrowserIface
*) plug
->new, GTK_MENU(menu
));
643 g_assert(plug
->old
->browser
!= NULL
);
644 if (plug
->old
->browser
->cat_right_mouse_menu
!= NULL
)
646 return plug
->old
->browser
->cat_right_mouse_menu(menu
, type
, tree
,
653 int gmpc_plugin_browser_key_press_event(gmpcPluginParent
* plug
, GtkWidget
* mw
,
654 GdkEventKey
* event
, int type
)
656 if (gmpc_plugin_is_browser(plug
))
660 /* not going to be implemented */
663 g_assert(plug
->old
->browser
!= NULL
);
664 if (plug
->old
->browser
->key_press_event
!= NULL
)
666 return plug
->old
->browser
->key_press_event(mw
, event
, type
);
672 int gmpc_plugin_browser_add_go_menu(gmpcPluginParent
* plug
, GtkWidget
* menu
)
674 if (gmpc_plugin_is_browser(plug
))
679 gmpc_plugin_browser_iface_browser_add_go_menu((GmpcPluginBrowserIface
*) plug
->new, GTK_MENU(menu
));
681 g_assert(plug
->old
->browser
!= NULL
);
682 if (plug
->old
->browser
->add_go_menu
!= NULL
)
684 return plug
->old
->browser
->add_go_menu(menu
);
690 int gmpc_plugin_browser_song_list_option_menu(gmpcPluginParent
* plug
,
696 if (GMPC_PLUGIN_IS_SONG_LIST_IFACE(plug
->new))
699 gmpc_plugin_song_list_iface_song_list
700 (GMPC_PLUGIN_SONG_LIST_IFACE(plug
->new), GTK_WIDGET(tree
),
705 if (gmpc_plugin_is_browser(plug
))
707 g_assert(plug
->old
->browser
!= NULL
);
708 if (plug
->old
->browser
->song_list_option_menu
)
710 return plug
->old
->browser
->song_list_option_menu(tree
, menu
);
716 gboolean
gmpc_plugin_browser_has_integrate_search(gmpcPluginParent
* plug
)
720 return GMPC_PLUGIN_IS_INTEGRATE_SEARCH_IFACE(plug
->new);
722 if (gmpc_plugin_is_browser(plug
))
724 return plug
->old
->browser
->integrate_search
!= NULL
;
729 MpdData
*gmpc_plugin_browser_integrate_search(gmpcPluginParent
* plug
,
730 const int search_field
,
734 if (!gmpc_plugin_browser_has_integrate_search(plug
))
738 gmpc_plugin_integrate_search_iface_search
739 (GMPC_PLUGIN_INTEGRATE_SEARCH_IFACE(plug
->new), search_field
,
741 return plug
->old
->browser
->integrate_search(search_field
, query
, error
);
744 gboolean
gmpc_plugin_browser_integrate_search_field_supported(gmpcPluginParent
*
749 if (!gmpc_plugin_browser_has_integrate_search(plug
))
754 gmpc_plugin_integrate_search_iface_field_supported
755 (GMPC_PLUGIN_INTEGRATE_SEARCH_IFACE(plug
->new), search_field
);
757 if (plug
->old
->browser
->integrate_search_field_supported
== NULL
)
759 return plug
->old
->browser
->integrate_search_field_supported(search_field
);
762 gboolean
gmpc_plugin_has_preferences(gmpcPluginParent
* plug
)
766 return GMPC_PLUGIN_IS_PREFERENCES_IFACE(plug
->new);
768 return (plug
->old
->pref
!= NULL
);
771 void gmpc_plugin_preferences_construct(gmpcPluginParent
* plug
, GtkWidget
* wid
)
773 if (gmpc_plugin_has_preferences(plug
))
777 gmpc_plugin_preferences_iface_preferences_pane_construct
778 (GMPC_PLUGIN_PREFERENCES_IFACE(plug
->new), GTK_CONTAINER(wid
));
781 g_assert(plug
->old
->pref
!= NULL
);
782 g_assert(plug
->old
->pref
->construct
);
783 plug
->old
->pref
->construct(wid
);
787 void gmpc_plugin_preferences_destroy(gmpcPluginParent
* plug
, GtkWidget
* wid
)
789 if (gmpc_plugin_has_preferences(plug
))
793 gmpc_plugin_preferences_iface_preferences_pane_destroy
794 (GMPC_PLUGIN_PREFERENCES_IFACE(plug
->new), GTK_CONTAINER(wid
));
797 g_assert(plug
->old
->pref
!= NULL
);
798 g_assert(plug
->old
->pref
->destroy
);
799 plug
->old
->pref
->destroy(wid
);
803 gboolean
gmpc_plugin_is_internal(gmpcPluginParent
* plug
)
807 return (((plug
->new->plugin_type
) & GMPC_INTERNALL
) != 0);
810 return (((plug
->old
->plugin_type
) & GMPC_INTERNALL
) != 0);
813 const int *gmpc_plugin_get_version(gmpcPluginParent
* plug
)
818 return (const int *)gmpc_plugin_base_get_version(plug
->new, &length
);
820 return (const int *)plug
->old
->version
;
823 int gmpc_plugin_get_type(gmpcPluginParent
* plug
)
827 return plug
->new->plugin_type
;
829 return plug
->old
->plugin_type
;
832 int gmpc_plugin_get_id(gmpcPluginParent
* plug
)
836 return plug
->new->id
;
838 return plug
->old
->id
;
841 gint
gmpc_plugin_tool_menu_integration(gmpcPluginParent
* plug
, GtkMenu
* menu
)
845 if (GMPC_PLUGIN_IS_TOOL_MENU_IFACE(plug
->new))
848 gmpc_plugin_tool_menu_iface_tool_menu_integration
849 (GMPC_PLUGIN_TOOL_MENU_IFACE(plug
->new), menu
);
855 if (plug
->old
->tool_menu_integration
)
856 return plug
->old
->tool_menu_integration(menu
);
861 gboolean
gmpc_plugin_has_enabled(gmpcPluginParent
* plug
)
867 if (plug
->old
&& plug
->old
->get_enabled
&& plug
->old
->set_enabled
)