changeset 3344:c03055ef8822 trunk

-Implemented /Player GetStatus, GetMetadata, GetCaps, PositionGet, PositionSet, VolumeGet, and VolumeSet -Implemented /TrackList Loop and GetMetadata -Fixed /TrackList Random so that it toggles shuffle mode based on the passed argument instead of the current state -Modified /TrackList AddTrack so that it will not start playing if the add failed (pending mods to playlist.c by yaz)
author Ben Tucker <ben.tucker@gmail.com>
date Sun, 12 Aug 2007 13:26:02 -0700
parents 904c58081f0a
children 67951f8f9d83
files src/audacious/dbus-service.h src/audacious/dbus.c
diffstat 2 files changed, 217 insertions(+), 48 deletions(-) [+]
line wrap: on
line diff
--- a/src/audacious/dbus-service.h	Sat Aug 11 22:17:13 2007 -0700
+++ b/src/audacious/dbus-service.h	Sun Aug 12 13:26:02 2007 -0700
@@ -28,7 +28,7 @@
 
 typedef struct {
     GObject parent;
-	DBusGProxy *proxy;
+    DBusGProxy *proxy;
 } RemoteObject, MprisRoot, MprisPlayer, MprisTrackList;
 
 typedef struct {
@@ -39,9 +39,19 @@
 void free_dbus();
 DBusGProxy *audacious_get_dbus_proxy();
 
-///////////////////////////
-// MPRIS defined methods //
-///////////////////////////
+/* MPRIS API */
+// Capabilities
+enum {
+    MPRIS_CAPS_NONE                     = 0,
+    MPRIS_CAPS_CAN_GO_NEXT              = 1 << 0,
+    MPRIS_CAPS_CAN_GO_PREV              = 1 << 1,
+    MPRIS_CAPS_CAN_PAUSE                = 1 << 2,
+    MPRIS_CAPS_CAN_PLAY                 = 1 << 3,
+    MPRIS_CAPS_CAN_SEEK                 = 1 << 4,
+    MPRIS_CAPS_CAN_PROVIDE_METADATA     = 1 << 5,
+    MPRIS_CAPS_PROVIDES_TIMING          = 1 << 6,
+};
+
 // MPRIS /
 gboolean mpris_root_identity(MprisRoot *obj, gchar **identity,
                              GError **error);
@@ -55,8 +65,8 @@
 gboolean mpris_player_quit(MprisPlayer *obj, GError **error);
 gboolean mpris_player_get_status(MprisPlayer *obj, gint *status,
                                  GError **error);
-gboolean mpris_player_get_metadata(MprisTrackList *obj, gint pos,
-                                   GHashTable *metadata, GError **error);
+gboolean mpris_player_get_metadata(MprisPlayer *obj, GHashTable **metadata,
+                                   GError **error);
 gboolean mpris_player_get_caps(MprisPlayer *obj, gint *capabilities,
                                  GError **error);
 gboolean mpris_player_volume_set(MprisPlayer *obj, gint vol, GError **error);
@@ -77,7 +87,7 @@
 
 // MPRIS /TrackList
 gboolean mpris_tracklist_get_metadata(MprisTrackList *obj, gint pos,
-                                      GHashTable *metadata, GError **error);
+                                      GHashTable **metadata, GError **error);
 gboolean mpris_tracklist_get_current_track(MprisTrackList *obj, gint *pos,
                                            GError **error);
 gboolean mpris_tracklist_get_length(MprisTrackList *obj, gint *length,
@@ -91,7 +101,7 @@
 gboolean mpris_tracklist_random(MprisTrackList *obj, gboolean random,
                                 GError **error);
 
-
+/* Legacy API */
 // Audacious General Information
 gboolean audacious_rc_version(RemoteObject *obj, gchar **version,
                               GError **error);
--- a/src/audacious/dbus.c	Sat Aug 11 22:17:13 2007 -0700
+++ b/src/audacious/dbus.c	Sun Aug 12 13:26:02 2007 -0700
@@ -28,6 +28,7 @@
 #include "dbus-service.h"
 #include "dbus-server-bindings.h"
 
+#include <math.h>
 #include "main.h"
 #include "ui_equalizer.h"
 #include "ui_main.h"
@@ -49,6 +50,8 @@
 G_DEFINE_TYPE(MprisPlayer, mpris_player, G_TYPE_OBJECT);
 G_DEFINE_TYPE(MprisTrackList, mpris_tracklist, G_TYPE_OBJECT);
 
+#define DBUS_TYPE_G_STRING_VALUE_HASHTABLE (dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE))
+
 void audacious_rc_class_init(RemoteObjectClass *klass) {}
 void mpris_root_class_init(MprisRootClass *klass) {}
 
@@ -59,24 +62,24 @@
                 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
                 0,
                 NULL, NULL,
-                g_cclosure_marshal_VOID__STRING,
-                G_TYPE_NONE, 1, G_TYPE_STRING);
+                g_cclosure_marshal_VOID__INT,
+                G_TYPE_NONE, 1, G_TYPE_INT);
     signals[TRACK_CHANGE_SIG] =
         g_signal_new("track_change",
             G_OBJECT_CLASS_TYPE(klass),
                 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
                 0,
                 NULL, NULL,
-                g_cclosure_marshal_VOID__STRING,
-                G_TYPE_NONE, 1, G_TYPE_STRING);
+                g_cclosure_marshal_VOID__BOXED,
+                G_TYPE_NONE, 1, DBUS_TYPE_G_STRING_VALUE_HASHTABLE);
     signals[STATUS_CHANGE_SIG] =
         g_signal_new("status_change",
             G_OBJECT_CLASS_TYPE(klass),
                 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
                 0,
                 NULL, NULL,
-                g_cclosure_marshal_VOID__STRING,
-                G_TYPE_NONE, 1, G_TYPE_STRING);
+                g_cclosure_marshal_VOID__INT,
+                G_TYPE_NONE, 1, G_TYPE_INT);
 }
 
 void mpris_tracklist_class_init(MprisTrackListClass *klass) {}
