#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <math.h>
#include <murphy/common/debug.h>
static char *commands[] = {
"play music",
"stop music",
+ "play louder",
+ "play quieter",
+ "play next",
+ "play previous",
"show player",
- "hide player",
+ "quit player",
NULL
};
static int ncommand = (sizeof(commands) / sizeof(commands[0])) - 1;
int clients_create(context_t *ctx)
{
clients_t *clients;
- srs_plugin_t *pl;
- srs_context_t *srs;
- srs_client_ops_t callbacks;
mrp_htbl_config_t cfg;
- if (!ctx || !(pl = ctx->plugin) || !(srs = pl->srs))
+ if (!ctx)
return -1;
if (!(clients = mrp_allocz(sizeof(clients_t))))
return -1;
- callbacks.notify_focus = notify_focus;
- callbacks.notify_command = notify_command;
-
- clients->srs_client = client_create(srs, SRS_CLIENT_TYPE_BUILTIN,
- PLUGIN_NAME, "player",
- commands, ncommand,
- PLUGIN_NAME, &callbacks, NULL);
-
memset(&cfg, 0, sizeof(cfg));
cfg.nentry = 10;
cfg.comp = mrp_string_comp;
int clients_start(context_t *ctx)
{
+ srs_plugin_t *pl;
+ srs_context_t *srs;
clients_t *clients;
player_t *player;
+ srs_client_ops_t callbacks;
- if (!ctx || !(clients = ctx->clients))
+ if (!ctx || !(pl = ctx->plugin) || !(srs = pl->srs) ||
+ !(clients = ctx->clients))
return -1;
+ callbacks.notify_focus = notify_focus;
+ callbacks.notify_command = notify_command;
+
+ clients->srs_client = client_create(srs, SRS_CLIENT_TYPE_BUILTIN,
+ PLUGIN_NAME, "player",
+ commands, ncommand,
+ PLUGIN_NAME, &callbacks, ctx);
+
mrp_htbl_foreach(clients->player.name, player_register, (void *)ctx);
+ client_request_focus(clients->srs_client, SRS_VOICE_FOCUS_SHARED);
+
return 0;
}
player->name, player->service ? player->service : "none",
player->object ? player->object : "none");
+ if (!clients->current) {
+ clients->current = player;
+ mrp_log_info("'%s' become the default player", player->name);
+ }
+
return 0;
}
mrp_log_info("mrpis2 client '%s' appeared (address %s)",
name, address);
- if (!clients->current)
- clients->current = player;
-
dbusif_query_player_properties(player);
}
}
player->active_list = NULL;
mrp_log_info("mrpis2 client '%s' disappeared", name);
-
- if (player == clients->current) {
- clients->current = NULL;
- }
}
}
}
}
}
+void clients_player_volume_changed(player_t *player, double volume)
+{
+ if (player) {
+ if (volume < 0.00001) volume = 0.00001;
+ else if (volume > 1.0) volume = 1.0;
+
+ player->volume = log10(volume) * 20.0;
+ }
+}
+
void clients_playlist_changed(player_t *player, size_t nlist,playlist_t *lists)
{
context_t *ctx;
dbusif_introspect_player(player);
}
+void clients_player_request_track(player_t *player, track_t track)
+{
+ if (!player || (track != NEXT_TRACK && track != PREVIOUS_TRACK))
+ return;
+
+ if (!player->address)
+ clients_player_request_state(player, PLAY);
+ else {
+ if (player->state != PLAY)
+ dbusif_set_player_state(player, PLAY);
+
+ if (track == PREVIOUS_TRACK)
+ dbusif_change_track(player, PREVIOUS_TRACK);
+
+ dbusif_change_track(player, track);
+ }
+}
+
+void clients_player_adjust_volume(player_t *player, double adjust)
+{
+ double volume;
+
+ if (player && player->address) {
+ volume = pow(10, (player->volume + adjust) / 20.0);
+
+ if (volume < 0.0) volume = 0;
+ else if (volume > 1.0) volume = 1.0;
+
+ dbusif_set_player_property(player, "Volume", "d", &volume);
+ }
+}
+
+void clients_player_show(player_t *player)
+{
+ if (player) {
+ if (player->address)
+ dbusif_raise_player(player);
+ else
+ /* this supposed to launch the player */
+ dbusif_introspect_player(player);
+ }
+}
+
+void clients_player_quit(player_t *player)
+{
+ if (player && player->address)
+ dbusif_quit_player(player);
+}
+
static int notify_focus(srs_client_t *srs_client, srs_voice_focus_t focus)
{
return TRUE;
static int notify_command(srs_client_t *srs_client, int ntoken, char **tokens)
{
+ context_t *ctx;
+ clients_t *clients;
+ player_t *player;
+ char cmd[2048];
+ char *e, *p, *sep;
+ int i;
+
+ if (!srs_client || !(ctx = srs_client->user_data) ||
+ !(clients = ctx->clients))
+ return FALSE;
+
+ e = (p = cmd) + (sizeof(cmd) - 1);
+
+ for (i = 0, sep = "", *p = 0; i < ntoken && p < e; i++, sep = " ")
+ p += snprintf(p, e-p, "%s%s", sep, tokens[i]);
+
+ if (!(player = clients->current)) {
+ mrp_log_info("no player to execute command '%s'", cmd);
+ return FALSE;
+ }
+
+ mrp_log_info("Mpris2 client got command '%s'\n", cmd);
+
+ if (!strcmp(cmd, "play music"))
+ clients_player_request_state(player, PLAY);
+ else if (!strcmp(cmd, "stop music"))
+ clients_player_request_state(player, PAUSE);
+ else if (!strcmp(cmd, "play next"))
+ clients_player_request_track(player, NEXT_TRACK);
+ else if (!strcmp(cmd, "play previous"))
+ clients_player_request_track(player, PREVIOUS_TRACK);
+ else if (!strcmp(cmd, "play louder"))
+ clients_player_adjust_volume(player, +2);
+ else if (!strcmp(cmd, "play quieter"))
+ clients_player_adjust_volume(player, -2);
+ else if (!strcmp(cmd, "show player"))
+ clients_player_show(player);
+ else if (!strcmp(cmd, "quit player"))
+ clients_player_quit(player);
+
return TRUE;
}
size_t nlist;
playlist_t *lists;
playlist_t *active_list;
+ double volume;
pa_time_event *timer;
};
const char *name;
};
+enum track_e {
+ TRACK_UNKNOWN = 0,
+ NEXT_TRACK,
+ PREVIOUS_TRACK
+};
+
int clients_create(context_t *ctx);
void clients_destroy(context_t *ctx);
void clients_player_disappeared(context_t *ctx, const char *name);
void clients_player_state_changed(player_t *player, player_state_t state);
void clients_player_status_changed(player_t *player, bool ready);
+void clients_player_volume_changed(player_t *player, double volume);
void clients_playlist_changed(player_t *player,size_t nlist,playlist_t *lists);
void clients_player_request_state(player_t *player, player_state_t state);
+void clients_player_request_track(player_t *player, track_t track);
+
+void clients_player_adjust_volume(player_t *player, double adjust);
+
+void clients_player_show(player_t *player);
+void clients_player_quit(player_t *player);
#endif /* __SRS_MPRIS2_CLIENT_H__ */
#define MAKE_DBUS_VERSION(major, minor, patch) \
(((major) << 16) | ((minor) << 8) | (patch))
-#if DBUS_VERSION < MAKE_DBUS_VERSION(1, 7, 0)
+#if DBUS_VERSION < MAKE_DBUS_VERSION(1, 6, 8)
/* For old versions, we define DBusBasicValue with the member we use... */
typedef union {
char *str;
dbus_message_unref(msg);
}
+void dbusif_set_player_property(player_t *player,
+ const char *name,
+ const char *type,
+ void *value)
+{
+ const char *interface = "org.mpris.MediaPlayer2.Player";
+
+ context_t *ctx;
+ dbusif_t *dbusif;
+ DBusMessage *msg;
+ DBusMessageIter mit;
+ DBusMessageIter vit;
+ int success;
+
+ if (!player || !player->name || !player->address ||
+ !(ctx = player->ctx) || !(dbusif = ctx->dbusif))
+ return;
+
+ msg = dbus_message_new_method_call(player->address,
+ "/org/mpris/MediaPlayer2",
+ "org.freedesktop.DBus.Properties",
+ "Set");
+
+ if (!msg)
+ return;
+
+ dbus_message_iter_init_append(msg, &mit);
+ dbus_message_iter_append_basic(&mit, DBUS_TYPE_STRING, &interface);
+ dbus_message_iter_append_basic(&mit, DBUS_TYPE_STRING, &name);
+ dbus_message_iter_open_container(&mit, DBUS_TYPE_VARIANT, type, &vit);
+ dbus_message_iter_append_basic(&vit, type[0], value);
+ dbus_message_iter_close_container(&mit, &vit);
+
+ mrp_dbus_send_msg(dbusif->dbus, msg);
+
+ dbus_message_unref(msg);
+}
+
void dbusif_introspect_player(player_t *player)
{
context_t *ctx;
DBusMessage *msg;
const char *member;
- printf("address: %s\n", player->address ? player->address : "unknown");
-
if (!player || !player->address ||
!(ctx = player->ctx) || !(dbusif = ctx->dbusif))
return;
default: return;
}
- printf("member: %s\n", member);
+ msg = dbus_message_new_method_call(player->address,
+ "/org/mpris/MediaPlayer2",
+ "org.mpris.MediaPlayer2.Player",
+ member);
+ if (msg) {
+ mrp_dbus_send_msg(dbusif->dbus, msg);
+ dbus_message_unref(msg);
+ }
+}
+void dbusif_change_track(player_t *player, track_t track)
+{
+ context_t *ctx;
+ dbusif_t *dbusif;
+ DBusMessage *msg;
+ const char *member;
+
+ if (!player || !player->address ||
+ !(ctx = player->ctx) || !(dbusif = ctx->dbusif))
+ return;
+
+ switch (track) {
+ case NEXT_TRACK: member = "Next"; break;
+ case PREVIOUS_TRACK: member = "Previous"; break;
+ default: return;
+ }
msg = dbus_message_new_method_call(player->address,
"/org/mpris/MediaPlayer2",
dbus_message_unref(msg);
}
+void dbusif_raise_player(player_t *player)
+{
+ context_t *ctx;
+ dbusif_t *dbusif;
+ DBusMessage *msg;
+ const char *member;
+
+ if (!player || !player->address ||
+ !(ctx = player->ctx) || !(dbusif = ctx->dbusif))
+ return;
+
+ msg = dbus_message_new_method_call(player->address,
+ "/org/mpris/MediaPlayer2",
+ "org.mpris.MediaPlayer2",
+ "Raise");
+ if (msg) {
+ mrp_dbus_send_msg(dbusif->dbus, msg);
+ dbus_message_unref(msg);
+ }
+}
+
+void dbusif_quit_player(player_t *player)
+{
+ context_t *ctx;
+ dbusif_t *dbusif;
+ DBusMessage *msg;
+ const char *member;
+
+ if (!player || !player->address ||
+ !(ctx = player->ctx) || !(dbusif = ctx->dbusif))
+ return;
+
+ msg = dbus_message_new_method_call(player->address,
+ "/org/mpris/MediaPlayer2",
+ "org.mpris.MediaPlayer2",
+ "Quit");
+ if (msg) {
+ mrp_dbus_send_msg(dbusif->dbus, msg);
+ dbus_message_unref(msg);
+ }
+}
+
static void name_follow_cb(mrp_dbus_t *dbus,
const char *dbus_name,
int error,
else
state = UNKNOWN;
- printf("*** state %d\n", state);
+ // printf("*** state %d\n", state);
if (state != UNKNOWN)
clients_player_state_changed(player, state);
}
else if (!strcmp(prop, "Volume") && type == DBUS_TYPE_DOUBLE) {
printf("*** volume %.4lf\n", value.dbl);
+ clients_player_volume_changed(player, value.dbl);
}
else if (!strcmp(prop, "CanPlay") && type == DBUS_TYPE_BOOLEAN) {
- printf("*** %s play\n", value.bool_val ? "can" : "unable to");
+ //printf("*** %s play\n", value.bool_val ? "can":"unable to");
clients_player_status_changed(player, value.bool_val);
}
}
void dbusif_unregister_player(context_t *ctx, const char *name);
void dbusif_query_player_properties(player_t *player);
+void dbusif_set_player_property(player_t *player, const char *name,
+ const char *type, void *value);
void dbusif_introspect_player(player_t *player);
void dbusif_set_player_state(player_t *player, player_state_t state);
+void dbusif_change_track(player_t *player, track_t track);
void dbusif_query_playlists(player_t *player);
void dbusif_set_playlist(player_t *player, const char *id);
+void dbusif_raise_player(player_t *player);
+void dbusif_quit_player(player_t *player);
+
#endif /* __SRS_MPRIS2_DBUS_INTERFACE_H__ */
#define PLUGIN_VERSION "0.0.1"
-/*************************************************************/
-pa_io_event *ioev;
-
-static void handle_input(pa_mainloop_api *api, pa_io_event *e, int fd,
- pa_io_event_flags_t events, void *ud)
-{
- context_t *ctx = (context_t *)ud;
- player_t *player;
- int cnt;
- char cmd;
-
- for (;;) {
- if ((cnt = read(0, &cmd, 1)) != 1) {
- if (errno == EINTR)
- continue;
- return;
- }
-
- if (!(player = clients_find_player_by_name(ctx, "rhythmbox"))) {
- printf("can't find rhythmbox player\n");
- return;
- }
-
- switch (cmd) {
- case 'p': clients_player_request_state(player, PLAY); break;
- case 's': clients_player_request_state(player, PAUSE); break;
- case 'e': clients_player_request_state(player, STOP); break;
- case 'a': printf("Show\n"); break;
- default: break;
- }
-
- return;
- }
-}
-
-static void input_create(context_t *ctx)
-{
- srs_plugin_t *pl = ctx->plugin;
- srs_context_t *srs = pl->srs;
- pa_mainloop_api *api = pa_mainloop_get_api(srs->pa);
-
- ioev = api->io_new(api, 0, PA_IO_EVENT_INPUT, handle_input, ctx);
-}
-
-/*************************************************************/
-
-
static int create_mpris2(srs_plugin_t *plugin)
{
clients_start(ctx);
- // input_create(ctx);
-
return TRUE;
}
#define MPRIS2_PREFIX "mpris2."
typedef enum player_state_e player_state_t;
+typedef enum track_e track_t;
typedef struct context_s context_t;
typedef struct dbusif_s dbusif_t;