diff -up logjam-4.5.3/configure.in.dbus logjam-4.5.3/configure.in --- logjam-4.5.3/configure.in.dbus 2009-04-22 23:46:38.000000000 +0300 +++ logjam-4.5.3/configure.in 2009-04-23 00:38:34.000000000 +0300 @@ -220,6 +220,13 @@ AC_SUBST(LIVEJOURNAL_CFLAGS) AC_SUBST(LIVEJOURNAL_LIBS) dnl end liblivejournal dependencies. +dnl D-Bus API +MODULES="dbus-1 dbus-glib-1" +PKG_CHECK_MODULES(DBUSAPI, $MODULES) +AC_SUBST(DBUSAPI_CFLAGS) +AC_SUBST(DBUSAPI_LIBS) +dnl D-Bus API + AM_CONDITIONAL(WITH_XMMS, test "$with_xmms" = "yes") AM_CONDITIONAL(WITH_HTML, test "$with_gtkhtml" != "no") AM_CONDITIONAL(WITH_RSVG, test "$with_librsvg" = "yes") diff -up logjam-4.5.3/src/conf.h.dbus logjam-4.5.3/src/conf.h --- logjam-4.5.3/src/conf.h.dbus 2009-04-22 23:46:38.000000000 +0300 +++ logjam-4.5.3/src/conf.h 2009-04-23 22:10:14.000000000 +0300 @@ -92,6 +92,7 @@ typedef struct { char *spawn_command; char *music_command; + gboolean music_mpris; char *proxy; char *proxyuser, *proxypass; diff -up logjam-4.5.3/src/conf_xml.c.dbus logjam-4.5.3/src/conf_xml.c --- logjam-4.5.3/src/conf_xml.c.dbus 2009-04-23 22:19:10.000000000 +0300 +++ logjam-4.5.3/src/conf_xml.c 2009-04-24 15:40:45.000000000 +0300 @@ -257,6 +257,7 @@ parseconf(xmlDocPtr doc, xmlNodePtr node XML_GET_CONF("proxyauth", parseproxyauth) XML_GET_STR("spawncommand", c->spawn_command) XML_GET_STR("musiccommand", c->music_command) + XML_GET_BOOL("musicmpris", c->music_mpris) #endif /* G_OS_WIN32 */ XML_GET_INT("cfuserinterval", c->cfuserinterval) XML_GET_INT("cfthreshold", c->cfthreshold) @@ -396,6 +397,8 @@ conf_write(Configuration *c, char *base) if (c->music_command) xmlAddTN(root, "musiccommand", c->music_command); + if (c->music_mpris) + xmlNewChild(root, NULL, BAD_CAST "musicmpris", NULL); #endif if (c->cfuserinterval) { diff -up logjam-4.5.3/src/init.c.dbus logjam-4.5.3/src/init.c --- logjam-4.5.3/src/init.c.dbus 2009-04-22 23:46:38.000000000 +0300 +++ logjam-4.5.3/src/init.c 2009-04-25 11:56:05.000000000 +0300 @@ -29,6 +29,7 @@ #include "conf_xml.h" #include "jamdoc.h" #include "cmdline.h" +#include "lj_dbus.h" #ifdef HAVE_GTK #include "login.h" @@ -120,6 +121,9 @@ init_app(int *argc, gchar *argv[]) { } #ifdef HAVE_GTK + +extern JamDBus *jdbus; + static void run_gtk(JamDoc *doc) { gchar *accelpath; @@ -148,8 +152,20 @@ run_gtk(JamDoc *doc) { jam_doc_set_account(doc, acc); } + jdbus = lj_dbus_new(); + if (conf.music_mpris) { + GError *error = NULL; + if (!lj_dbus_mpris_update_list(jdbus, &error)) { + g_printerr("Error: %s\n", error->message); + g_error_free(error); + } + } + jam_run(doc); + lj_dbus_close(jdbus); + jdbus = NULL; + g_object_unref(G_OBJECT(app.remote)); gtk_accel_map_save(accelpath); diff -up logjam-4.5.3/src/jamview.c.dbus logjam-4.5.3/src/jamview.c --- logjam-4.5.3/src/jamview.c.dbus 2009-04-25 13:39:45.000000000 +0300 +++ logjam-4.5.3/src/jamview.c 2009-04-25 14:27:56.000000000 +0300 @@ -20,6 +20,7 @@ #include "marshalers.h" #include "datesel.h" #include "tags.h" +#include "lj_dbus.h" #define KEY_PICTUREKEYWORD "logjam-picturekeyword" @@ -356,7 +357,12 @@ picture_store(JamView *view) { static void music_refresh_cb(JamView *view) { GError *err = NULL; - gchar *music = music_detect(&err); + gchar *music; + + if (conf.music_mpris) + music = lj_dbus_mpris_current_music(jdbus, &err); + else + music = music_detect(&err); if (music) { gtk_entry_set_text(GTK_ENTRY(view->music), music); @@ -373,7 +379,7 @@ static void music_add(JamView *view) { view->music = gtk_entry_new(); view->musicbar = labelled_box_new_sg(_("_Music:"), view->music, view->sizegroup); - if (music_can_detect(NULL)) { + if (conf.music_mpris || music_can_detect(NULL)) { GtkWidget *refresh = gtk_button_new_from_stock(GTK_STOCK_REFRESH); g_signal_connect_swapped(G_OBJECT(refresh), "clicked", G_CALLBACK(music_refresh_cb), view); diff -up logjam-4.5.3/src/lj_dbus.c.dbus logjam-4.5.3/src/lj_dbus.c --- logjam-4.5.3/src/lj_dbus.c.dbus 2009-04-25 13:40:31.000000000 +0300 +++ logjam-4.5.3/src/lj_dbus.c 2009-04-25 14:21:30.000000000 +0300 @@ -0,0 +1,244 @@ +/* logjam - a GTK client for LiveJournal. + * Copyright (C) 2009 Andy Shevchenko + * + * vim: tabstop=4 shiftwidth=4 noexpandtab : + * + * See http://wiki.xmms2.xmms.se/wiki/MPRIS for MRPIS specification + */ + +#include /* memset */ + +#include "lj_dbus.h" + +#define MPRIS_IF "org.freedesktop.MediaPlayer" + +/* Internal prototypes */ +static gboolean lj_dbus_open(JamDBus *jd); +static gboolean lj_dbus_append_player(JamDBus *jd, gchar *dest); +static void lj_dbus_players_clear(JamDBus *jd); +static gboolean lj_dbus_players_find(JamDBus *jd, GError **error); + +/* Implementation */ +static gboolean +lj_dbus_open(JamDBus *jd) { + GError *error = NULL; + + jd->bus = dbus_g_bus_get(DBUS_BUS_SESSION, &error); + if (jd->bus == NULL) { + g_printerr("Failed to open connection to bus: %s\n", error->message); + g_error_free(error); + return FALSE; + } + return TRUE; +} + +void lj_dbus_close(JamDBus *jd) { + if (jd == NULL) + return; + lj_dbus_players_clear(jd); + dbus_g_connection_unref(jd->bus); +} + +JamDBus * +lj_dbus_new(void) { + JamDBus *jd = (JamDBus *) g_malloc0(sizeof(JamDBus)); + + if (lj_dbus_open(jd) == FALSE) { + g_free(jd); + return NULL; + } + return jd; +} + +static gboolean +lj_dbus_append_player(JamDBus *jd, gchar *dest) { + MediaPlayer *player; + DBusGProxy *proxy; + GError *error = NULL; + gchar *name; + + proxy = dbus_g_proxy_new_for_name(jd->bus, dest, "/", MPRIS_IF); + + if (!dbus_g_proxy_call(proxy, "Identity", &error, G_TYPE_INVALID, + G_TYPE_STRING, &name, G_TYPE_INVALID)) { + g_printerr("Error: %s\n", error->message); + g_error_free(error); + return FALSE; + } + + player = (MediaPlayer *) g_malloc0(sizeof(MediaPlayer)); + player->dest = g_strdup(dest); + player->name = g_strdup(name); + player->proxy = dbus_g_proxy_new_for_name(jd->bus, dest, "/Player", MPRIS_IF); + + if (g_str_has_suffix(dest, "audacious")) + player->hint |= MPRIS_HINT_BAD_STATUS; + + jd->player = g_list_append(jd->player, (gpointer) player); + + g_free(name); + g_object_unref(proxy); + + return TRUE; +} + +static void +lj_dbus_players_clear(JamDBus *jd) { + GList *list; + + if (jd->player == NULL) + return; + + for (list = g_list_first(jd->player); list; list = g_list_next(list)) { + MediaPlayer *player = (MediaPlayer *) list->data; + g_object_unref(player->proxy); + if (player->name) + g_free(player->name); + if (player->dest) + g_free(player->dest); + } + g_list_free(jd->player); + jd->player = NULL; +} + +static gboolean +lj_dbus_players_find(JamDBus *jd, GError **error) { + DBusGProxy *proxy; + gchar **names, **p; + + proxy = dbus_g_proxy_new_for_name(jd->bus, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS); + + if (!dbus_g_proxy_call(proxy, "ListNames", error, G_TYPE_INVALID, + G_TYPE_STRV, &names, G_TYPE_INVALID)) { + return FALSE; + } + + for (p = names; *p; p++) { + if (g_str_has_prefix(*p, "org.mpris.")) { + lj_dbus_append_player(jd, *p); + } + } + + g_strfreev(names); + g_object_unref(proxy); + + return TRUE; +} + +gboolean +lj_dbus_mpris_update_list(JamDBus *jd, GError **error) { + if (jd == NULL) + return FALSE; + lj_dbus_players_clear(jd); + return lj_dbus_players_find(jd, error); +} + +#define DBUS_TYPE_MPRIS_STATUS \ + (dbus_g_type_get_struct("GValueArray", G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INVALID)) +#define DBUS_TYPE_G_STRING_VALUE_HASHTABLE \ + (dbus_g_type_get_map("GHashTable", G_TYPE_STRING, G_TYPE_VALUE)) + +/* TODO: Connect to status change signal */ + +gboolean +lj_dbus_mpris_update_info(JamDBus *jd, GList *list, GError **error) { + GValueArray *array = NULL; + GHashTable *info = NULL; + GValue *value; + MediaPlayer *player; + + if (jd == NULL) + return FALSE; + + if (list == NULL) + return FALSE; + + if ((player = (MediaPlayer *) list->data) == NULL) + return FALSE; + + memset((void *) &player->info, 0, sizeof(MetaInfo)); + + if (player->hint & MPRIS_HINT_BAD_STATUS) { + if (!dbus_g_proxy_call(player->proxy, "GetStatus", error, G_TYPE_INVALID, + G_TYPE_INT, &player->info.status, G_TYPE_INVALID)) { + return FALSE; + } + } else { + if (!dbus_g_proxy_call(player->proxy, "GetStatus", error, G_TYPE_INVALID, + DBUS_TYPE_MPRIS_STATUS, &array, G_TYPE_INVALID)) { + return FALSE; + } + + value = g_value_array_get_nth(array, 0); + player->info.status = g_value_get_int(value); + g_value_array_free(array); + } + + if (player->info.status == MPRIS_STATUS_PLAYING) { + if (!dbus_g_proxy_call(player->proxy, "GetMetadata", error, G_TYPE_INVALID, + DBUS_TYPE_G_STRING_VALUE_HASHTABLE, &info, G_TYPE_INVALID)) { + return FALSE; + } + value = (GValue *) g_hash_table_lookup(info, "artist"); + if (value != NULL && G_VALUE_HOLDS_STRING(value)) { + g_strlcpy(player->info.artist, g_value_get_string(value), MPRIS_INFO_LEN); + } + + value = (GValue *) g_hash_table_lookup(info, "album"); + if (value != NULL && G_VALUE_HOLDS_STRING(value)) { + g_strlcpy(player->info.album, g_value_get_string(value), MPRIS_INFO_LEN); + } + + value = (GValue *) g_hash_table_lookup(info, "title"); + if (value != NULL && G_VALUE_HOLDS_STRING(value)) { + g_strlcpy(player->info.title, g_value_get_string(value), MPRIS_INFO_LEN); + } + } + + return TRUE; +} + +GQuark +lj_dbus_error_quark(void) { + static GQuark quark = 0; + if (quark == 0) + quark = g_quark_from_static_string("dbus-error-quark"); + return quark; +} + +/* TODO: User defined format */ + +gchar * +lj_dbus_mpris_current_music(JamDBus *jd, GError **error) { + gchar *music; + GList *list; + + if (!lj_dbus_mpris_update_list(jd, error)) + return NULL; + + list = jd ? jd->player : NULL; + + if (lj_dbus_mpris_update_info(jd, list, error)) { + MediaPlayer *player = (MediaPlayer *) list->data; + if (player->info.status == MPRIS_STATUS_PLAYING) { + music = g_strdup_printf("%s - %s - %s", + player->info.artist[0] ? player->info.artist : _("Unknown Artist"), + player->info.album[0] ? player->info.album: _("Unknown Album"), + player->info.title[0] ? player->info.title: _("Unknown Track")); + return music; + } else { + g_set_error(error, lj_dbus_error_quark(), MPRIS_ERROR_NOT_PLAYING, + _("Player is stopped.")); + } + } else if (error == NULL || *error == NULL) { + g_set_error(error, lj_dbus_error_quark(), MPRIS_ERROR_NO_PLAYER, + _("No players found.")); + } + return NULL; +} + +/* lj_dbus.c */ + diff -up logjam-4.5.3/src/lj_dbus.h.dbus logjam-4.5.3/src/lj_dbus.h --- logjam-4.5.3/src/lj_dbus.h.dbus 2009-04-25 13:40:36.000000000 +0300 +++ logjam-4.5.3/src/lj_dbus.h 2009-04-25 14:25:27.000000000 +0300 @@ -0,0 +1,68 @@ +/* logjam - a GTK client for LiveJournal. + * Copyright (C) 2009 Andy Shevchenko + * + * vim: tabstop=4 shiftwidth=4 noexpandtab : + */ + +#ifndef lj_dbus_h +#define lj_dbus_h + +#include +#include +#include + +/* Take care about i18n strings */ +#ifndef _ +#ifdef GETTEXT_PACKAGE +#include +#else +#define _(x) x +#endif +#endif /* _ */ + +#define MPRIS_INFO_LEN 128 + +typedef struct _MetaInfo MetaInfo; +struct _MetaInfo { + gchar artist[MPRIS_INFO_LEN]; + gchar album[MPRIS_INFO_LEN]; + gchar title[MPRIS_INFO_LEN]; + +#define MPRIS_STATUS_PLAYING 0 +#define MPRIS_STATUS_PAUSED 1 +#define MPRIS_STATUS_STOPPED 2 + + gint status; +}; + +typedef struct _MediaPlayer MediaPlayer; +struct _MediaPlayer { + gchar *name; + gchar *dest; + DBusGProxy *proxy; + MetaInfo info; + +#define MPRIS_HINT_BAD_STATUS 1 << 0 + + gint hint; +}; + +typedef struct _JamDBus JamDBus; +struct _JamDBus { + DBusGConnection *bus; + GList *player; +}; + +void lj_dbus_close(JamDBus *jd); +JamDBus *lj_dbus_new(void); + +gboolean lj_dbus_mpris_update_list(JamDBus *jd, GError **error); +gboolean lj_dbus_mpris_update_info(JamDBus *jd, GList *list, GError **error); + +#define MPRIS_ERROR_NOT_PLAYING 1 +#define MPRIS_ERROR_NO_PLAYER 2 + +gchar *lj_dbus_mpris_current_music(JamDBus *jd, GError **error); + +#endif /* lj_dbus_h */ + diff -up logjam-4.5.3/src/Makefile.am.dbus logjam-4.5.3/src/Makefile.am --- logjam-4.5.3/src/Makefile.am.dbus 2009-04-23 00:44:11.000000000 +0300 +++ logjam-4.5.3/src/Makefile.am 2009-04-24 15:59:08.000000000 +0300 @@ -3,7 +3,7 @@ bin_PROGRAMS = logjam localedir=$(datadir)/locale # AM_CFLAGS = -DLOCALEDIR=\"$(localedir)\" -I$(top_srcdir)/protocol/liblivejournal -I$(top_srcdir)/protocol/blogger -I$(top_srcdir)/util/gxr @LOGJAM_CFLAGS@ @NETWORK_CFLAGS@ -AM_CFLAGS = -DLOCALEDIR=\"$(localedir)\" -I$(top_srcdir)/protocol/liblivejournal -I$(top_srcdir)/util/gxr @LOGJAM_CFLAGS@ @NETWORK_CFLAGS@ +AM_CFLAGS = -DLOCALEDIR=\"$(localedir)\" -I$(top_srcdir)/protocol/liblivejournal -I$(top_srcdir)/util/gxr @LOGJAM_CFLAGS@ @NETWORK_CFLAGS@ @DBUSAPI_CFLAGS@ ACCOUNT = account.h account.c host.c livejournal.c # blogger.c @@ -50,6 +50,7 @@ src_gtk = gtk-all.h util-gtk.c util-gtk. settings.c settings.h $(GROUPEDBOX) tie.c tie.h \ $(HISTORY) $(OFFLINE) \ jam.c jam.h $(USEJOURNAL) \ + lj_dbus.c lj_dbus.h \ security.c security.h about.c thanks.h \ menu.c menu.h $(TOOLS) smartquotes.c smartquotes.h \ $(LOGIN) manager.c \ @@ -116,7 +117,7 @@ logjam_SOURCES += journalstore-xml.c endif # logjam_LDADD=$(top_builddir)/protocol/liblivejournal/livejournal/liblivejournal.la $(top_builddir)/protocol/blogger/libblogger.la @LOGJAM_LIBS@ @NETWORK_LIBS@ -logjam_LDADD=$(top_builddir)/protocol/liblivejournal/livejournal/liblivejournal.la @LOGJAM_LIBS@ @NETWORK_LIBS@ +logjam_LDADD=$(top_builddir)/protocol/liblivejournal/livejournal/liblivejournal.la @LOGJAM_LIBS@ @NETWORK_LIBS@ @DBUSAPI_LIBS@ ctags: @version=`ctags --version | head -1`; \ diff -up logjam-4.5.3/src/music.c.dbus logjam-4.5.3/src/music.c --- logjam-4.5.3/src/music.c.dbus 2009-04-22 23:46:38.000000000 +0300 +++ logjam-4.5.3/src/music.c 2009-04-25 01:11:29.000000000 +0300 @@ -9,8 +9,11 @@ #include "gtk-all.h" #include "conf.h" +#include "lj_dbus.h" #include "music.h" +JamDBus *jdbus = NULL; + const CommandList music_commands[] = { { N_("None"), NULL }, { "Music Player Daemon", "sh -c \"mpc | grep -v '^volume: .* repeat: .* random: .*'\"" }, diff -up logjam-4.5.3/src/music.h.dbus logjam-4.5.3/src/music.h --- logjam-4.5.3/src/music.h.dbus 2005-02-21 20:30:27.000000000 +0200 +++ logjam-4.5.3/src/music.h 2009-04-25 01:05:55.000000000 +0300 @@ -8,6 +8,9 @@ #define music_h #include "conf.h" /* CommandList */ +#include "lj_dbus.h" + +extern JamDBus *jdbus; typedef enum { MUSIC_SOURCE_NONE, diff -up logjam-4.5.3/src/settings.c.dbus logjam-4.5.3/src/settings.c --- logjam-4.5.3/src/settings.c.dbus 2009-04-25 13:41:03.000000000 +0300 +++ logjam-4.5.3/src/settings.c 2009-04-25 15:19:42.000000000 +0300 @@ -24,6 +24,7 @@ #include "tie.h" #include "account.h" #include "jamview.h" +#include "lj_dbus.h" /* what's this? all of these funny structures in the settings box? * well, instead of creating and tearing down all of these widgets, i @@ -101,6 +102,8 @@ static SettingsWidget settingswidgets[] { "music_command", &conf.music_command, SW_COMMAND, N_("Detect music from:"), (gpointer)music_commands }, + { "music_mpris", &conf.music_mpris, + SW_TOGGLE, N_("Detect music via MPRIS") }, { "net_useproxy", &conf.options.useproxy, SW_TOGGLE, N_("Use _proxy server") }, @@ -169,6 +172,19 @@ toggle_tie_enable(GtkWidget *toggle, Gtk } static void +toggle_disable_cb(GtkWidget *toggle, GtkWidget *target) { + gtk_widget_set_sensitive(target, + !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle))); +} +static void +toggle_tie_disable(GtkWidget *toggle, GtkWidget *target) { + gtk_widget_set_sensitive(target, + !gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(toggle))); + g_signal_connect(G_OBJECT(toggle), "toggled", + G_CALLBACK(toggle_disable_cb), target); +} + +static void toggle_tie(SettingsWidget *sw) { tie_toggle(GTK_TOGGLE_BUTTON(sw->widget), (gboolean*)sw->conf); } @@ -499,16 +515,21 @@ static void music_diagnose(GtkWidget *button) { GtkWindow *dlg = GTK_WINDOW(gtk_widget_get_toplevel(button)); GError *err = NULL; - char *music, *result; - MusicSource source = music_current_source(); + char *music = NULL, *result; + MusicSource source = MUSIC_SOURCE_NONE; - if (!music_can_detect(&err)) { - jam_warning(dlg, "%s", err->message); - g_error_free(err); - return; + if (conf.music_mpris) { + music = lj_dbus_mpris_current_music(jdbus, &err); + } else { + source = music_current_source(); + if (!music_can_detect(&err)) { + jam_warning(dlg, "%s", err->message); + g_error_free(err); + return; + } + music = music_detect(&err); } - music = music_detect(&err); if (!music) { if (source == MUSIC_SOURCE_XMMS && err->domain == G_SPAWN_ERROR && err->code == G_SPAWN_ERROR_NOENT) { @@ -555,21 +576,40 @@ proxysettings(void) { return group; } +static void +mpris_update_cb(GtkToggleButton *button, JamDBus *jd) { + GError *error = NULL; + if (!lj_dbus_mpris_update_list(jdbus, &error)) { + GtkWindow *dlg = GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(button))); + jam_warning(dlg, _("Error detecting music: %s"), error->message); + g_error_free(error); + } +} + static GtkWidget* programsettings(JamWin *jw) { GtkWidget *group; - GtkWidget *button, *hbox; + GtkWidget *button, *vbox, *hbox; SettingsWidget *sw; JamView *jv = jam_win_get_cur_view(jw); group = groupedbox_new_with_text(_("Programs")); groupedbox_pack(GROUPEDBOX(group), sw_make("web_spawn"), FALSE); - groupedbox_pack(GROUPEDBOX(group), sw_make("music_command"), FALSE); + vbox = sw_make("music_command"); + groupedbox_pack(GROUPEDBOX(group), vbox, FALSE); sw = sw_lookup("music_command"); g_signal_connect_swapped(G_OBJECT(sw->widget), "changed", G_CALLBACK(jam_view_settings_changed), jv); + button = sw_make("music_mpris"); + groupedbox_pack(GROUPEDBOX(group), button, FALSE); + g_signal_connect(G_OBJECT(button), "toggled", + G_CALLBACK(mpris_update_cb), jdbus); + toggle_tie_disable(button, vbox); + g_signal_connect_swapped(G_OBJECT(button), "toggled", + G_CALLBACK(jam_view_settings_changed), jv); + button = gtk_button_new_with_mnemonic(_("_Diagnose")); g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(music_diagnose), NULL);