linux-bluetooth.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH BlueZ 1/6 v2] tools: Make mpris-player to export players using MPRIS interface
@ 2013-01-24  9:57 Luiz Augusto von Dentz
  2013-01-24  9:58 ` [PATCH BlueZ 2/6 v2] tools: Add support for setting Shuffle and LoopStatus to mpris-player Luiz Augusto von Dentz
                   ` (6 more replies)
  0 siblings, 7 replies; 9+ messages in thread
From: Luiz Augusto von Dentz @ 2013-01-24  9:57 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

Players found are exported in private connections using
org.mpris.MediaPlayer2.<device name> on the session bus.
---
v2: Fix generating invalid player bus name if device name contains invalid
chars.

 tools/mpris-player.c | 644 +++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 623 insertions(+), 21 deletions(-)

diff --git a/tools/mpris-player.c b/tools/mpris-player.c
index 246791a..d58e53f 100644
--- a/tools/mpris-player.c
+++ b/tools/mpris-player.c
@@ -32,6 +32,7 @@
 #include <signal.h>
 #include <getopt.h>
 #include <string.h>
+#include <inttypes.h>
 
 #include <dbus/dbus.h>
 #include <glib.h>
@@ -41,13 +42,29 @@
 #define BLUEZ_PATH "/org/bluez"
 #define BLUEZ_ADAPTER_INTERFACE "org.bluez.Adapter1"
 #define BLUEZ_MEDIA_INTERFACE "org.bluez.Media1"
+#define BLUEZ_MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer1"
+#define MPRIS_BUS_NAME "org.mpris.MediaPlayer2."
 #define MPRIS_PLAYER_INTERFACE "org.mpris.MediaPlayer2.Player"
 #define MPRIS_PLAYER_PATH "/org/mpris/MediaPlayer2"
+#define ERROR_INTERFACE "org.mpris.MediaPlayer2.Error"
 
 static GMainLoop *main_loop;
 static GDBusProxy *adapter = NULL;
 static DBusConnection *sys = NULL;
 static DBusConnection *session = NULL;
+static GDBusClient *client = NULL;
+static GSList *players = NULL;
+
+struct player {
+	char *name;
+	char *bus_name;
+	DBusConnection *conn;
+	GDBusProxy *proxy;
+	GDBusProxy *device;
+};
+
+typedef int (* parse_metadata_func) (DBusMessageIter *iter, const char *key,
+						DBusMessageIter *metadata);
 
 static void dict_append_entry(DBusMessageIter *dict, const char *key, int type,
 								void *val);
@@ -190,7 +207,8 @@ static int parse_metadata_entry(DBusMessageIter *entry, const char *key,
 	return 0;
 }
 
-static int parse_metadata(DBusMessageIter *args, DBusMessageIter *metadata)
+static int parse_metadata(DBusMessageIter *args, DBusMessageIter *metadata,
+						parse_metadata_func func)
 {
 	DBusMessageIter dict;
 	int ctype;
@@ -216,7 +234,7 @@ static int parse_metadata(DBusMessageIter *args, DBusMessageIter *metadata)
 		dbus_message_iter_get_basic(&entry, &key);
 		dbus_message_iter_next(&entry);
 
-		if (parse_metadata_entry(&entry, key, metadata) < 0)
+		if (func(&entry, key, metadata) < 0)
 			return -EINVAL;
 
 		dbus_message_iter_next(&dict);
@@ -225,7 +243,8 @@ static int parse_metadata(DBusMessageIter *args, DBusMessageIter *metadata)
 	return 0;
 }
 
-static void append_metadata(DBusMessageIter *iter, DBusMessageIter *dict)
+static void append_metadata(DBusMessageIter *iter, DBusMessageIter *dict,
+						parse_metadata_func func)
 {
 	DBusMessageIter value, metadata;
 
@@ -237,7 +256,7 @@ static void append_metadata(DBusMessageIter *iter, DBusMessageIter *dict)
 			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
 			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &metadata);
 
-	parse_metadata(dict, &metadata);
+	parse_metadata(dict, &metadata, func);
 
 	dbus_message_iter_close_container(&value, &metadata);
 	dbus_message_iter_close_container(iter, &value);
