diff --git a/TODO b/TODO index 4844afe..e0f40ae 100644 --- a/TODO +++ b/TODO @@ -9,24 +9,22 @@ Consider the following order to implement this todo: - window geometry - volume - repeat and shuffle states - 3. Single instance with dbus: - - just get a name - 4. Create preferences infrastructure: + 3. Create preferences infrastructure: - new elm_layout style (in Elementary) for preferences - choose media backend (xine, gstreamer) - 5. Create library manager preferences: + 4. Create library manager preferences: - add/remove directories (or at least choose folder) - rescan, stop-scan. with progress feedback - option to choose rescan schedule - remove command line scan - 6. Refactor some features into plugins + 5. Refactor some features into plugins - define ecore_events and plugin interface - coverart-lastfm - cover art local fs lookup - 7. Modular plugins (elm_module) + 6. Modular plugins (elm_module) - selection saved in eet - gui to select enabled plugins - 8. Save/Resume between runs (list, nowplaying and navigation) + 7. Save/Resume between runs (list, nowplaying and navigation) Library (from "Preferences") @@ -90,8 +88,6 @@ Work to cover the above cases: More ---- - * Single instance using dbus (otherwise configuration will be screwed with - multiple processes writing it); * Save/Resume state between runs (start from the same place as last time); - window geometry - volume diff --git a/configure.ac b/configure.ac index 4324570..859e521 100644 --- a/configure.ac +++ b/configure.ac @@ -43,6 +43,9 @@ VMIC=`echo $PACKAGE_VERSION | awk -F. '{printf("%s", $3);}'` SNAP=`echo $PACKAGE_VERSION | awk -F. '{printf("%s", $4);}'` version_info=`expr $VMAJ + $VMIN`":$VMIC:$VMIN" m4_ifdef([v_rev], , [m4_define([v_rev], [0])]) +AC_DEFINE_UNQUOTED(VMAJ, [v_maj], [Major version]) +AC_DEFINE_UNQUOTED(VMIN, [v_min], [Minor version]) +AC_DEFINE_UNQUOTED(VMIC, [v_mic], [Micro version]) AC_DEFINE_UNQUOTED(VREV, [v_rev], [Revison]) AC_SUBST(VMAJ) AC_SUBST(version_info) diff --git a/src/bin/Makefile.am b/src/bin/Makefile.am index 4f23aa1..dd25673 100644 --- a/src/bin/Makefile.am +++ b/src/bin/Makefile.am @@ -20,7 +20,7 @@ endif enjoy_LDADD = @ELEMENTARY_LIBS@ @EMOTION_LIBS@ @LMS_LIBS@ @SQLITE3_LIBS@ @EDBUS_LIBS@ enjoy_CFLAGS = -rdynamic -enjoy_SOURCES = main.c win.c db.c list.c page.c cover.c nowplaying.c libmanager.c coverart-lastfm.c +enjoy_SOURCES = main.c win.c db.c list.c page.c cover.c nowplaying.c libmanager.c coverart-lastfm.c dbus.c if BUILD_QUICKLAUNCH ############################################################################ diff --git a/src/bin/coverart-lastfm.c b/src/bin/coverart-lastfm.c index f0c71e4..5b291f9 100644 --- a/src/bin/coverart-lastfm.c +++ b/src/bin/coverart-lastfm.c @@ -26,6 +26,7 @@ struct _LastFM_Cover_Request { }; static char *_local_cache_dir = NULL; +static Eina_Bool _lastfm_inited = EINA_FALSE; /* * This API Key belongs to leandro@profusion.mobi -- do not use it for commercial @@ -281,6 +282,9 @@ lastfm_cover_init(void) { int i; + if (_lastfm_inited) return; + _lastfm_inited = EINA_TRUE; + ecore_init(); ecore_file_init(); ecore_con_url_init(); @@ -320,6 +324,7 @@ lastfm_cover_shutdown(void) { int i; + if (!_lastfm_inited) return; for (i = 0; disc_number_regexes[i].str; i++) { regfree(disc_number_regexes[i].exp); diff --git a/src/bin/dbus.c b/src/bin/dbus.c new file mode 100644 index 0000000..e3900a7 --- /dev/null +++ b/src/bin/dbus.c @@ -0,0 +1,135 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "private.h" +#include + +#define DBUS_NAME "org.enlightenment.enjoy" +#define DBUS_IFACE "org.enlightenment.enjoy.Control" +#define DBUS_PATH "/org/enlightenment/enjoy/Control" + +static E_DBus_Connection *conn = NULL; +static E_DBus_Object *dbus_obj = NULL; +static E_DBus_Interface *dbus_iface = NULL; + +typedef struct _Enjoy_DBus_Method Enjoy_DBus_Method; +struct _Enjoy_DBus_Method { + const char *name; + const char *par; + const char *ret; + E_DBus_Method_Cb cb; +}; + +static DBusMessage * +_cb_dbus_quit(E_DBus_Object *obj __UNUSED__, DBusMessage *msg) +{ + enjoy_quit(); + return dbus_message_new_method_return(msg); +} + +static DBusMessage * +_cb_dbus_version(E_DBus_Object *obj __UNUSED__, DBusMessage *msg) +{ + DBusMessage *reply = dbus_message_new_method_return(msg); + DBusMessageIter iter, siter; + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_open_container(&iter, DBUS_TYPE_STRUCT, NULL, &siter); + +#define APPEND_UINT16(val) \ + do { \ + unsigned short _tmp_val = val; \ + dbus_message_iter_append_basic(&siter, DBUS_TYPE_UINT16, &_tmp_val); \ + } while (0) + APPEND_UINT16(VMAJ); + APPEND_UINT16(VMIN); + APPEND_UINT16(VMIC); +#undef APPEND_UINT16 + + dbus_message_iter_close_container(&iter, &siter); + return reply; +} + +/* Avoid duplicating MPRIS -- see src/plugins/mpris */ +static const Enjoy_DBus_Method control_methods[] = { + {"Quit", "", "", _cb_dbus_quit}, + {"Version", "", "(qqq)", _cb_dbus_version}, + /* TODO: DB management */ + {NULL, NULL, NULL, NULL} +}; + +static void +_dbus_methods_add(E_DBus_Interface *iface, const Enjoy_DBus_Method desc[]) +{ + const Enjoy_DBus_Method *itr = desc; + for (; itr->name; itr++) + e_dbus_interface_method_add(iface, itr->name, itr->par, itr->ret, itr->cb); +} + +static void +_cb_dbus_request_name(void *data __UNUSED__, DBusMessage *msg, DBusError *err) +{ + DBusError new_err; + dbus_uint32_t msgtype; + E_DBus_Interface *iface; + + if (dbus_error_is_set(err)) + { + ERR("Could not get DBus name: %s", err->message); + goto error; + } + + dbus_error_init(&new_err); + dbus_message_get_args + (msg, &new_err, DBUS_TYPE_UINT32, &msgtype, DBUS_TYPE_INVALID); + if (msgtype != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) + { + ERR("Could not get the DBus name: reply=%d", msgtype); + goto error; + } + + INF("Got DBus name - unique instance running."); + + dbus_obj = e_dbus_object_add(conn, DBUS_PATH, NULL); + if (!dbus_obj) + { + ERR("Could not create Control DBus object."); + goto error; + } + dbus_iface = e_dbus_interface_new(DBUS_IFACE); + e_dbus_object_interface_attach(dbus_obj, dbus_iface); + _dbus_methods_add(dbus_iface, control_methods); + + /* will run after other events run, in the main loop */ + ecore_event_add(ENJOY_EVENT_STARTED, NULL, NULL, NULL); + return; + + error: + ecore_main_loop_quit(); + return; +} + +Eina_Bool +enjoy_dbus_init(void) +{ + e_dbus_init(); + conn = e_dbus_bus_get(DBUS_BUS_SESSION); + if (!conn) + { + ERR("Could not get DBus session bus"); + return EINA_FALSE; + } + + e_dbus_request_name + (conn, DBUS_NAME, DBUS_NAME_FLAG_DO_NOT_QUEUE, + _cb_dbus_request_name, NULL); +} + +void +enjoy_dbus_shutdown(void) +{ + if (dbus_obj) e_dbus_object_free(dbus_obj); + if (dbus_iface) e_dbus_interface_unref(dbus_iface); + conn = NULL; + e_dbus_shutdown(); +} diff --git a/src/bin/main.c b/src/bin/main.c index 31c8fe6..830761b 100644 --- a/src/bin/main.c +++ b/src/bin/main.c @@ -319,6 +319,7 @@ enjoy_module_load(void) static void enjoy_module_unload(void) { + if (!app.modules) return; while (eina_array_count_get(app.modules)) eina_module_unload(eina_array_pop(app.modules)); eina_array_free(app.modules); @@ -370,6 +371,17 @@ _cb_started(void *data __UNUSED__, int type __UNUSED__, void *event __UNUSED__) { Enjoy_Plugin *p; + app.win = win_new(&app); + if (!app.win) + { + ERR("Could not create main window"); + enjoy_quit(); + return ECORE_CALLBACK_PASS_ON; + } + + cover_init(); + enjoy_module_load(); + enjoy_plugins_walk(); EINA_INLIST_FOREACH(plugins_registry, p) enjoy_plugin_enable(p); @@ -378,6 +390,35 @@ _cb_started(void *data __UNUSED__, int type __UNUSED__, void *event __UNUSED__) return ECORE_CALLBACK_PASS_ON; } +static DBusMessage * +_cb_dbus_quit(E_DBus_Object *obj __UNUSED__, DBusMessage *msg) +{ + enjoy_quit(); + return dbus_message_new_method_return(msg); +} + +static DBusMessage * +_cb_dbus_version(E_DBus_Object *obj __UNUSED__, DBusMessage *msg) +{ + DBusMessage *reply = dbus_message_new_method_return(msg); + DBusMessageIter iter, siter; + dbus_message_iter_init_append(reply, &iter); + dbus_message_iter_open_container(&iter, DBUS_TYPE_STRUCT, NULL, &siter); + +#define APPEND_UINT16(val) \ + do { \ + unsigned short _tmp_val = val; \ + dbus_message_iter_append_basic(&siter, DBUS_TYPE_UINT16, &_tmp_val); \ + } while (0) + APPEND_UINT16(VMAJ); + APPEND_UINT16(VMIN); + APPEND_UINT16(VMIC); +#undef APPEND_UINT16 + + dbus_message_iter_close_container(&iter, &siter); + return reply; +} + EAPI int elm_main(int argc, char **argv) { @@ -396,6 +437,8 @@ elm_main(int argc, char **argv) ECORE_GETOPT_VALUE_NONE }; + memset(&app, 0, sizeof(app)); + #if ENABLE_NLS setlocale(LC_ALL, ""); bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR); @@ -442,28 +485,31 @@ elm_main(int argc, char **argv) goto end; } - app.win = win_new(&app); - if (!app.win) goto end; - - cover_init(); enjoy_event_id_init(); ecore_event_handler_add(ENJOY_EVENT_STARTED, _cb_started, NULL); - enjoy_module_load(); + /* will call ENJOY_EVENT_STARTED whenever it's ready */ + if (!enjoy_dbus_init()) + { + ERR("Could not start Enjoy's DBus subsystem"); + r = -1; + goto end; + } - /* will run after other events run, in the main loop */ - ecore_event_add(ENJOY_EVENT_STARTED, NULL, NULL, NULL); elm_run(); end: EINA_LIST_FREE(app.add_dirs, s) free(s); EINA_LIST_FREE(app.del_dirs, s) free(s); + enjoy_module_unload(); + cover_shutdown(); + + enjoy_dbus_shutdown(); + eina_log_domain_unregister(_log_domain); _log_domain = -1; elm_shutdown(); - enjoy_module_unload(); - cover_shutdown(); return r; } diff --git a/src/bin/private.h b/src/bin/private.h index 8577b55..a46df6e 100644 --- a/src/bin/private.h +++ b/src/bin/private.h @@ -181,5 +181,7 @@ void db_nameid_free(NameID *nameid); Eina_Bool enjoy_plugin_enable(Enjoy_Plugin *p); Eina_Bool enjoy_plugin_disable(Enjoy_Plugin *p); +Eina_Bool enjoy_dbus_init(void); +void enjoy_dbus_shutdown(void); #endif diff --git a/src/plugins/mpris/mpris.c b/src/plugins/mpris/mpris.c index e1bad89..ed20a20 100644 --- a/src/plugins/mpris/mpris.c +++ b/src/plugins/mpris/mpris.c @@ -244,7 +244,8 @@ mpris_enable(Enjoy_Plugin *p __UNUSED__) _cb_player_tracklist_change, NULL); #undef EV_HANDLER - e_dbus_request_name(conn, APPLICATION_NAME, 0, _cb_dbus_request_name, NULL); + e_dbus_request_name(conn, APPLICATION_NAME, DBUS_NAME_FLAG_DO_NOT_QUEUE, + _cb_dbus_request_name, NULL); return EINA_TRUE; } @@ -337,10 +338,11 @@ _cb_dbus_request_name(void *data __UNUSED__, DBusMessage *msg, DBusError *err) { DBusError new_err; dbus_uint32_t msgtype; - + int i; + if (dbus_error_is_set(err)) { - dbus_error_free(err); + ERR("Could not get DBus name: %s", err->message); return; } @@ -355,33 +357,32 @@ _cb_dbus_request_name(void *data __UNUSED__, DBusMessage *msg, DBusError *err) e_dbus_object_free(bus_obj); bus_obj = NULL; } - + dbus_error_init(&new_err); - dbus_message_get_args(msg, &new_err, DBUS_TYPE_UINT32, &msgtype, DBUS_TYPE_INVALID); - - if (msgtype == DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER || - msgtype == DBUS_REQUEST_NAME_REPLY_ALREADY_OWNER) + dbus_message_get_args + (msg, &new_err, DBUS_TYPE_UINT32, &msgtype, DBUS_TYPE_INVALID); + if (msgtype != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) { - int i; - - interface_list = eina_hash_string_small_new((Eina_Free_Cb)e_dbus_interface_unref); - - for (i = 0; object_list[i]; i++) - { - bus_obj = e_dbus_object_add(conn, object_list[i], NULL); - E_DBus_Interface *interface = e_dbus_interface_new(PLAYER_INTERFACE_NAME); - e_dbus_object_interface_attach(bus_obj, interface); - - eina_hash_add(interface_list, object_list[i], interface); - } - - _mpris_signals_add(PLAYER_NAME, mpris_player_signals); - _mpris_signals_add(TRACKLIST_NAME, mpris_tracklist_signals); - - _mpris_methods_add(ROOT_NAME, mpris_root_methods); - _mpris_methods_add(PLAYER_NAME, mpris_player_methods); - _mpris_methods_add(TRACKLIST_NAME, mpris_tracklist_methods); + ERR("Could not get the DBus name: reply=%d", msgtype); + return; } + + interface_list = eina_hash_string_small_new + ((Eina_Free_Cb)e_dbus_interface_unref); + for (i = 0; object_list[i]; i++) + { + bus_obj = e_dbus_object_add(conn, object_list[i], NULL); + E_DBus_Interface *iface = e_dbus_interface_new(PLAYER_INTERFACE_NAME); + e_dbus_object_interface_attach(bus_obj, iface); + eina_hash_add(interface_list, object_list[i], iface); + } + + _mpris_signals_add(PLAYER_NAME, mpris_player_signals); + _mpris_signals_add(TRACKLIST_NAME, mpris_tracklist_signals); + + _mpris_methods_add(ROOT_NAME, mpris_root_methods); + _mpris_methods_add(PLAYER_NAME, mpris_player_methods); + _mpris_methods_add(TRACKLIST_NAME, mpris_tracklist_methods); } static void