diff --git a/plugins/media-keys/gsd-media-keys-manager.c b/plugins/media-keys/gsd-media-keys-manager.c index b5f9506db5d451a9362349f6fd6ec47b99c8f73e..166f311a22c6f486e9a2ed54f82e7c5947f48336 100644 --- a/plugins/media-keys/gsd-media-keys-manager.c +++ b/plugins/media-keys/gsd-media-keys-manager.c @@ -119,6 +119,9 @@ static const gchar introspection_xml[] = #define TOUCHPAD_ENABLED_KEY "send-events" #define HIGH_CONTRAST "HighContrast" +#define REWIND_USEC (-10 * G_USEC_PER_SEC) +#define FASTFORWARD_USEC (45 * G_USEC_PER_SEC) + #define VOLUME_STEP "volume-step" #define VOLUME_STEP_PRECISE 2 #define MAX_VOLUME 65536.0 @@ -1888,8 +1891,21 @@ gsd_media_player_key_pressed (GsdMediaKeysManager *manager, /* Prefer MPRIS players to those using the native API */ if (mpris_controller_get_has_active_player (priv->mpris_controller)) { - if (mpris_controller_key (priv->mpris_controller, key)) + if (g_str_equal (key, "Rewind")) { + if (mpris_controller_seek (priv->mpris_controller, REWIND_USEC)) + return TRUE; + } else if (g_str_equal (key, "FastForward")) { + if (mpris_controller_seek (priv->mpris_controller, FASTFORWARD_USEC)) + return TRUE; + } else if (g_str_equal (key, "Repeat")) { + if (mpris_controller_toggle (priv->mpris_controller, "LoopStatus")) + return TRUE; + } else if (g_str_equal (key, "Shuffle")) { + if (mpris_controller_toggle (priv->mpris_controller, "Shuffle")) + return TRUE; + } else if (mpris_controller_key (priv->mpris_controller, key)) { return TRUE; + } } have_listeners = (priv->media_players != NULL); diff --git a/plugins/media-keys/mpris-controller.c b/plugins/media-keys/mpris-controller.c index c70f0bc1a7dcb5388aa18cbd0cf81175fa07639a..d60d362ec1ab932fb36aef3f34f9be67b9a72c36 100644 --- a/plugins/media-keys/mpris-controller.c +++ b/plugins/media-keys/mpris-controller.c @@ -77,6 +77,9 @@ mpris_proxy_call_done (GObject *object, gboolean mpris_controller_key (MprisController *self, const gchar *key) { + g_return_val_if_fail (MPRIS_IS_CONTROLLER (self), FALSE); + g_return_val_if_fail (key != NULL, FALSE); + if (!self->mpris_client_proxy) return FALSE; @@ -92,6 +95,117 @@ mpris_controller_key (MprisController *self, const gchar *key) return TRUE; } +gboolean +mpris_controller_seek (MprisController *self, gint64 offset) +{ + g_return_val_if_fail (MPRIS_IS_CONTROLLER (self), FALSE); + + if (!self->mpris_client_proxy) + return FALSE; + + g_debug ("calling Seek over dbus to mpris client %s", + g_dbus_proxy_get_name (self->mpris_client_proxy)); + g_dbus_proxy_call (self->mpris_client_proxy, + "Seek", g_variant_new ("(x)", offset, NULL), + G_DBUS_CALL_FLAGS_NONE, -1, self->cancellable, + mpris_proxy_call_done, + NULL); + return TRUE; +} + +static GDBusProxy * +get_props_proxy (GDBusProxy *proxy, + GCancellable *cancellable) +{ + GDBusProxy *props = NULL; + g_autoptr(GError) error = NULL; + + g_return_val_if_fail (proxy != NULL, NULL); + + props = g_dbus_proxy_new_sync (g_dbus_proxy_get_connection (proxy), + G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS | G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES, + NULL, + g_dbus_proxy_get_name (proxy), + g_dbus_proxy_get_object_path (proxy), + "org.freedesktop.DBus.Properties", + cancellable, + &error); + if (!props) { + g_debug ("Could not get properties proxy for %s: %s", + g_dbus_proxy_get_interface_name (proxy), + error->message); + return NULL; + } + + return props; +} + +gboolean +mpris_controller_toggle (MprisController *self, const gchar *property) +{ + g_return_val_if_fail (MPRIS_IS_CONTROLLER (self), FALSE); + g_return_val_if_fail (property != NULL, FALSE); + + if (!self->mpris_client_proxy) + return FALSE; + + if (g_str_equal (property, "LoopStatus")) { + g_autoptr(GDBusProxy) props = NULL; + g_autoptr(GVariant) loop_status; + const gchar *status_str, *new_status; + + loop_status = g_dbus_proxy_get_cached_property (self->mpris_client_proxy, "LoopStatus"); + if (!loop_status) + return FALSE; + if (!g_variant_is_of_type (loop_status, G_VARIANT_TYPE_STRING)) + return FALSE; + status_str = g_variant_get_string (loop_status, NULL); + if (g_str_equal (status_str, "Playlist")) + new_status = "None"; + else + new_status = "Playlist"; + + props = get_props_proxy (self->mpris_client_proxy, self->cancellable); + if (!props) + return FALSE; + g_dbus_proxy_call (props, + "Set", + g_variant_new_parsed ("('org.mpris.MediaPlayer2.Player', 'LoopStatus', %v)", + g_variant_new_string (new_status)), + G_DBUS_CALL_FLAGS_NONE, + -1, + self->cancellable, + mpris_proxy_call_done, NULL); + } else if (g_str_equal (property, "Shuffle")) { + g_autoptr(GDBusProxy) props = NULL; + g_autoptr(GVariant) shuffle_status; + gboolean status; + + shuffle_status = g_dbus_proxy_get_cached_property (self->mpris_client_proxy, "Shuffle"); + if (!shuffle_status) + return FALSE; + if (!g_variant_is_of_type (shuffle_status, G_VARIANT_TYPE_BOOLEAN)) + return FALSE; + status = g_variant_get_boolean (shuffle_status); + + props = get_props_proxy (self->mpris_client_proxy, self->cancellable); + if (!props) + return FALSE; + g_dbus_proxy_call (props, + "Set", + g_variant_new_parsed ("('org.mpris.MediaPlayer2.Player', 'Shuffle', %v)", + g_variant_new_boolean (!status)), + G_DBUS_CALL_FLAGS_NONE, + -1, + self->cancellable, + mpris_proxy_call_done, NULL); + } + + g_debug ("Unhandled toggle property '%s'", property); + + return TRUE; +} + static gboolean mpris_client_is_playing (GDBusProxy *proxy) { diff --git a/plugins/media-keys/mpris-controller.h b/plugins/media-keys/mpris-controller.h index a0044c4dfa01c6390692d43464ff416a5731c105..e44bc9045ef1280c5e00a95dc464fc3da33ee3c7 100644 --- a/plugins/media-keys/mpris-controller.h +++ b/plugins/media-keys/mpris-controller.h @@ -29,6 +29,8 @@ G_DECLARE_FINAL_TYPE (MprisController, mpris_controller, MPRIS, CONTROLLER, GObj MprisController *mpris_controller_new (void); gboolean mpris_controller_key (MprisController *self, const gchar *key); +gboolean mpris_controller_seek (MprisController *self, gint64 offset); +gboolean mpris_controller_toggle (MprisController *self, const gchar *property); gboolean mpris_controller_get_has_active_player (MprisController *controller); G_END_DECLS