@@ -260,7 +279,7 @@ static void dict_append_entry(DBusMessageIter *dict, const char *key, int type,
 	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
 
 	if (strcasecmp(key, "Metadata") == 0)
-		append_metadata(&entry, val);
+		append_metadata(&entry, val, parse_metadata_entry);
 	else
 		append_variant(&entry, type, val);
 
@@ -509,21 +528,29 @@ done:
 static void remove_player(DBusConnection *conn, const char *sender)
 {
 	DBusMessage *msg;
-	char *path;
+	char *path, *owner;
 
 	if (!adapter)
 		return;
 
+	path = sender2path(sender);
+	dbus_connection_get_object_path_data(sys, path, (void **) &owner);
+
+	if (owner == NULL) {
+		g_free(path);
+		return;
+	}
+
 	msg = dbus_message_new_method_call(BLUEZ_BUS_NAME,
 					g_dbus_proxy_get_path(adapter),
 					BLUEZ_MEDIA_INTERFACE,
 					"UnregisterPlayer");
 	if (!msg) {
 		fprintf(stderr, "Can't allocate new method call\n");
+		g_free(path);
 		return;
 	}
 
-	path = sender2path(sender);
 	dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &path,
 					DBUS_TYPE_INVALID);
 
@@ -540,10 +567,16 @@ static gboolean properties_changed(DBusConnection *conn,
 {
 	DBusMessageIter iter;
 	const char *iface;
-	char *path;
+	char *path, *owner;
 
 	dbus_message_iter_init(msg, &iter);
 
+	path = sender2path(dbus_message_get_sender(msg));
+	dbus_connection_get_object_path_data(sys, path, (void **) &owner);
+
+	if (owner == NULL)
+		goto done;
+
 	if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
 		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 
@@ -553,14 +586,28 @@ static gboolean properties_changed(DBusConnection *conn,
 
 	dbus_message_iter_next(&iter);
 
-	path = sender2path(dbus_message_get_sender(msg));
 	parse_properties(conn, path, &iter, NULL);
 
+done:
 	g_free(path);
 
 	return TRUE;
 }
 
+static struct player *find_player_by_bus_name(const char *name)
+{
+	GSList *l;
+
+	for (l = players; l; l = l->next) {
+		struct player *player = l->data;
+
+		if (strcmp(player->bus_name, name) == 0)
+			return player;
+	}
+
+	return NULL;
+}
+
 static gboolean name_owner_changed(DBusConnection *conn,
 						DBusMessage *msg, void *data)
 {
@@ -581,7 +628,7 @@ static gboolean name_owner_changed(DBusConnection *conn,
 	if (*new == '\0') {
 		printf("player %s at %s disappear\n", name, old);
 		remove_player(conn, old);
-	} else {
+	} else if (find_player_by_bus_name(name) == NULL) {
 		printf("player %s at %s found\n", name, new);
 		add_player(conn, name, new);
 	}
@@ -733,23 +780,570 @@ static void disconnect_handler(DBusConnection *connection, void *user_data)
 	printf("org.bluez disappeared\n");
 }
 
-static void proxy_added(GDBusProxy *proxy, void *user_data)
+static void player_free(void *data)
 {
-	const char *interface;
+	struct player *player = data;
+
+	if (player->conn) {
+		dbus_connection_close(player->conn);
+		dbus_connection_unref(player->conn);
+	}
+
+	g_dbus_proxy_unref(player->device);
+	g_dbus_proxy_unref(player->proxy);
+
+	g_free(player->name);
+	g_free(player->bus_name);
+	g_free(player);
+}
+
+struct pending_call {
+	struct player *player;
+	DBusMessage *msg;
+};
+
+static void pending_call_free(void *data)
+{
+	struct pending_call *p = data;
+
+	if (p->msg)
+		dbus_message_unref(p->msg);
+
+	g_free(p);
+}
+
+static void player_reply(DBusMessage *message, void *user_data)
+{
+	struct pending_call *p = user_data;
+	struct player *player = p->player;
+	DBusMessage *msg = p->msg;
+	DBusMessage *reply;
+	DBusError err;
+
+	dbus_error_init(&err);
+	if (dbus_set_error_from_message(&err, message)) {
+		fprintf(stderr, "error: %s", err.name);
+		reply = g_dbus_create_error(msg, err.name, err.message);
+		dbus_error_free(&err);
+	} else
+		reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+
+	g_dbus_send_message(player->conn, reply);
+}
+
+static void player_control(struct player *player, DBusMessage *msg,
+							const char *name)
+{
+	struct pending_call *p;
+
+	p = g_new0(struct pending_call, 1);
+	p->player = player;
+	p->msg = dbus_message_ref(msg);
+
+	g_dbus_proxy_method_call(player->proxy, name, NULL, player_reply,
+						p, pending_call_free);
+}
+
+static DBusMessage *player_toggle(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct player *player = data;
+	DBusMessageIter value;
+	const char *status;
+
+	if (!g_dbus_proxy_get_property(player->proxy, "Status", &value))
+		return FALSE;
+
+	dbus_message_iter_get_basic(&value, &status);
+
+	if (strcasecmp(status, "Playing") == 0)
+		player_control(player, msg, "Pause");
+	else
+		player_control(player, msg, "Play");
+
+	return NULL;
+}
+
+static DBusMessage *player_play(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct player *player = data;
+
+	player_control(player, msg, "Play");
+
+	return NULL;
+}
+
+static DBusMessage *player_pause(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct player *player = data;
+
+	player_control(player, msg, "Pause");
+
+	return NULL;
+}
+
+static DBusMessage *player_stop(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct player *player = data;
+
+	player_control(player, msg, "Stop");
+
+	return NULL;
+}
+
+static DBusMessage *player_next(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct player *player = data;
+
+	player_control(player, msg, "Next");
+
+	return NULL;
+}
+
+static DBusMessage *player_previous(DBusConnection *conn, DBusMessage *msg,
+								void *data)
+{
+	struct player *player = data;
+
+	player_control(player, msg, "Previous");
+
+	return NULL;
+}
+
+static gboolean status_exists(const GDBusPropertyTable *property, void *data)
+{
+	DBusMessageIter iter;
+	struct player *player = data;
+
+	return g_dbus_proxy_get_property(player->proxy, "Status", &iter);
+}
+
+static const char *status_to_playback(const char *status)
+{
+	if (strcasecmp(status, "playing") == 0)
+		return "Playing";
+	else if (strcasecmp(status, "paused") == 0)
+		return "Paused";
+	else
+		return "Stopped";
+}
+
+static gboolean get_status(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct player *player = data;
+	DBusMessageIter value;
+	const char *status;
+
+	if (!g_dbus_proxy_get_property(player->proxy, "Status", &value))
+		return FALSE;
+
+	dbus_message_iter_get_basic(&value, &status);
+
+	status = status_to_playback(status);
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &status);
+
+	return TRUE;
+}
+
+static gboolean repeat_exists(const GDBusPropertyTable *property, void *data)
+{
+	DBusMessageIter iter;
+	struct player *player = data;
+
+	return g_dbus_proxy_get_property(player->proxy, "Repeat", &iter);
+}
+
+static const char *repeat_to_loopstatus(const char *value)
+{
+	if (strcasecmp(value, "off") == 0)
+		return "None";
+	else if (strcasecmp(value, "singletrack") == 0)
+		return "Track";
+	else if (strcasecmp(value, "alltracks") == 0)
+		return "Playlist";
+
+	return NULL;
+}
+
+static gboolean get_repeat(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct player *player = data;
+	DBusMessageIter value;
+	const char *status;
+
+	if (!g_dbus_proxy_get_property(player->proxy, "Repeat", &value))
+		return FALSE;
+
+	dbus_message_iter_get_basic(&value, &status);
+
+	status = repeat_to_loopstatus(status);
+	if (status == NULL)
+		return FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &status);
+
+	return TRUE;
+}
+
+static gboolean get_double(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	double value = 1.0;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_DOUBLE, &value);
+
+	return TRUE;
+}
+
+static gboolean shuffle_exists(const GDBusPropertyTable *property, void *data)
+{
+	DBusMessageIter iter;
+	struct player *player = data;
+
+	return g_dbus_proxy_get_property(player->proxy, "Shuffle", &iter);
+}
+
+static gboolean get_shuffle(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct player *player = data;
+	DBusMessageIter value;
+	const char *string;
+	dbus_bool_t shuffle;
+
+	if (!g_dbus_proxy_get_property(player->proxy, "Shuffle", &value))
+		return FALSE;
+
+	dbus_message_iter_get_basic(&value, &string);
+
+	shuffle = strcmp(string, "off") != 0;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &shuffle);
+
+	return TRUE;
+}
+
+static gboolean position_exists(const GDBusPropertyTable *property, void *data)
+{
+	DBusMessageIter iter;
+	struct player *player = data;
+
+	return g_dbus_proxy_get_property(player->proxy, "Position", &iter);
+}
 
