592 lines
16 KiB
C
592 lines
16 KiB
C
/*
|
|
* Copyright 2016 Various Authors
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License as
|
|
* published by the Free Software Foundation; either version 2 of the
|
|
* License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#ifdef CONFIG_MPRIS_BASU
|
|
#include <basu/sd-bus.h>
|
|
#else
|
|
#include <systemd/sd-bus.h>
|
|
#endif
|
|
|
|
#include "mpris.h"
|
|
#include "ui_curses.h"
|
|
#include "cmus.h"
|
|
#include "player.h"
|
|
#include "options.h"
|
|
#include "output.h"
|
|
#include "track_info.h"
|
|
#include "utils.h"
|
|
#include "uchar.h"
|
|
#include "path.h"
|
|
|
|
#define CK(v) \
|
|
do { \
|
|
int tmp = (v); \
|
|
if (tmp < 0) \
|
|
return tmp; \
|
|
} while (0)
|
|
|
|
static sd_bus *bus;
|
|
int mpris_fd = -1;
|
|
|
|
static int mpris_msg_ignore(sd_bus_message *m, void *_userdata,
|
|
sd_bus_error *_ret_error)
|
|
{
|
|
return sd_bus_reply_method_return(m, "");
|
|
}
|
|
|
|
static int mpris_read_false(sd_bus *_bus, const char *_path,
|
|
const char *_interface, const char *_property,
|
|
sd_bus_message *reply, void *_userdata,
|
|
sd_bus_error *_ret_error)
|
|
{
|
|
uint32_t b = 0;
|
|
return sd_bus_message_append_basic(reply, 'b', &b);
|
|
}
|
|
|
|
static int mpris_read_true(sd_bus *_bus, const char *_path,
|
|
const char *_interface, const char *_property,
|
|
sd_bus_message *reply, void *_userdata,
|
|
sd_bus_error *_ret_error)
|
|
{
|
|
uint32_t b = 1;
|
|
return sd_bus_message_append_basic(reply, 'b', &b);
|
|
}
|
|
|
|
static int mpris_write_ignore(sd_bus *_bus, const char *_path,
|
|
const char *_interface, const char *_property,
|
|
sd_bus_message *value, void *_userdata,
|
|
sd_bus_error *_ret_error)
|
|
{
|
|
return sd_bus_reply_method_return(value, "");
|
|
}
|
|
|
|
static int mpris_raise_vte(sd_bus_message *m, void *_userdata,
|
|
sd_bus_error *_ret_error)
|
|
{
|
|
cmus_raise_vte();
|
|
return sd_bus_reply_method_return(m, "");
|
|
}
|
|
|
|
static int mpris_can_raise_vte(sd_bus *_bus, const char *_path,
|
|
const char *_interface, const char *_property,
|
|
sd_bus_message *reply, void *_userdata,
|
|
sd_bus_error *_ret_error)
|
|
{
|
|
uint32_t b = cmus_can_raise_vte();
|
|
return sd_bus_message_append_basic(reply, 'b', &b);
|
|
}
|
|
|
|
static int mpris_identity(sd_bus *_bus, const char *_path,
|
|
const char *_interface, const char *_property,
|
|
sd_bus_message *reply, void *_userdata,
|
|
sd_bus_error *_ret_error)
|
|
{
|
|
const char *id = "cmus";
|
|
return sd_bus_message_append_basic(reply, 's', id);
|
|
}
|
|
|
|
static int mpris_uri_schemes(sd_bus *_bus, const char *_path,
|
|
const char *_interface, const char *_property,
|
|
sd_bus_message *reply, void *_userdata,
|
|
sd_bus_error *_ret_error)
|
|
{
|
|
static const char * const schemes[] = { "file", "http", NULL };
|
|
return sd_bus_message_append_strv(reply, (char **)schemes);
|
|
}
|
|
|
|
static int mpris_mime_types(sd_bus *_bus, const char *_path,
|
|
const char *_interface, const char *_property,
|
|
sd_bus_message *reply, void *_userdata,
|
|
sd_bus_error *_ret_error)
|
|
{
|
|
static const char * const types[] = { NULL };
|
|
return sd_bus_message_append_strv(reply, (char **)types);
|
|
}
|
|
|
|
static int mpris_next(sd_bus_message *m, void *_userdata,
|
|
sd_bus_error *_ret_error)
|
|
{
|
|
cmus_next();
|
|
return sd_bus_reply_method_return(m, "");
|
|
}
|
|
|
|
static int mpris_prev(sd_bus_message *m, void *_userdata,
|
|
sd_bus_error *_ret_error)
|
|
{
|
|
cmus_prev();
|
|
return sd_bus_reply_method_return(m, "");
|
|
}
|
|
|
|
static int mpris_pause(sd_bus_message *m, void *_userdata,
|
|
sd_bus_error *_ret_error)
|
|
{
|
|
player_pause_playback();
|
|
return sd_bus_reply_method_return(m, "");
|
|
}
|
|
|
|
static int mpris_toggle_pause(sd_bus_message *m, void *_userdata,
|
|
sd_bus_error *_ret_error)
|
|
{
|
|
player_pause();
|
|
return sd_bus_reply_method_return(m, "");
|
|
}
|
|
|
|
static int mpris_stop(sd_bus_message *m, void *_userdata,
|
|
sd_bus_error *_ret_error)
|
|
{
|
|
player_stop();
|
|
return sd_bus_reply_method_return(m, "");
|
|
}
|
|
|
|
static int mpris_play(sd_bus_message *m, void *_userdata,
|
|
sd_bus_error *_ret_error)
|
|
{
|
|
player_play();
|
|
return sd_bus_reply_method_return(m, "");
|
|
}
|
|
|
|
static int mpris_seek(sd_bus_message *m, void *_userdata,
|
|
sd_bus_error *_ret_error)
|
|
{
|
|
int64_t val = 0;
|
|
CK(sd_bus_message_read_basic(m, 'x', &val));
|
|
player_seek(val / (1000 * 1000), 1, 0);
|
|
return sd_bus_reply_method_return(m, "");
|
|
}
|
|
|
|
static int mpris_seek_abs(sd_bus_message *m, void *_userdata,
|
|
sd_bus_error *_ret_error)
|
|
{
|
|
char buf[] = "/1122334455667788";
|
|
if (player_info.ti)
|
|
sprintf(buf, "/%"PRIx64, player_info.ti->uid);
|
|
else
|
|
sprintf(buf, "/");
|
|
|
|
const char *path = NULL;
|
|
int64_t val = 0;
|
|
CK(sd_bus_message_read_basic(m, 'o', &path));
|
|
CK(sd_bus_message_read_basic(m, 'x', &val));
|
|
|
|
if (strcmp(buf, path) == 0)
|
|
player_seek(val / (1000 * 1000), 0, 0);
|
|
return sd_bus_reply_method_return(m, "");
|
|
}
|
|
|
|
static int mpris_play_file(sd_bus_message *m, void *_userdata,
|
|
sd_bus_error *_ret_error)
|
|
{
|
|
const char *path = NULL;
|
|
CK(sd_bus_message_read_basic(m, 's', &path));
|
|
cmus_play_file(path);
|
|
return sd_bus_reply_method_return(m, "");
|
|
}
|
|
|
|
static int mpris_playback_status(sd_bus *_bus, const char *_path,
|
|
const char *_interface, const char *_property,
|
|
sd_bus_message *reply, void *_userdata,
|
|
sd_bus_error *_ret_error)
|
|
{
|
|
const char *ss[] = { "Stopped", "Playing", "Paused" };
|
|
const char *s = ss[player_info.status];
|
|
return sd_bus_message_append_basic(reply, 's', s);
|
|
}
|
|
|
|
static int mpris_loop_status(sd_bus *_bus, const char *_path,
|
|
const char *_interface, const char *_property,
|
|
sd_bus_message *reply, void *_userdata,
|
|
sd_bus_error *_ret_error)
|
|
{
|
|
const char *t = "None";
|
|
if (player_repeat_current)
|
|
t = "Track";
|
|
else if (repeat)
|
|
t = "Playlist";
|
|
return sd_bus_message_append_basic(reply, 's', t);
|
|
}
|
|
|
|
static int mpris_set_loop_status(sd_bus *_bus, const char *_path,
|
|
const char *_interface, const char *_property,
|
|
sd_bus_message *value, void *_userdata,
|
|
sd_bus_error *_ret_error)
|
|
{
|
|
const char *t = NULL;
|
|
CK(sd_bus_message_read_basic(value, 's', &t));
|
|
if (strcmp(t, "None") == 0) {
|
|
player_repeat_current = 0;
|
|
repeat = 0;
|
|
} else if (strcmp(t, "Track") == 0) {
|
|
player_repeat_current = 1;
|
|
} else if (strcmp(t, "Playlist") == 0) {
|
|
player_repeat_current = 0;
|
|
repeat = 1;
|
|
}
|
|
update_statusline();
|
|
return sd_bus_reply_method_return(value, "");
|
|
}
|
|
|
|
static int mpris_rate(sd_bus *_bus, const char *_path,
|
|
const char *_interface, const char *_property,
|
|
sd_bus_message *reply, void *_userdata,
|
|
sd_bus_error *_ret_error)
|
|
{
|
|
static const double d = 1.0;
|
|
return sd_bus_message_append_basic(reply, 'd', &d);
|
|
}
|
|
|
|
static int mpris_shuffle(sd_bus *_bus, const char *_path,
|
|
const char *_interface, const char *_property,
|
|
sd_bus_message *reply, void *_userdata,
|
|
sd_bus_error *_ret_error)
|
|
{
|
|
const uint32_t s = shuffle;
|
|
return sd_bus_message_append_basic(reply, 'b', &s);
|
|
}
|
|
|
|
static int mpris_set_shuffle(sd_bus *_bus, const char *_path,
|
|
const char *_interface, const char *_property,
|
|
sd_bus_message *value, void *_userdata,
|
|
sd_bus_error *_ret_error)
|
|
{
|
|
uint32_t s = 0;
|
|
CK(sd_bus_message_read_basic(value, 'b', &s));
|
|
shuffle = s;
|
|
update_statusline();
|
|
return sd_bus_reply_method_return(value, "");
|
|
}
|
|
|
|
static int mpris_volume(sd_bus *_bus, const char *_path,
|
|
const char *_interface, const char *_property,
|
|
sd_bus_message *reply, void *_userdata,
|
|
sd_bus_error *_ret_error)
|
|
{
|
|
double vol;
|
|
if (soft_vol) {
|
|
vol = (soft_vol_l + soft_vol_r) / 200.0;
|
|
} else if (volume_max && volume_l >= 0 && volume_r >= 0) {
|
|
int vol_left = scale_to_percentage(volume_l, volume_max);
|
|
int vol_right = scale_to_percentage(volume_r, volume_max);
|
|
vol = (vol_left + vol_right) / 200.0;
|
|
}
|
|
return sd_bus_message_append_basic(reply, 'd', &vol);
|
|
}
|
|
|
|
static int mpris_set_volume(sd_bus *_bus, const char *_path,
|
|
const char *_interface, const char *_property,
|
|
sd_bus_message *value, void *_userdata,
|
|
sd_bus_error *_ret_error)
|
|
{
|
|
double vol;
|
|
CK(sd_bus_message_read_basic(value, 'd', &vol));
|
|
if (vol < 0.0)
|
|
vol = 0.0;
|
|
else if (vol > 1.0)
|
|
vol = 1.0;
|
|
int ivol = vol * 100;
|
|
player_set_vol(ivol, VF_PERCENTAGE, ivol, VF_PERCENTAGE);
|
|
update_statusline();
|
|
return sd_bus_reply_method_return(value, "");
|
|
}
|
|
|
|
static int mpris_position(sd_bus *_bus, const char *_path,
|
|
const char *_interface, const char *_property,
|
|
sd_bus_message *reply, void *_userdata,
|
|
sd_bus_error *_ret_error)
|
|
{
|
|
int64_t pos = player_info.pos;
|
|
pos *= 1000 * 1000;
|
|
return sd_bus_message_append_basic(reply, 'x', &pos);
|
|
}
|
|
|
|
static int mpris_msg_append_simple_dict(sd_bus_message *m, const char *tag,
|
|
char type, const void *val)
|
|
{
|
|
const char s[] = { type, 0 };
|
|
CK(sd_bus_message_open_container(m, 'e', "sv"));
|
|
CK(sd_bus_message_append_basic(m, 's', tag));
|
|
CK(sd_bus_message_open_container(m, 'v', s));
|
|
CK(sd_bus_message_append_basic(m, type, val));
|
|
CK(sd_bus_message_close_container(m));
|
|
CK(sd_bus_message_close_container(m));
|
|
return 0;
|
|
}
|
|
|
|
static int mpris_msg_append_si_dict(sd_bus_message *m, const char *a,
|
|
int32_t i)
|
|
{
|
|
return mpris_msg_append_simple_dict(m, a, 'i', &i);
|
|
}
|
|
|
|
static int mpris_msg_append_sx_dict(sd_bus_message *m, const char *a,
|
|
int64_t i)
|
|
{
|
|
return mpris_msg_append_simple_dict(m, a, 'x', &i);
|
|
}
|
|
|
|
static int mpris_msg_append_ss_dict(sd_bus_message *m, const char *a,
|
|
const char *b)
|
|
{
|
|
return mpris_msg_append_simple_dict(m, a, 's', b);
|
|
}
|
|
|
|
static int mpris_msg_append_so_dict(sd_bus_message *m, const char *a,
|
|
const char *b)
|
|
{
|
|
return mpris_msg_append_simple_dict(m, a, 'o', b);
|
|
}
|
|
|
|
static int mpris_msg_append_sas_dict(sd_bus_message *m, const char *a,
|
|
const char *b)
|
|
{
|
|
CK(sd_bus_message_open_container(m, 'e', "sv"));
|
|
CK(sd_bus_message_append_basic(m, 's', a));
|
|
CK(sd_bus_message_open_container(m, 'v', "as"));
|
|
CK(sd_bus_message_open_container(m, 'a', "s"));
|
|
CK(sd_bus_message_append_basic(m, 's', b));
|
|
CK(sd_bus_message_close_container(m));
|
|
CK(sd_bus_message_close_container(m));
|
|
CK(sd_bus_message_close_container(m));
|
|
return 0;
|
|
}
|
|
|
|
static int mpris_metadata(sd_bus *_bus, const char *_path,
|
|
const char *_interface, const char *_property,
|
|
sd_bus_message *reply, void *_userdata,
|
|
sd_bus_error *_ret_error)
|
|
{
|
|
CK(sd_bus_message_open_container(reply, 'a', "{sv}"));
|
|
|
|
struct track_info *ti = player_info.ti;
|
|
if (ti) {
|
|
char buf[] = "/1122334455667788";
|
|
sprintf(buf, "/%"PRIx64, ti->uid);
|
|
CK(mpris_msg_append_so_dict(reply, "mpris:trackid", buf));
|
|
|
|
int64_t dur = ti->duration;
|
|
dur *= 1000 * 1000;
|
|
CK(mpris_msg_append_sx_dict(reply, "mpris:length", dur));
|
|
|
|
//The dbus connection closes if invalid data is sent.
|
|
//As a *temporary* fix, ensure all strings are encoded in utf8.
|
|
if (ti->artist) {
|
|
char corrected[u_str_print_size(ti->artist)];
|
|
u_to_utf8(corrected, ti->artist);
|
|
CK(mpris_msg_append_sas_dict(reply,
|
|
"xesam:artist", corrected));
|
|
}
|
|
if (ti->title) {
|
|
char corrected[u_str_print_size(ti->title)];
|
|
u_to_utf8(corrected, ti->title);
|
|
CK(mpris_msg_append_ss_dict(reply,
|
|
"xesam:title", corrected));
|
|
} else {
|
|
char corrected[u_str_print_size(path_basename(ti->filename))];
|
|
u_to_utf8(corrected, path_basename(ti->filename));
|
|
CK(mpris_msg_append_ss_dict(reply,
|
|
"xesam:title", corrected));
|
|
}
|
|
if (ti->album) {
|
|
char corrected[u_str_print_size(ti->album)];
|
|
u_to_utf8(corrected, ti->album);
|
|
CK(mpris_msg_append_ss_dict(reply,
|
|
"xesam:album", corrected));
|
|
}
|
|
if (ti->albumartist) {
|
|
char corrected[u_str_print_size(ti->albumartist)];
|
|
u_to_utf8(corrected, ti->albumartist);
|
|
CK(mpris_msg_append_sas_dict(reply,
|
|
"xesam:albumArtist", corrected));
|
|
}
|
|
if (ti->genre) {
|
|
char corrected[u_str_print_size(ti->genre)];
|
|
u_to_utf8(corrected, ti->genre);
|
|
CK(mpris_msg_append_sas_dict(reply,
|
|
"xesam:genre", corrected));
|
|
}
|
|
if (ti->comment) {
|
|
char corrected[u_str_print_size(ti->comment)];
|
|
u_to_utf8(corrected, ti->comment);
|
|
CK(mpris_msg_append_sas_dict(reply,
|
|
"xesam:comment", corrected));
|
|
}
|
|
if (ti->bpm != -1)
|
|
CK(mpris_msg_append_si_dict(reply, "xesam:audioBPM",
|
|
ti->bpm));
|
|
if (ti->tracknumber != -1)
|
|
CK(mpris_msg_append_si_dict(reply, "xesam:trackNumber",
|
|
ti->tracknumber));
|
|
if (ti->discnumber != -1)
|
|
CK(mpris_msg_append_si_dict(reply, "xesam:discNumber",
|
|
ti->discnumber));
|
|
if (is_http_url(ti->filename))
|
|
CK(mpris_msg_append_ss_dict(reply, "cmus:stream_title",
|
|
get_stream_title()));
|
|
}
|
|
|
|
CK(sd_bus_message_close_container(reply));
|
|
return 0;
|
|
}
|
|
|
|
#define MPRIS_PROP(name, type, read) \
|
|
SD_BUS_PROPERTY(name, type, read, 0, \
|
|
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE)
|
|
|
|
#define MPRIS_WPROP(name, type, read, write) \
|
|
SD_BUS_WRITABLE_PROPERTY(name, type, read, write, 0, \
|
|
SD_BUS_VTABLE_PROPERTY_EMITS_CHANGE)
|
|
|
|
static const sd_bus_vtable media_player2_vt[] = {
|
|
SD_BUS_VTABLE_START(0),
|
|
SD_BUS_METHOD("Raise", "", "", mpris_raise_vte, 0),
|
|
SD_BUS_METHOD("Quit", "", "", mpris_msg_ignore, 0),
|
|
MPRIS_PROP("CanQuit", "b", mpris_read_false),
|
|
MPRIS_WPROP("Fullscreen", "b", mpris_read_false, mpris_write_ignore),
|
|
MPRIS_PROP("CanSetFullscreen", "b", mpris_read_false),
|
|
MPRIS_PROP("CanRaise", "b", mpris_can_raise_vte),
|
|
MPRIS_PROP("HasTrackList", "b", mpris_read_false),
|
|
MPRIS_PROP("Identity", "s", mpris_identity),
|
|
MPRIS_PROP("SupportedUriSchemes", "as", mpris_uri_schemes),
|
|
MPRIS_PROP("SupportedMimeTypes", "as", mpris_mime_types),
|
|
SD_BUS_VTABLE_END,
|
|
};
|
|
|
|
static const sd_bus_vtable media_player2_player_vt[] = {
|
|
SD_BUS_VTABLE_START(0),
|
|
SD_BUS_METHOD("Next", "", "", mpris_next, 0),
|
|
SD_BUS_METHOD("Previous", "", "", mpris_prev, 0),
|
|
SD_BUS_METHOD("Pause", "", "", mpris_pause, 0),
|
|
SD_BUS_METHOD("PlayPause", "", "", mpris_toggle_pause, 0),
|
|
SD_BUS_METHOD("Stop", "", "", mpris_stop, 0),
|
|
SD_BUS_METHOD("Play", "", "", mpris_play, 0),
|
|
SD_BUS_METHOD("Seek", "x", "", mpris_seek, 0),
|
|
SD_BUS_METHOD("SetPosition", "ox", "", mpris_seek_abs, 0),
|
|
SD_BUS_METHOD("OpenUri", "s", "", mpris_play_file, 0),
|
|
MPRIS_PROP("PlaybackStatus", "s", mpris_playback_status),
|
|
MPRIS_WPROP("LoopStatus", "s", mpris_loop_status, mpris_set_loop_status),
|
|
MPRIS_WPROP("Rate", "d", mpris_rate, mpris_write_ignore),
|
|
MPRIS_WPROP("Shuffle", "b", mpris_shuffle, mpris_set_shuffle),
|
|
MPRIS_WPROP("Volume", "d", mpris_volume, mpris_set_volume),
|
|
SD_BUS_PROPERTY("Position", "x", mpris_position, 0, 0),
|
|
MPRIS_PROP("MinimumRate", "d", mpris_rate),
|
|
MPRIS_PROP("MaximumRate", "d", mpris_rate),
|
|
MPRIS_PROP("CanGoNext", "b", mpris_read_true),
|
|
MPRIS_PROP("CanGoPrevious", "b", mpris_read_true),
|
|
MPRIS_PROP("CanPlay", "b", mpris_read_true),
|
|
MPRIS_PROP("CanPause", "b", mpris_read_true),
|
|
MPRIS_PROP("CanSeek", "b", mpris_read_true),
|
|
SD_BUS_PROPERTY("CanControl", "b", mpris_read_true, 0, 0),
|
|
MPRIS_PROP("Metadata", "a{sv}", mpris_metadata),
|
|
SD_BUS_SIGNAL("Seeked", "x", 0),
|
|
SD_BUS_VTABLE_END,
|
|
};
|
|
|
|
void mpris_init(void)
|
|
{
|
|
int res = 0;
|
|
|
|
res = sd_bus_default_user(&bus);
|
|
if (res < 0)
|
|
goto out;
|
|
res = sd_bus_add_object_vtable(bus, NULL, "/org/mpris/MediaPlayer2",
|
|
"org.mpris.MediaPlayer2", media_player2_vt, NULL);
|
|
if (res < 0)
|
|
goto out;
|
|
res = sd_bus_add_object_vtable(bus, NULL, "/org/mpris/MediaPlayer2",
|
|
"org.mpris.MediaPlayer2.Player",
|
|
media_player2_player_vt, NULL);
|
|
if (res < 0)
|
|
goto out;
|
|
res = sd_bus_request_name(bus, "org.mpris.MediaPlayer2.cmus", 0);
|
|
mpris_fd = sd_bus_get_fd(bus);
|
|
|
|
out:
|
|
if (res < 0) {
|
|
sd_bus_unref(bus);
|
|
bus = NULL;
|
|
mpris_fd = -1;
|
|
|
|
const char *msg = "an error occurred while initializing "
|
|
"MPRIS: %s. MPRIS will be disabled.";
|
|
|
|
error_msg(msg, strerror(-res));
|
|
}
|
|
}
|
|
|
|
void mpris_process(void)
|
|
{
|
|
if (bus) {
|
|
while (sd_bus_process(bus, NULL) > 0)
|
|
;
|
|
}
|
|
}
|
|
|
|
void mpris_free(void)
|
|
{
|
|
sd_bus_unref(bus);
|
|
bus = NULL;
|
|
mpris_fd = -1;
|
|
}
|
|
|
|
static void mpris_player_property_changed(const char *name)
|
|
{
|
|
const char * const strv[] = { name, NULL };
|
|
if (bus) {
|
|
sd_bus_emit_properties_changed_strv(bus,
|
|
"/org/mpris/MediaPlayer2",
|
|
"org.mpris.MediaPlayer2.Player", (char **)strv);
|
|
sd_bus_flush(bus);
|
|
}
|
|
}
|
|
|
|
void mpris_playback_status_changed(void)
|
|
{
|
|
mpris_player_property_changed("PlaybackStatus");
|
|
}
|
|
|
|
void mpris_loop_status_changed(void)
|
|
{
|
|
mpris_player_property_changed("LoopStatus");
|
|
}
|
|
|
|
void mpris_shuffle_changed(void)
|
|
{
|
|
mpris_player_property_changed("Shuffle");
|
|
}
|
|
|
|
void mpris_volume_changed(void)
|
|
{
|
|
mpris_player_property_changed("Volume");
|
|
}
|
|
|
|
void mpris_metadata_changed(void)
|
|
{
|
|
mpris_player_property_changed("Metadata");
|
|
// the following is not necessary according to the spec but some
|
|
// applications seem to disregard the spec and expect this to happen
|
|
mpris_seeked();
|
|
}
|
|
|
|
void mpris_seeked(void)
|
|
{
|
|
if (!bus)
|
|
return;
|
|
int64_t pos = player_info.pos;
|
|
pos *= 1000 * 1000;
|
|
sd_bus_emit_signal(bus, "/org/mpris/MediaPlayer2",
|
|
"org.mpris.MediaPlayer2.Player", "Seeked", "x", pos);
|
|
}
|