@@ -84,7 +87,7 @@
 void audacious_rc_init(RemoteObject *object) {
     GError *error = NULL;
     DBusGProxy *driver_proxy;
-    unsigned int request_ret;
+    guint request_ret;
 
     
     dbus_g_object_type_install_info(audacious_rc_get_type(),
@@ -163,9 +166,62 @@
     g_message("D-Bus support has been activated");
 }
 
-///////////////////////////
-// MPRIS defined methods //
-///////////////////////////
+GValue *tuple_value_to_gvalue(Tuple *tuple, const gchar *key) {
+    GValue *val;
+    TupleValueType type;
+    type = tuple_get_value_type(tuple, key);
+    if (type == TUPLE_STRING) {
+        val = g_new0(GValue, 1);
+        g_value_init(val, G_TYPE_STRING);
+        g_value_set_string(val, tuple_get_string(tuple, key));
+        return val;
+    } else if (type == TUPLE_INT) {
+        val = g_new0(GValue, 1);
+        g_value_init(val, G_TYPE_INT);
+        g_value_set_int(val, tuple_get_int(tuple, key));
+        return val;
+    }
+    return NULL;
+}
+
+GHashTable *mpris_metadata_from_tuple(Tuple *tuple) {
+    GHashTable *md = NULL;
+    GValue *value;
+
+    if (tuple == NULL)
+        return NULL;
+
+    md = g_hash_table_new(g_str_hash, g_str_equal);
+
+    value = tuple_value_to_gvalue(tuple, "length");
+    if (value != NULL) {
+        g_hash_table_insert(md, "length", value);
+    }
+
+    value = tuple_value_to_gvalue(tuple, "title");
+    if (value != NULL) {
+        g_hash_table_insert(md, "name", value);
+    }
+
+    value = tuple_value_to_gvalue(tuple, "artist");
+    if (value != NULL) {
+        g_hash_table_insert(md, "artist", value);
+    }
+
+    value = tuple_value_to_gvalue(tuple, "album");
+    if (value != NULL) {
+        g_hash_table_insert(md, "album", value);
+    }
+
+    value = tuple_value_to_gvalue(tuple, "genre");
+    if (value != NULL) {
+        g_hash_table_insert(md, "genre", value);
+    }
+
+    return md;
+}
+
+/* MPRIS API */
 // MPRIS /
 gboolean mpris_root_identity(MprisRoot *obj, gchar **identity,
                              GError **error) {
@@ -175,26 +231,26 @@
 
 // MPRIS /Player
 gboolean mpris_player_next(MprisPlayer *obj, GError **error) {
-	playlist_next(playlist_get_active());
+    playlist_next(playlist_get_active());
     return TRUE;
 }
 gboolean mpris_player_prev(MprisPlayer *obj, GError **error) {
-	playlist_prev(playlist_get_active());
+    playlist_prev(playlist_get_active());
     return TRUE;
 }
 gboolean mpris_player_pause(MprisPlayer *obj, GError **error) {
-	playback_pause();
+    playback_pause();
     return TRUE;
 }
 gboolean mpris_player_stop(MprisPlayer *obj, GError **error) {
-	ip_data.stop = TRUE;
+    ip_data.stop = TRUE;
     playback_stop();
     ip_data.stop = FALSE;
     mainwin_clear_song_info();
     return TRUE;
 }
 gboolean mpris_player_play(MprisPlayer *obj, GError **error) {
-	if (playback_get_paused())
+    if (playback_get_paused())
         playback_pause();
     else if (playlist_get_length(playlist_get_active()))
         playback_initiate();
@@ -208,39 +264,111 @@
     return TRUE;
 }
 gboolean mpris_player_quit(MprisPlayer *obj, GError **error) {
-	mainwin_quit_cb();
+    mainwin_quit_cb();
     return TRUE;
 }
 gboolean mpris_player_get_status(MprisPlayer *obj, gint *status,
                                  GError **error) {
-    return FALSE;
+    // check paused before playing because playback_get_playing() is true when
+    // paused as well as when playing
+    if (playback_get_paused())
+        *status = 1;
+    else if (playback_get_playing())
+        *status = 0;
+    else
+        *status = 2;
+    return TRUE;
 }
-gboolean mpris_player_get_metadata(MprisTrackList *obj, gint pos,
-                                   GHashTable *metadata, GError **error) {
-	return FALSE;
+gboolean mpris_player_get_metadata(MprisPlayer *obj, GHashTable **metadata,
+                                   GError **error) {
+    GHashTable *md = NULL;
+    Tuple *tuple = NULL;
+    GValue *value;
+    Playlist *active;
+
+    active = playlist_get_active();
+    gint pos = playlist_get_position(active);
+    tuple = playlist_get_tuple(active, pos);
+
+    md = mpris_metadata_from_tuple(tuple);
+
+    if (md == NULL) {
+        // there's no metadata for this track
+        return TRUE;
+    }
+
+    // Song URI
+    value = g_new0(GValue, 1);
+    g_value_init(value, G_TYPE_STRING);
+    g_value_set_string(value, playlist_get_filename(active, pos));
+
+    g_hash_table_insert(md, "URI", value);
+
+    *metadata = md;
+
+    return TRUE;
 }
 gboolean mpris_player_get_caps(MprisPlayer *obj, gint *capabilities,
                                  GError **error) {
-    return FALSE;
+    *capabilities = MPRIS_CAPS_CAN_GO_NEXT |
+        MPRIS_CAPS_CAN_GO_PREV |
+        MPRIS_CAPS_CAN_PAUSE |
+        MPRIS_CAPS_CAN_PLAY |
+        MPRIS_CAPS_CAN_SEEK |
+        MPRIS_CAPS_CAN_PROVIDE_METADATA |
+        MPRIS_CAPS_PROVIDES_TIMING;
+    return TRUE;
 }
 gboolean mpris_player_volume_set(MprisPlayer *obj, gint vol, GError **error) {
-    return FALSE;
+    gint vl, vr, v;
+
+    // get the current volume so we can maintain the balance
+    input_get_volume(&vl, &vr);
+
+    // sanity check
+    vl = CLAMP(vl, 0, 100);
+    vr = CLAMP(vr, 0, 100);
+    v = CLAMP(vol, 0, 100);
+
+    if (vl > vr) {
+        input_set_volume(v, (gint) rint(((gdouble) vr / vl) * v));
+    } else if (vl < vr) {
+        input_set_volume((gint) rint(((gdouble) vl / vr) * v), v);
+    } else {
+        input_set_volume(v, v);
+    }
+    return TRUE;
 }
 gboolean mpris_player_volume_get(MprisPlayer *obj, gint *vol,
                                  GError **error) {
-    return FALSE;
+    gint vl, vr;
+    input_get_volume(&vl, &vr);
+    // vl and vr may be different depending on the balance; the true volume is
+    // the maximum of vl or vr.
+    *vol = MAX(vl, vr);
+    return TRUE;
 }
 gboolean mpris_player_position_set(MprisPlayer *obj, gint pos,
                                    GError **error) {
-    return FALSE;
+    gint time = CLAMP(pos / 1000, 0,
+            playlist_get_current_length(playlist_get_active()) / 1000 - 1);
+    playback_seek(time);
+    return TRUE;
 }
 gboolean mpris_player_position_get(MprisPlayer *obj, gint *pos,
                                    GError **error) {
-    return FALSE;
+    if (playback_get_playing())
+        *pos = playback_get_time();
+    else
+        *pos = 0;
+    return TRUE;
 }
 // MPRIS /Player signals
 gboolean mpris_player_emit_caps_change(MprisPlayer *obj, GError **error) {
-    g_signal_emit(obj, signals[CAPS_CHANGE_SIG], 0, "capabilities changed");
+    static GQuark quark;
+    if (!quark)
+        quark = g_quark_from_static_string("CapsChange");
+    g_signal_emit(obj, signals[CAPS_CHANGE_SIG], quark, "capabilities changed");
     return TRUE;
 }
 
@@ -256,41 +384,72 @@
 
 // MPRIS /TrackList
 gboolean mpris_tracklist_get_metadata(MprisTrackList *obj, gint pos,
-                                      GHashTable *metadata, GError **error) {
-    return FALSE;
+                                      GHashTable **metadata, GError **error) {
+    GHashTable *md = NULL;
+    Tuple *tuple = NULL;
+    GValue *value;
+    Playlist *active;
+
+    active = playlist_get_active();
+    tuple = playlist_get_tuple(active, pos);
+
+    md = mpris_metadata_from_tuple(tuple);
+
+    if (md == NULL) {
+        // there's no metadata for this track
+        return TRUE;
+    }
+
+    // Song URI
+    value = g_new0(GValue, 1);
+    g_value_init(value, G_TYPE_STRING);
+    g_value_set_string(value, playlist_get_filename(active, pos));
+
+    g_hash_table_insert(md, "URI", value);
+
+    *metadata = md;
+
+    return TRUE;
 }
 gboolean mpris_tracklist_get_current_track(MprisTrackList *obj, gint *pos,
                                            GError **error) {
-	*pos = playlist_get_position(playlist_get_active());
+    *pos = playlist_get_position(playlist_get_active());
     return TRUE;
 }
 gboolean mpris_tracklist_get_length(MprisTrackList *obj, gint *length,
                                     GError **error) {
-	*length = playlist_get_length(playlist_get_active());
+    *length = playlist_get_length(playlist_get_active());
     return TRUE;
 }
 gboolean mpris_tracklist_add_track(MprisTrackList *obj, gchar *uri,
                                    gboolean play, GError **error) {
-    playlist_add_url(playlist_get_active(), uri);
-    if (play) {
-        int pos = playlist_get_length(playlist_get_active()) - 1;
+    guint num_added;
+    num_added = playlist_add_url(playlist_get_active(), uri);
+    if (play && num_added > 0) {
+        gint pos = playlist_get_length(playlist_get_active()) - 1;
         playlist_set_position(playlist_get_active(), pos);
         playback_initiate();
     }
+    // TODO: set an error if num_added == 0
     return TRUE;
 }
 gboolean mpris_tracklist_del_track(MprisTrackList *obj, gint pos,
                                    GError **error) {
-	playlist_delete_index(playlist_get_active(), pos);
+    playlist_delete_index(playlist_get_active(), pos);
     return TRUE;
 }
 gboolean mpris_tracklist_loop(MprisTrackList *obj, gboolean loop,
                               GError **error) {
-    return FALSE;
+    mainwin_repeat_pushed(loop);
+    if (loop) {
+        mainwin_set_noplaylistadvance(FALSE);
+        mainwin_set_stopaftersong(FALSE);
+    }
+    return TRUE;
 }
 gboolean mpris_tracklist_random(MprisTrackList *obj, gboolean random,
                                 GError **error) {
-	mainwin_shuffle_pushed(!cfg.shuffle);
+    mainwin_shuffle_pushed(random);
     return TRUE;
 }
 
@@ -464,7 +623,7 @@
 }
 
 // Playlist Information/Manipulation
-gboolean audacious_rc_position(RemoteObject *obj, int *pos, GError **error) {
+gboolean audacious_rc_position(RemoteObject *obj, gint *pos, GError **error) {
     *pos = playlist_get_position(playlist_get_active());
     return TRUE;
 }
@@ -479,7 +638,7 @@
     return TRUE;
 }
 
-gboolean audacious_rc_length(RemoteObject *obj, int *length,
+gboolean audacious_rc_length(RemoteObject *obj, gint *length,
                              GError **error) {
     *length = playlist_get_length(playlist_get_active());
     return TRUE;
@@ -505,13 +664,13 @@
     return TRUE;
 }
 
-gboolean audacious_rc_song_length(RemoteObject *obj, guint pos, int *length,
+gboolean audacious_rc_song_length(RemoteObject *obj, guint pos, gint *length,
                                   GError **error) {
     *length = playlist_get_songtime(playlist_get_active(), pos) / 1000;
     return TRUE;
 }
 
-gboolean audacious_rc_song_frames(RemoteObject *obj, guint pos, int *length,
+gboolean audacious_rc_song_frames(RemoteObject *obj, guint pos, gint *length,
                                   GError **error) {
     *length = playlist_get_songtime(playlist_get_active(), pos);
     return TRUE;