-	if (adapter != NULL)
+static gboolean get_position(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct player *player = data;
+	DBusMessageIter var;
+	uint32_t position;
+	int64_t value;
+
+	if (!g_dbus_proxy_get_property(player->proxy, "Position", &var))
+		return FALSE;
+
+	dbus_message_iter_get_basic(&var, &position);
+
+	value = position * 1000;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_INT64, &value);
+
+	return TRUE;
+}
+
+static gboolean track_exists(const GDBusPropertyTable *property, void *data)
+{
+	DBusMessageIter iter;
+	struct player *player = data;
+
+	return g_dbus_proxy_get_property(player->proxy, "Track", &iter);
+}
+
+static gboolean parse_string_metadata(DBusMessageIter *iter, const char *key,
+						DBusMessageIter *metadata)
+{
+	const char *value;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
+		return FALSE;
+
+	dbus_message_iter_get_basic(iter, &value);
+
+	dict_append_entry(metadata, key, DBUS_TYPE_STRING, &value);
+
+	return TRUE;
+}
+
+static gboolean parse_array_metadata(DBusMessageIter *iter, const char *key,
+						DBusMessageIter *metadata)
+{
+	char **value;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
+		return FALSE;
+
+	value = dbus_malloc0(sizeof(char *));
+
+	dbus_message_iter_get_basic(iter, &(value[0]));
+
+	dict_append_array(metadata, key, DBUS_TYPE_STRING, &value, 1);
+
+	dbus_free(value);
+
+	return TRUE;
+}
+
+static gboolean parse_int64_metadata(DBusMessageIter *iter, const char *key,
+						DBusMessageIter *metadata)
+{
+	uint32_t duration;
+	int64_t value;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32)
+		return FALSE;
+
+	dbus_message_iter_get_basic(iter, &duration);
+
+	value = duration * 1000;
+
+	dict_append_entry(metadata, key, DBUS_TYPE_INT64, &value);
+
+	return TRUE;
+}
+
+static gboolean parse_int32_metadata(DBusMessageIter *iter, const char *key,
+						DBusMessageIter *metadata)
+{
+	uint32_t value;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32)
+		return FALSE;
+
+	dbus_message_iter_get_basic(iter, &value);
+
+	dict_append_entry(metadata, key, DBUS_TYPE_INT32, &value);
+
+	return TRUE;
+}
+
+static int parse_track_entry(DBusMessageIter *entry, const char *key,
+						DBusMessageIter *metadata)
+{
+	DBusMessageIter var;
+
+	printf("metadata %s found\n", key);
+
+	if (dbus_message_iter_get_arg_type(entry) != DBUS_TYPE_VARIANT)
+		return -EINVAL;
+
+	dbus_message_iter_recurse(entry, &var);
+
+	if (strcasecmp(key, "Title") == 0) {
+		if (!parse_string_metadata(&var, "xesam:title", metadata))
+			return -EINVAL;
+	} else if (strcasecmp(key, "Artist") == 0) {
+		if (!parse_array_metadata(&var, "xesam:artist", metadata))
+			return -EINVAL;
+	} else if (strcasecmp(key, "Album") == 0) {
+		if (!parse_string_metadata(&var, "xesam:album", metadata))
+			return -EINVAL;
+	} else if (strcasecmp(key, "Genre") == 0) {
+		if (!parse_array_metadata(&var, "xesam:genre", metadata))
+			return -EINVAL;
+	} else if (strcasecmp(key, "Duration") == 0) {
+		if (!parse_int64_metadata(&var, "mpris:length", metadata))
+			return -EINVAL;
+	} else if (strcasecmp(key, "TrackNumber") == 0) {
+		if (!parse_int32_metadata(&var, "xesam:trackNumber", metadata))
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+static gboolean get_track(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct player *player = data;
+	DBusMessageIter var, metadata;
+
+	if (!g_dbus_proxy_get_property(player->proxy, "Track", &var))
+		return FALSE;
+
+	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+			DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+			DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+			DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &metadata);
+
+	parse_metadata(&var, &metadata, parse_track_entry);
+
+	dbus_message_iter_close_container(iter, &metadata);
+
+	return TRUE;
+}
+
+static gboolean get_enable(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	dbus_bool_t value = TRUE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
+
+	return TRUE;
+}
+
+static const GDBusMethodTable player_methods[] = {
+	{ GDBUS_ASYNC_METHOD("PlayPause", NULL, NULL, player_toggle) },
+	{ GDBUS_ASYNC_METHOD("Play", NULL, NULL, player_play) },
+	{ GDBUS_ASYNC_METHOD("Pause", NULL, NULL, player_pause) },
+	{ GDBUS_ASYNC_METHOD("Stop", NULL, NULL, player_stop) },
+	{ GDBUS_ASYNC_METHOD("Next", NULL, NULL, player_next) },
+	{ GDBUS_ASYNC_METHOD("Previous", NULL, NULL, player_previous) },
+	{ }
+};
+
+static const GDBusSignalTable player_signals[] = {
+	{ GDBUS_SIGNAL("Seeked", GDBUS_ARGS({"Position", "x"})) },
+	{ }
+};
+
+static const GDBusPropertyTable player_properties[] = {
+	{ "PlaybackStatus", "s", get_status, NULL, status_exists },
+	{ "LoopStatus", "s", get_repeat, NULL, repeat_exists },
+	{ "Rate", "d", get_double, NULL, NULL },
+	{ "MinimumRate", "d", get_double, NULL, NULL },
+	{ "MaximumRate", "d", get_double, NULL, NULL },
+	{ "Shuffle", "b", get_shuffle, NULL, shuffle_exists },
+	{ "Position", "x", get_position, NULL, position_exists },
+	{ "Metadata", "a{sv}", get_track, NULL, track_exists },
+	{ "Volume", "d", get_double, NULL, NULL },
+	{ "CanGoNext", "b", get_enable, NULL, NULL },
+	{ "CanGoPrevious", "b", get_enable, NULL, NULL },
+	{ "CanPlay", "b", get_enable, NULL, NULL },
+	{ "CanPause", "b", get_enable, NULL, NULL },
+	{ "CanSeek", "b", get_enable, NULL, NULL },
+	{ "CanControl", "b", get_enable, NULL, NULL },
+	{ }
+};
+
+
+#define a_z "abcdefghijklmnopqrstuvwxyz"
+#define A_Z "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+#define _0_9 "_0123456789"
+
+static char *mpris_busname(char *name)
+{
+	return g_strconcat(MPRIS_BUS_NAME,
+				g_strcanon(name, A_Z a_z _0_9, '_'), NULL);
+}
+
+static void register_player(GDBusProxy *proxy)
+{
+	struct player *player;
+	DBusMessageIter iter;
+	const char *path, *name;
+	GDBusProxy *device;
+
+	if (!g_dbus_proxy_get_property(proxy, "Device", &iter))
+		return;
+
+	dbus_message_iter_get_basic(&iter, &path);
+
+	device = g_dbus_proxy_new(client, path, "org.bluez.Device1");
+	if (device == NULL)
+		return;
+
+	if (!g_dbus_proxy_get_property(device, "Name", &iter))
 		return;
 
+	dbus_message_iter_get_basic(&iter, &name);
+
+	player = g_new0(struct player, 1);
+	player->name = g_strdup(name);
+	player->bus_name = mpris_busname(player->name);
+	player->proxy = g_dbus_proxy_ref(proxy);
+	player->device = device;
+
+	players = g_slist_prepend(players, player);
+
+	printf("Player %s created\n", player->bus_name);
+
+	player->conn = g_dbus_setup_private(DBUS_BUS_SESSION, player->bus_name,
+									NULL);
+	if (!session) {
+		fprintf(stderr, "Could not register bus name %s",
+							player->bus_name);
+		goto fail;
+	}
+
+	if (!g_dbus_register_interface(player->conn, MPRIS_PLAYER_PATH,
+						MPRIS_PLAYER_INTERFACE,
+						player_methods,
+						player_signals,
+						player_properties,
+						player, player_free)) {
+		fprintf(stderr, "Could not register interface %s",
+						MPRIS_PLAYER_INTERFACE);
+		goto fail;
+	}
+
+	return;
+
+fail:
+	players = g_slist_remove(players, player);
+	player_free(player);
+}
+
+static void proxy_added(GDBusProxy *proxy, void *user_data)
+{
+	const char *interface;
+
 	interface = g_dbus_proxy_get_interface(proxy);
 
 	if (!strcmp(interface, BLUEZ_ADAPTER_INTERFACE)) {
+		if (adapter != NULL)
+			return;
+
 		printf("Bluetooth Adapter %s found\n",
 						g_dbus_proxy_get_path(proxy));
 		adapter = proxy;
 		list_names(session);
+	} else if (!strcmp(interface, BLUEZ_MEDIA_PLAYER_INTERFACE)) {
+		printf("Bluetooth Player %s found\n",
+						g_dbus_proxy_get_path(proxy));
+		register_player(proxy);
 	}
 }
 
+static void unregister_player(struct player *player)
+{
+	players = g_slist_remove(players, player);
+
+	g_dbus_unregister_interface(player->conn, MPRIS_PLAYER_PATH,
+						MPRIS_PLAYER_INTERFACE);
+}
+
+static struct player *find_player(GDBusProxy *proxy)
+{
+	GSList *l;
+
+	for (l = players; l; l = l->next) {
+		struct player *player = l->data;
+
+		if (player->proxy == proxy)
+			return player;
+	}
+
+	return NULL;
+}
+
 static void proxy_removed(GDBusProxy *proxy, void *user_data)
 {
 	const char *interface;
@@ -759,19 +1353,27 @@ static void proxy_removed(GDBusProxy *proxy, void *user_data)
 
 	interface = g_dbus_proxy_get_interface(proxy);
 
-	if (strcmp(interface, BLUEZ_ADAPTER_INTERFACE))
-		return;
+	if (strcmp(interface, BLUEZ_ADAPTER_INTERFACE) == 0) {
+		if (adapter != proxy)
+			return;
+		printf("Bluetooth Adapter %s removed\n",
+						g_dbus_proxy_get_path(proxy));
+		adapter = NULL;
+	} else if (strcmp(interface, BLUEZ_MEDIA_PLAYER_INTERFACE) == 0) {
+		struct player *player;
 
-	if (adapter != proxy)
-		return;
+		player = find_player(proxy);
+		if (player == NULL)
+			return;
 
-	printf("Bluetooth Adapter %s removed\n", g_dbus_proxy_get_path(proxy));
-	adapter = NULL;
+		printf("Bluetooth Player %s removed\n",
+						g_dbus_proxy_get_path(proxy));
+		unregister_player(player);
+	}
 }
 
 int main(int argc, char *argv[])
 {
-	GDBusClient *client;
 	guint owner_watch, properties_watch;
 	struct sigaction sa;
 	int opt;
@@ -825,7 +1427,7 @@ int main(int argc, char *argv[])
 	g_dbus_client_set_disconnect_watch(client, disconnect_handler, NULL);
 
 	g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed,
-							NULL, NULL);
+								NULL, NULL);
 
 	g_main_loop_run(main_loop);
 
-- 
1.8.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH BlueZ 2/6 v2] tools: Add support for setting Shuffle and LoopStatus to mpris-player
  2013-01-24  9:57 [PATCH BlueZ 1/6 v2] tools: Make mpris-player to export players using MPRIS interface Luiz Augusto von Dentz
@ 2013-01-24  9:58 ` Luiz Augusto von Dentz
  2013-01-24  9:58 ` [PATCH BlueZ 3/6 v2] tools: Convert player's properties changed signals to MPRIS Luiz Augusto von Dentz
                   ` (5 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: Luiz Augusto von Dentz @ 2013-01-24  9:58 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This add write support for Shuffle and LoopStatus properties of
org.mpris.MediaPlayer2.Player interface.
---
 tools/mpris-player.c | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 78 insertions(+), 2 deletions(-)

diff --git a/tools/mpris-player.c b/tools/mpris-player.c
index d58e53f..7183bf6 100644
--- a/tools/mpris-player.c
+++ b/tools/mpris-player.c
@@ -992,6 +992,58 @@ static gboolean get_repeat(const GDBusPropertyTable *property,
 	return TRUE;
 }
 
+static const char *loopstatus_to_repeat(const char *value)
+{
+	if (strcasecmp(value, "None") == 0)
+		return "off";
+	else if (strcasecmp(value, "Track") == 0)
+		return "singletrack";
+	else if (strcasecmp(value, "Playlist") == 0)
+		return "alltracks";
+
+	return NULL;
+}
+
+static void property_result(const DBusError *err, void *user_data)
+{
+	GDBusPendingPropertySet id = GPOINTER_TO_UINT(user_data);
+
+	if (!dbus_error_is_set(err))
+		return g_dbus_pending_property_success(id);
+
+	g_dbus_pending_property_error(id, err->name, err->message);
+}
+
+static void set_repeat(const GDBusPropertyTable *property,
+			DBusMessageIter *iter, GDBusPendingPropertySet id,
+			void *data)
+{
+	struct player *player = data;
+	const char *value;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) {
+		g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments in method call");
+		return;
+	}
+
+	dbus_message_iter_get_basic(iter, &value);
+
+	value = loopstatus_to_repeat(value);
+	if (value == NULL) {
+		g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments in method call");
+		return;
+	}
+
+	g_dbus_proxy_set_property_basic(player->proxy, "Repeat",
+					DBUS_TYPE_STRING, value,
+					property_result, GUINT_TO_POINTER(id),
+					NULL);
+}
+
 static gboolean get_double(const GDBusPropertyTable *property,
 					DBusMessageIter *iter, void *data)
 {
@@ -1030,6 +1082,30 @@ static gboolean get_shuffle(const GDBusPropertyTable *property,
 	return TRUE;
 }
 
+static void set_shuffle(const GDBusPropertyTable *property,
+			DBusMessageIter *iter, GDBusPendingPropertySet id,
+			void *data)
+{
+	struct player *player = data;
+	dbus_bool_t shuffle;
+	const char *value;
+
+	if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN) {
+		g_dbus_pending_property_error(id,
+					ERROR_INTERFACE ".InvalidArguments",
+					"Invalid arguments in method call");
+		return;
+	}
+
+	dbus_message_iter_get_basic(iter, &shuffle);
+	value = shuffle ? "alltracks" : "off";
+
+	g_dbus_proxy_set_property_basic(player->proxy, "Shuffle",
+					DBUS_TYPE_STRING, value,
+					property_result, GUINT_TO_POINTER(id),
+					NULL);
+}
+
 static gboolean position_exists(const GDBusPropertyTable *property, void *data)
 {
 	DBusMessageIter iter;
@@ -1216,11 +1292,11 @@ static const GDBusSignalTable player_signals[] = {
 
 static const GDBusPropertyTable player_properties[] = {
 	{ "PlaybackStatus", "s", get_status, NULL, status_exists },
-	{ "LoopStatus", "s", get_repeat, NULL, repeat_exists },
+	{ "LoopStatus", "s", get_repeat, set_repeat, repeat_exists },
 	{ "Rate", "d", get_double, NULL, NULL },
 	{ "MinimumRate", "d", get_double, NULL, NULL },
 	{ "MaximumRate", "d", get_double, NULL, NULL },
-	{ "Shuffle", "b", get_shuffle, NULL, shuffle_exists },
+	{ "Shuffle", "b", get_shuffle, set_shuffle, shuffle_exists },
 	{ "Position", "x", get_position, NULL, position_exists },
 	{ "Metadata", "a{sv}", get_track, NULL, track_exists },
 	{ "Volume", "d", get_double, NULL, NULL },
-- 
1.8.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH BlueZ 3/6 v2] tools: Convert player's properties changed signals to MPRIS
  2013-01-24  9:57 [PATCH BlueZ 1/6 v2] tools: Make mpris-player to export players using MPRIS interface Luiz Augusto von Dentz
  2013-01-24  9:58 ` [PATCH BlueZ 2/6 v2] tools: Add support for setting Shuffle and LoopStatus to mpris-player Luiz Augusto von Dentz
@ 2013-01-24  9:58 ` Luiz Augusto von Dentz
  2013-01-24  9:58 ` [PATCH BlueZ 4/6 v2] tools: Make mpris-player to export org.mpris.MediaPlayer2 Luiz Augusto von Dentz
                   ` (4 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: Luiz Augusto von Dentz @ 2013-01-24  9:58 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

Thsi convert properties changed signals from org.bluez.MediaPlayer1 to
org.mpris.MediaPlayer2.Player interface.
---
 tools/mpris-player.c | 42 +++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 41 insertions(+), 1 deletion(-)

diff --git a/tools/mpris-player.c b/tools/mpris-player.c
index 7183bf6..320309e 100644
--- a/tools/mpris-player.c
+++ b/tools/mpris-player.c
@@ -1448,6 +1448,46 @@ static void proxy_removed(GDBusProxy *proxy, void *user_data)
 	}
 }
 
+static const char *property_to_mpris(const char *property)
+{
+	if (strcasecmp(property, "Repeat") == 0)
+		return "LoopStatus";
+	else if (strcasecmp(property, "Shuffle") == 0)
+		return "Shuffle";
+	else if (strcasecmp(property, "Status") == 0)
+		return "PlaybackStatus";
+	else if (strcasecmp(property, "Position") == 0)
+		return "Position";
+	else if (strcasecmp(property, "Track") == 0)
+		return "Metadata";
+
+	return NULL;
+}
+
+static void property_changed(GDBusProxy *proxy, const char *name,
+					DBusMessageIter *iter, void *user_data)
+{
+	struct player *player;
+	const char *interface, *property;
+
+	interface = g_dbus_proxy_get_interface(proxy);
+
+	if (strcmp(interface, BLUEZ_MEDIA_PLAYER_INTERFACE) != 0)
+		return;
+
+	player = find_player(proxy);
+	if (player == NULL)
+		return;
+
+	property = property_to_mpris(name);
+	if (property == NULL)
+		return;
+
+	g_dbus_emit_property_changed(player->conn, MPRIS_PLAYER_PATH,
+						MPRIS_PLAYER_INTERFACE,
+						property);
+}
+
 int main(int argc, char *argv[])
 {
 	guint owner_watch, properties_watch;
@@ -1503,7 +1543,7 @@ int main(int argc, char *argv[])
 	g_dbus_client_set_disconnect_watch(client, disconnect_handler, NULL);
 
 	g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed,
-								NULL, NULL);
+						property_changed, NULL);
 
 	g_main_loop_run(main_loop);
 
-- 
1.8.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH BlueZ 4/6 v2] tools: Make mpris-player to export org.mpris.MediaPlayer2
  2013-01-24  9:57 [PATCH BlueZ 1/6 v2] tools: Make mpris-player to export players using MPRIS interface Luiz Augusto von Dentz
  2013-01-24  9:58 ` [PATCH BlueZ 2/6 v2] tools: Add support for setting Shuffle and LoopStatus to mpris-player Luiz Augusto von Dentz
  2013-01-24  9:58 ` [PATCH BlueZ 3/6 v2] tools: Convert player's properties changed signals to MPRIS Luiz Augusto von Dentz
@ 2013-01-24  9:58 ` Luiz Augusto von Dentz
  2013-01-24  9:58 ` [PATCH BlueZ 5/6 v2] tools: Add volume support for mpris-player Luiz Augusto von Dentz
                   ` (3 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: Luiz Augusto von Dentz @ 2013-01-24  9:58 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This adds support for org.mpris.MediaPlayer2 interface whic is also
mandatory accourding to MPRIS spec.
---
 tools/mpris-player.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 46 insertions(+)

diff --git a/tools/mpris-player.c b/tools/mpris-player.c
index 320309e..c8377ec 100644
--- a/tools/mpris-player.c
+++ b/tools/mpris-player.c
@@ -44,6 +44,7 @@
 #define BLUEZ_MEDIA_INTERFACE "org.bluez.Media1"
 #define BLUEZ_MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer1"
 #define MPRIS_BUS_NAME "org.mpris.MediaPlayer2."
+#define MPRIS_INTERFACE "org.mpris.MediaPlayer2"
 #define MPRIS_PLAYER_INTERFACE "org.mpris.MediaPlayer2.Player"
 #define MPRIS_PLAYER_PATH "/org/mpris/MediaPlayer2"
 #define ERROR_INTERFACE "org.mpris.MediaPlayer2.Error"
@@ -1275,6 +1276,7 @@ static gboolean get_enable(const GDBusPropertyTable *property,
 	return TRUE;
 }
 
+
 static const GDBusMethodTable player_methods[] = {
 	{ GDBUS_ASYNC_METHOD("PlayPause", NULL, NULL, player_toggle) },
 	{ GDBUS_ASYNC_METHOD("Play", NULL, NULL, player_play) },
@@ -1309,6 +1311,39 @@ static const GDBusPropertyTable player_properties[] = {
 	{ }
 };
 
+static gboolean get_disable(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	dbus_bool_t value = FALSE;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
+
+	return TRUE;
+}
+
+static gboolean get_name(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct player *player = data;
+
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &player->name);
+
+	return TRUE;
+}
+
+static const GDBusMethodTable mpris_methods[] = {
+	{ }
+};
+
+static const GDBusPropertyTable mpris_properties[] = {
+	{ "CanQuit", "b", get_disable, NULL, NULL },
+	{ "Fullscreen", "b", get_disable, NULL, NULL },
+	{ "CanSetFullscreen", "b", get_disable, NULL, NULL },
+	{ "CanRaise", "b", get_disable, NULL, NULL },
+	{ "HasTrackList", "b", get_disable, NULL, NULL },
+	{ "Identity", "s", get_name, NULL, NULL },
+	{ }
+};
 
 #define a_z "abcdefghijklmnopqrstuvwxyz"
 #define A_Z "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
@@ -1360,6 +1395,17 @@ static void register_player(GDBusProxy *proxy)
 	}
 
 	if (!g_dbus_register_interface(player->conn, MPRIS_PLAYER_PATH,
+						MPRIS_INTERFACE,
+						mpris_methods,
+						NULL,
+						mpris_properties,
+						player, NULL)) {
+		fprintf(stderr, "Could not register interface %s",
+						MPRIS_INTERFACE);
+		goto fail;
+	}
+
+	if (!g_dbus_register_interface(player->conn, MPRIS_PLAYER_PATH,
 						MPRIS_PLAYER_INTERFACE,
 						player_methods,
 						player_signals,
-- 
1.8.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH BlueZ 5/6 v2] tools: Add volume support for mpris-player
  2013-01-24  9:57 [PATCH BlueZ 1/6 v2] tools: Make mpris-player to export players using MPRIS interface Luiz Augusto von Dentz
                   ` (2 preceding siblings ...)
  2013-01-24  9:58 ` [PATCH BlueZ 4/6 v2] tools: Make mpris-player to export org.mpris.MediaPlayer2 Luiz Augusto von Dentz
@ 2013-01-24  9:58 ` Luiz Augusto von Dentz
  2013-01-24  9:58 ` [PATCH BlueZ 6/6 v2] tools: Emit Seeked signal if Position changes Luiz Augusto von Dentz
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 9+ messages in thread
From: Luiz Augusto von Dentz @ 2013-01-24  9:58 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

This uses MediaTransport1 to track the Volume changes.
---
 tools/mpris-player.c | 187 ++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 178 insertions(+), 9 deletions(-)

diff --git a/tools/mpris-player.c b/tools/mpris-player.c
index c8377ec..5903ca1 100644
--- a/tools/mpris-player.c
+++ b/tools/mpris-player.c
@@ -43,6 +43,7 @@
 #define BLUEZ_ADAPTER_INTERFACE "org.bluez.Adapter1"
 #define BLUEZ_MEDIA_INTERFACE "org.bluez.Media1"
 #define BLUEZ_MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer1"
+#define BLUEZ_MEDIA_TRANSPORT_INTERFACE "org.bluez.MediaTransport1"
 #define MPRIS_BUS_NAME "org.mpris.MediaPlayer2."
 #define MPRIS_INTERFACE "org.mpris.MediaPlayer2"
 #define MPRIS_PLAYER_INTERFACE "org.mpris.MediaPlayer2.Player"
@@ -55,6 +56,7 @@ static DBusConnection *sys = NULL;
 static DBusConnection *session = NULL;
 static GDBusClient *client = NULL;
 static GSList *players = NULL;
+static GSList *transports = NULL;
 
 struct player {
 	char *name;
@@ -62,6 +64,7 @@ struct player {
 	DBusConnection *conn;
 	GDBusProxy *proxy;
 	GDBusProxy *device;
+	GDBusProxy *transport;
 };
 
 typedef int (* parse_metadata_func) (DBusMessageIter *iter, const char *key,
@@ -793,6 +796,9 @@ static void player_free(void *data)
 	g_dbus_proxy_unref(player->device);
 	g_dbus_proxy_unref(player->proxy);
 
+	if (player->transport)
+		g_dbus_proxy_unref(player->transport);
+
 	g_free(player->name);
 	g_free(player->bus_name);
 	g_free(player);
@@ -1277,6 +1283,30 @@ static gboolean get_enable(const GDBusPropertyTable *property,
 }
 
 
+static gboolean get_volume(const GDBusPropertyTable *property,
+					DBusMessageIter *iter, void *data)
+{
+	struct player *player = data;
+	double value = 0.0;
+	uint16_t volume;
+	DBusMessageIter var;
+
+	if (player->transport == NULL)
+		goto done;
+
+	if (!g_dbus_proxy_get_property(player->transport, "Volume", &var))
+		goto done;
+
+	dbus_message_iter_get_basic(&var, &volume);
+
+	value = (double) volume / 127;
+
+done:
+	dbus_message_iter_append_basic(iter, DBUS_TYPE_DOUBLE, &value);
+
+	return TRUE;
+}
+
 static const GDBusMethodTable player_methods[] = {
 	{ GDBUS_ASYNC_METHOD("PlayPause", NULL, NULL, player_toggle) },
 	{ GDBUS_ASYNC_METHOD("Play", NULL, NULL, player_play) },
@@ -1301,7 +1331,7 @@ static const GDBusPropertyTable player_properties[] = {
 	{ "Shuffle", "b", get_shuffle, set_shuffle, shuffle_exists },
 	{ "Position", "x", get_position, NULL, position_exists },
 	{ "Metadata", "a{sv}", get_track, NULL, track_exists },
-	{ "Volume", "d", get_double, NULL, NULL },
+	{ "Volume", "d", get_volume, NULL, NULL },
 	{ "CanGoNext", "b", get_enable, NULL, NULL },
 	{ "CanGoPrevious", "b", get_enable, NULL, NULL },
 	{ "CanPlay", "b", get_enable, NULL, NULL },
@@ -1355,12 +1385,33 @@ static char *mpris_busname(char *name)
 				g_strcanon(name, A_Z a_z _0_9, '_'), NULL);
 }
 
+static GDBusProxy *find_transport_by_path(const char *path)
+{
+	GSList *l;
+
+	for (l = transports; l; l = l->next) {
+		GDBusProxy *transport = l->data;
+		DBusMessageIter iter;
+		const char *value;
+
+		if (!g_dbus_proxy_get_property(transport, "Device", &iter))
+			continue;
+
+		dbus_message_iter_get_basic(&iter, &value);
+
+		if (strcmp(path, value) == 0)
+			return transport;
+	}
+
+	return NULL;
+}
+
 static void register_player(GDBusProxy *proxy)
 {
 	struct player *player;
 	DBusMessageIter iter;
 	const char *path, *name;
-	GDBusProxy *device;
+	GDBusProxy *device, *transport;
 
 	if (!g_dbus_proxy_get_property(proxy, "Device", &iter))
 		return;
@@ -1416,6 +1467,10 @@ static void register_player(GDBusProxy *proxy)
 		goto fail;
 	}
 
+	transport = find_transport_by_path(path);
+	if (transport)
+		player->transport = g_dbus_proxy_ref(transport);
+
 	return;
 
 fail:
@@ -1423,6 +1478,47 @@ fail:
 	player_free(player);
 }
 
+static struct player *find_player_by_device(const char *device)
+{
+	GSList *l;
+
+	for (l = players; l; l = l->next) {
+		struct player *player = l->data;
+		const char *path = g_dbus_proxy_get_path(player->device);
+
+		if (g_strcmp0(device, path) == 0)
+			return player;
+	}
+
+	return NULL;
+}
+
+static void register_transport(GDBusProxy *proxy)
+{
+	struct player *player;
+	DBusMessageIter iter;
+	const char *path;
+
+	if (g_slist_find(transports, proxy) != NULL)
+		return;
+
+	if (!g_dbus_proxy_get_property(proxy, "Volume", &iter))
+		return;
+
+	if (!g_dbus_proxy_get_property(proxy, "Device", &iter))
+		return;
+
+	dbus_message_iter_get_basic(&iter, &path);
+
+	transports = g_slist_append(transports, proxy);
+
+	player = find_player_by_device(path);
+	if (player == NULL || player->transport != NULL)
+		return;
+
+	player->transport = g_dbus_proxy_ref(proxy);
+}
+
 static void proxy_added(GDBusProxy *proxy, void *user_data)
 {
 	const char *interface;
@@ -1441,6 +1537,10 @@ static void proxy_added(GDBusProxy *proxy, void *user_data)
 		printf("Bluetooth Player %s found\n",
 						g_dbus_proxy_get_path(proxy));
 		register_player(proxy);
+	} else if (!strcmp(interface, BLUEZ_MEDIA_TRANSPORT_INTERFACE)) {
+		printf("Bluetooth Transport %s found\n",
+						g_dbus_proxy_get_path(proxy));
+		register_transport(proxy);
 	}
 }
 
@@ -1466,6 +1566,37 @@ static struct player *find_player(GDBusProxy *proxy)
 	return NULL;
 }
 
+static struct player *find_player_by_transport(GDBusProxy *proxy)
+{
+	GSList *l;
+
+	for (l = players; l; l = l->next) {
+		struct player *player = l->data;
+
+		if (player->transport == proxy)
+			return player;
+	}
+
+	return NULL;
+}
+
+static void unregister_transport(GDBusProxy *proxy)
+{
+	struct player *player;
+
+	if (g_slist_find(transports, proxy) == NULL)
+		return;
+
+	transports = g_slist_remove(transports, proxy);
+
+	player = find_player_by_transport(proxy);
+	if (player == NULL)
+		return;
+
+	g_dbus_proxy_unref(player->transport);
+	player->transport = NULL;
+}
+
 static void proxy_removed(GDBusProxy *proxy, void *user_data)
 {
 	const char *interface;
@@ -1491,6 +1622,10 @@ static void proxy_removed(GDBusProxy *proxy, void *user_data)
 		printf("Bluetooth Player %s removed\n",
 						g_dbus_proxy_get_path(proxy));
 		unregister_player(player);
+	} else if (strcmp(interface, BLUEZ_MEDIA_TRANSPORT_INTERFACE) == 0) {
+		printf("Bluetooth Transport %s removed\n",
+						g_dbus_proxy_get_path(proxy));
+		unregister_transport(proxy);
 	}
 }
 
@@ -1510,16 +1645,11 @@ static const char *property_to_mpris(const char *property)
 	return NULL;
 }
 
-static void property_changed(GDBusProxy *proxy, const char *name,
+static void player_property_changed(GDBusProxy *proxy, const char *name,
 					DBusMessageIter *iter, void *user_data)
 {
 	struct player *player;
-	const char *interface, *property;
-
-	interface = g_dbus_proxy_get_interface(proxy);
-
-	if (strcmp(interface, BLUEZ_MEDIA_PLAYER_INTERFACE) != 0)
-		return;
+	const char *property;
 
 	player = find_player(proxy);
 	if (player == NULL)
@@ -1534,6 +1664,45 @@ static void property_changed(GDBusProxy *proxy, const char *name,
 						property);
 }
 
+static void transport_property_changed(GDBusProxy *proxy, const char *name,
+					DBusMessageIter *iter, void *user_data)
+{
+	struct player *player;
+	DBusMessageIter var;
+	const char *path;
+
+	if (strcasecmp(name, "Volume") != 0)
+		return;
+
+	if (!g_dbus_proxy_get_property(proxy, "Device", &var))
+		return;
+
+	dbus_message_iter_get_basic(&var, &path);
+
+	player = find_player_by_device(path);
+	if (player == NULL)
+		return;
+
+	g_dbus_emit_property_changed(player->conn, MPRIS_PLAYER_PATH,
+						MPRIS_PLAYER_INTERFACE,
+						name);
+}
+
+static void property_changed(GDBusProxy *proxy, const char *name,
+					DBusMessageIter *iter, void *user_data)
+{
+	const char *interface;
+
+	interface = g_dbus_proxy_get_interface(proxy);
+
+	if (strcmp(interface, BLUEZ_MEDIA_PLAYER_INTERFACE) == 0)
+		return player_property_changed(proxy, name, iter, user_data);
+
+	if (strcmp(interface, BLUEZ_MEDIA_TRANSPORT_INTERFACE) == 0)
+		return transport_property_changed(proxy, name, iter,
+								user_data);
+}
+
 int main(int argc, char *argv[])
 {
 	guint owner_watch, properties_watch;
-- 
1.8.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH BlueZ 6/6 v2] tools: Emit Seeked signal if Position changes
  2013-01-24  9:57 [PATCH BlueZ 1/6 v2] tools: Make mpris-player to export players using MPRIS interface Luiz Augusto von Dentz
                   ` (3 preceding siblings ...)
  2013-01-24  9:58 ` [PATCH BlueZ 5/6 v2] tools: Add volume support for mpris-player Luiz Augusto von Dentz
@ 2013-01-24  9:58 ` Luiz Augusto von Dentz
  2013-01-25  7:42 ` [PATCH BlueZ 1/6 v2] tools: Make mpris-player to export players using MPRIS interface Johan Hedberg
  2013-01-25  9:28 ` [PATCH] tools: Fix format string warnings for g_dbus_create_error Chan-yeol Park
  6 siblings, 0 replies; 9+ messages in thread
From: Luiz Augusto von Dentz @ 2013-01-24  9:58 UTC (permalink / raw)
  To: linux-bluetooth

From: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>

MPRIS spec says PropertiesChanged is not emitted for Position, which is
probably to make clear that progress is done using the rate, so instead
Seeked should be emitted.
---
 tools/mpris-player.c | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/tools/mpris-player.c b/tools/mpris-player.c
index 5903ca1..95ef8ab 100644
--- a/tools/mpris-player.c
+++ b/tools/mpris-player.c
@@ -1650,6 +1650,8 @@ static void player_property_changed(GDBusProxy *proxy, const char *name,
 {
 	struct player *player;
 	const char *property;
+	uint32_t position;
+	uint64_t value;
 
 	player = find_player(proxy);
 	if (player == NULL)
@@ -1662,6 +1664,18 @@ static void player_property_changed(GDBusProxy *proxy, const char *name,
 	g_dbus_emit_property_changed(player->conn, MPRIS_PLAYER_PATH,
 						MPRIS_PLAYER_INTERFACE,
 						property);
+
+	if (strcasecmp(name, "Position") != 0)
+		return;
+
+	dbus_message_iter_get_basic(iter, &position);
+
+	value = position * 1000;
+
+	g_dbus_emit_signal(player->conn, MPRIS_PLAYER_PATH,
+					MPRIS_PLAYER_INTERFACE, "Seeked",
+					DBUS_TYPE_INT64, &value,
+					DBUS_TYPE_INVALID);
 }
 
 static void transport_property_changed(GDBusProxy *proxy, const char *name,
-- 
1.8.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH BlueZ 1/6 v2] tools: Make mpris-player to export players using MPRIS interface
  2013-01-24  9:57 [PATCH BlueZ 1/6 v2] tools: Make mpris-player to export players using MPRIS interface Luiz Augusto von Dentz
                   ` (4 preceding siblings ...)
  2013-01-24  9:58 ` [PATCH BlueZ 6/6 v2] tools: Emit Seeked signal if Position changes Luiz Augusto von Dentz
@ 2013-01-25  7:42 ` Johan Hedberg
  2013-01-25  9:28 ` [PATCH] tools: Fix format string warnings for g_dbus_create_error Chan-yeol Park
  6 siblings, 0 replies; 9+ messages in thread
From: Johan Hedberg @ 2013-01-25  7:42 UTC (permalink / raw)
  To: Luiz Augusto von Dentz; +Cc: linux-bluetooth

Hi Luiz,

On Thu, Jan 24, 2013, Luiz Augusto von Dentz wrote:
> Players found are exported in private connections using
> org.mpris.MediaPlayer2.<device name> on the session bus.
> ---
> v2: Fix generating invalid player bus name if device name contains invalid
> chars.
> 
>  tools/mpris-player.c | 644 +++++++++++++++++++++++++++++++++++++++++++++++++--
>  1 file changed, 623 insertions(+), 21 deletions(-)

All patches in this set have been applied. Thanks.

Johan

^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH] tools: Fix format string warnings for g_dbus_create_error
  2013-01-24  9:57 [PATCH BlueZ 1/6 v2] tools: Make mpris-player to export players using MPRIS interface Luiz Augusto von Dentz
                   ` (5 preceding siblings ...)
  2013-01-25  7:42 ` [PATCH BlueZ 1/6 v2] tools: Make mpris-player to export players using MPRIS interface Johan Hedberg
@ 2013-01-25  9:28 ` Chan-yeol Park
  2013-01-25  9:53   ` Luiz Augusto von Dentz
  6 siblings, 1 reply; 9+ messages in thread
From: Chan-yeol Park @ 2013-01-25  9:28 UTC (permalink / raw)
  To: linux-bluetooth

This patch fixes gcc warnings for "format not a string literal and no
format arguments".
---
 tools/mpris-player.c |    2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/mpris-player.c b/tools/mpris-player.c
index 95ef8ab..aafa142 100644
--- a/tools/mpris-player.c
+++ b/tools/mpris-player.c
@@ -830,7 +830,7 @@ static void player_reply(DBusMessage *message, void *user_data)
 	dbus_error_init(&err);
 	if (dbus_set_error_from_message(&err, message)) {
 		fprintf(stderr, "error: %s", err.name);
-		reply = g_dbus_create_error(msg, err.name, err.message);
+		reply = g_dbus_create_error(msg, err.name, "%s", err.message);
 		dbus_error_free(&err);
 	} else
 		reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
-- 
1.7.10.4


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH] tools: Fix format string warnings for g_dbus_create_error
  2013-01-25  9:28 ` [PATCH] tools: Fix format string warnings for g_dbus_create_error Chan-yeol Park
@ 2013-01-25  9:53   ` Luiz Augusto von Dentz
  0 siblings, 0 replies; 9+ messages in thread
From: Luiz Augusto von Dentz @ 2013-01-25  9:53 UTC (permalink / raw)
  To: Chan-yeol Park; +Cc: linux-bluetooth@vger.kernel.org

Hi Chan-yeol,

On Fri, Jan 25, 2013 at 11:28 AM, Chan-yeol Park
<chanyeol.park@samsung.com> wrote:
> This patch fixes gcc warnings for "format not a string literal and no
> format arguments".
> ---
>  tools/mpris-player.c |    2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/tools/mpris-player.c b/tools/mpris-player.c
> index 95ef8ab..aafa142 100644
> --- a/tools/mpris-player.c
> +++ b/tools/mpris-player.c
> @@ -830,7 +830,7 @@ static void player_reply(DBusMessage *message, void *user_data)
>         dbus_error_init(&err);
>         if (dbus_set_error_from_message(&err, message)) {
>                 fprintf(stderr, "error: %s", err.name);
> -               reply = g_dbus_create_error(msg, err.name, err.message);
> +               reply = g_dbus_create_error(msg, err.name, "%s", err.message);
>                 dbus_error_free(&err);
>         } else
>                 reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
> --
> 1.7.10.4

Applied, thanks.


--
Luiz Augusto von Dentz

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2013-01-25  9:53 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-01-24  9:57 [PATCH BlueZ 1/6 v2] tools: Make mpris-player to export players using MPRIS interface Luiz Augusto von Dentz
2013-01-24  9:58 ` [PATCH BlueZ 2/6 v2] tools: Add support for setting Shuffle and LoopStatus to mpris-player Luiz Augusto von Dentz
2013-01-24  9:58 ` [PATCH BlueZ 3/6 v2] tools: Convert player's properties changed signals to MPRIS Luiz Augusto von Dentz
2013-01-24  9:58 ` [PATCH BlueZ 4/6 v2] tools: Make mpris-player to export org.mpris.MediaPlayer2 Luiz Augusto von Dentz
2013-01-24  9:58 ` [PATCH BlueZ 5/6 v2] tools: Add volume support for mpris-player Luiz Augusto von Dentz
2013-01-24  9:58 ` [PATCH BlueZ 6/6 v2] tools: Emit Seeked signal if Position changes Luiz Augusto von Dentz
2013-01-25  7:42 ` [PATCH BlueZ 1/6 v2] tools: Make mpris-player to export players using MPRIS interface Johan Hedberg
2013-01-25  9:28 ` [PATCH] tools: Fix format string warnings for g_dbus_create_error Chan-yeol Park
2013-01-25  9:53   ` Luiz Augusto von Dentz

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).