[gnome-settings-daemon] media-keys: Use MPRIS when no apps use the media-keys API



commit cae0fc064b6df1edfc00ee39e6d844d375156f6b
Author: Michael Wood <michael g wood intel com>
Date:   Thu Aug 1 09:55:57 2013 +0200

    media-keys: Use MPRIS when no apps use the media-keys API
    
    If the media-keys plugin gets a Play or Pause key events and we
    don't have any clients registered to handle it through the native
    media-keys API, look for an MPRIS interface using bus namespace
    watching and try that.
    
    This fixes integration of Spotify for example, as it's unlikely to
    implement a GNOME specific interface.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=697810

 plugins/media-keys/Makefile.am              |    4 +
 plugins/media-keys/gsd-media-keys-manager.c |   15 ++-
 plugins/media-keys/mpris-controller.c       |  209 +++++++++++++++++++++++++++
 plugins/media-keys/mpris-controller.h       |   56 +++++++
 4 files changed, 281 insertions(+), 3 deletions(-)
---
diff --git a/plugins/media-keys/Makefile.am b/plugins/media-keys/Makefile.am
index 6b82014..28d1c37 100644
--- a/plugins/media-keys/Makefile.am
+++ b/plugins/media-keys/Makefile.am
@@ -38,6 +38,8 @@ libmedia_keys_la_SOURCES =            \
        shell-keybinding-modes.h        \
        bus-watch-namespace.c           \
        bus-watch-namespace.h           \
+       mpris-controller.c              \
+       mpris-controller.h              \
        $(BUILT_SOURCES)                \
        $(NULL)
 
@@ -82,6 +84,8 @@ gsd_test_media_keys_SOURCES =                 \
        gsd-screenshot-utils.c                  \
        bus-watch-namespace.c                   \
        bus-watch-namespace.h                   \
+       mpris-controller.c                      \
+       mpris-controller.h                      \
        test-media-keys.c                       \
        $(BUILT_SOURCES)                        \
        $(NULL)
diff --git a/plugins/media-keys/gsd-media-keys-manager.c b/plugins/media-keys/gsd-media-keys-manager.c
index 6bae5e3..030146a 100644
--- a/plugins/media-keys/gsd-media-keys-manager.c
+++ b/plugins/media-keys/gsd-media-keys-manager.c
@@ -44,6 +44,7 @@
 #include <gudev/gudev.h>
 #endif
 
+#include "mpris-controller.h"
 #include "gnome-settings-plugin.h"
 #include "gnome-settings-session.h"
 #include "gnome-settings-profile.h"
@@ -184,6 +185,8 @@ struct GsdMediaKeysManagerPrivate
         GCancellable    *cancellable;
 
         guint            start_idle_id;
+
+        MprisController *mpris_controller;
 };
 
 static void     gsd_media_keys_manager_class_init  (GsdMediaKeysManagerClass *klass);
@@ -1511,9 +1514,11 @@ gsd_media_player_key_pressed (GsdMediaKeysManager *manager,
         have_listeners = (manager->priv->media_players != NULL);
 
         if (!have_listeners) {
-                /* Popup a dialog with an (/) icon */
-                show_osd (manager, "action-unavailable-symbolic", NULL, -1);
-                return TRUE;
+                if (!mpris_controller_key (manager->priv->mpris_controller, key)) {
+                        /* Popup a dialog with an (/) icon */
+                        show_osd (manager, "action-unavailable-symbolic", NULL, -1);
+                }
+               return TRUE;
         }
 
         player = manager->priv->media_players->data;
@@ -2332,6 +2337,9 @@ start_media_keys_idle_cb (GsdMediaKeysManager *manager)
                                                          on_shell_vanished,
                                                          manager, NULL);
 
+        g_debug ("Starting mpris controller");
+        manager->priv->mpris_controller = mpris_controller_new ();
+
         gnome_settings_profile_end (NULL);
 
         manager->priv->start_idle_id = 0;
@@ -2393,6 +2401,7 @@ gsd_media_keys_manager_stop (GsdMediaKeysManager *manager)
         g_clear_object (&priv->power_proxy);
         g_clear_object (&priv->power_screen_proxy);
         g_clear_object (&priv->power_keyboard_proxy);
+        g_clear_object (&priv->mpris_controller);
 
         if (manager->priv->name_owner_id) {
                 g_bus_unwatch_name (manager->priv->name_owner_id);
diff --git a/plugins/media-keys/mpris-controller.c b/plugins/media-keys/mpris-controller.c
new file mode 100644
index 0000000..0e6622e
--- /dev/null
+++ b/plugins/media-keys/mpris-controller.c
@@ -0,0 +1,209 @@
+/*
+ * Copyright © 2013 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 Lesser General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses>
+ *
+ * Author: Michael Wood <michael g wood intel com>
+ */
+
+#include "mpris-controller.h"
+#include "bus-watch-namespace.h"
+#include <gio/gio.h>
+
+G_DEFINE_TYPE (MprisController, mpris_controller, G_TYPE_OBJECT)
+
+#define CONTROLLER_PRIVATE(o) \
+  (G_TYPE_INSTANCE_GET_PRIVATE ((o), MPRIS_TYPE_CONTROLLER, MprisControllerPrivate))
+
+struct _MprisControllerPrivate
+{
+  GCancellable *cancellable;
+  GDBusProxy *mpris_client_proxy;
+  guint namespace_watcher_id;
+  GSList *other_players;
+  gboolean connecting;
+};
+
+
+static void
+mpris_controller_dispose (GObject *object)
+{
+  MprisControllerPrivate *priv = MPRIS_CONTROLLER (object)->priv;
+
+  g_clear_object (&priv->cancellable);
+  g_clear_object (&priv->mpris_client_proxy);
+
+  if (priv->namespace_watcher_id)
+    {
+      bus_unwatch_namespace (priv->namespace_watcher_id);
+      priv->namespace_watcher_id = 0;
+    }
+
+  if (priv->other_players)
+    {
+      g_slist_free_full (priv->other_players, g_free);
+      priv->other_players = NULL;
+    }
+
+  G_OBJECT_CLASS (mpris_controller_parent_class)->dispose (object);
+}
+
+static void
+mpris_proxy_call_done (GObject      *object,
+                       GAsyncResult *res,
+                       gpointer      user_data)
+{
+  GError *error = NULL;
+  GVariant *ret;
+
+  if (!(ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (object), res, &error)))
+    {
+      g_warning ("Error calling method %s", error->message);
+      g_clear_error (&error);
+      return;
+    }
+  g_variant_unref (ret);
+}
+
+gboolean
+mpris_controller_key (MprisController *self, const gchar *key)
+{
+  MprisControllerPrivate *priv = MPRIS_CONTROLLER (self)->priv;
+
+  if (!priv->mpris_client_proxy)
+    return FALSE;
+
+  g_debug ("calling %s over dbus to mpris client %s",
+           key, g_dbus_proxy_get_name (priv->mpris_client_proxy));
+  g_dbus_proxy_call (priv->mpris_client_proxy,
+                     key, NULL, 0, -1, priv->cancellable,
+                     mpris_proxy_call_done,
+                     NULL);
+  return TRUE;
+}
+
+static void
+mpris_proxy_ready_cb (GObject      *object,
+                      GAsyncResult *res,
+                      gpointer      user_data)
+{
+  MprisControllerPrivate *priv = MPRIS_CONTROLLER (user_data)->priv;
+  GError *error = NULL;
+
+  priv->mpris_client_proxy = g_dbus_proxy_new_for_bus_finish (res, &error);
+
+  if (!priv->mpris_client_proxy)
+    g_warning ("Error connecting to mpris interface %s", error->message);
+
+  g_clear_error (&error);
+}
+
+static void
+start_mpris_proxy (MprisController *self, const gchar *name)
+{
+  MprisControllerPrivate *priv = MPRIS_CONTROLLER (self)->priv;
+
+  g_debug ("Creating proxy for for %s", name);
+  g_dbus_proxy_new_for_bus (G_BUS_TYPE_SESSION,
+                            0,
+                            NULL,
+                            name,
+                            "/org/mpris/MediaPlayer2",
+                            "org.mpris.MediaPlayer2.Player",
+                            priv->cancellable,
+                            mpris_proxy_ready_cb,
+                            self);
+  priv->connecting = TRUE;
+}
+
+static void
+mpris_player_appeared (GDBusConnection *connection,
+                       const gchar     *name,
+                       const gchar     *name_owner,
+                       gpointer         user_data)
+{
+  MprisController *self = user_data;
+  MprisControllerPrivate *priv = MPRIS_CONTROLLER (self)->priv;
+
+  if (priv->mpris_client_proxy == NULL && !priv->connecting)
+    start_mpris_proxy (self, name);
+  else
+    self->priv->other_players = g_slist_prepend (self->priv->other_players, g_strdup (name));
+}
+
+static void
+mpris_player_vanished (GDBusConnection *connection,
+                       const gchar     *name,
+                       gpointer         user_data)
+{
+  MprisController *self = user_data;
+  MprisControllerPrivate *priv = MPRIS_CONTROLLER (self)->priv;
+
+  if (priv->mpris_client_proxy &&
+      g_strcmp0 (name, g_dbus_proxy_get_name (priv->mpris_client_proxy)) == 0)
+    {
+      g_clear_object (&priv->mpris_client_proxy);
+
+      /* take the next one if there's one */
+      if (self->priv->other_players && !priv->connecting)
+        {
+          GSList *first;
+          gchar *name;
+
+          first = self->priv->other_players;
+          name = first->data;
+
+          start_mpris_proxy (self, name);
+
+          self->priv->other_players = self->priv->other_players->next;
+          g_free (name);
+          g_slist_free_1 (first);
+        }
+    }
+}
+
+static void
+mpris_controller_constructed (GObject *object)
+{
+  MprisControllerPrivate *priv = MPRIS_CONTROLLER (object)->priv;
+
+  priv->namespace_watcher_id = bus_watch_namespace (G_BUS_TYPE_SESSION,
+                                                    "org.mpris.MediaPlayer2",
+                                                    mpris_player_appeared,
+                                                    mpris_player_vanished,
+                                                    MPRIS_CONTROLLER (object),
+                                                    NULL);
+}
+
+static void
+mpris_controller_class_init (MprisControllerClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (MprisControllerPrivate));
+
+  object_class->constructed = mpris_controller_constructed;
+  object_class->dispose = mpris_controller_dispose;
+}
+
+static void
+mpris_controller_init (MprisController *self)
+{
+  self->priv = CONTROLLER_PRIVATE (self);
+}
+
+MprisController *
+mpris_controller_new (void)
+{
+  return g_object_new (MPRIS_TYPE_CONTROLLER, NULL);
+}
diff --git a/plugins/media-keys/mpris-controller.h b/plugins/media-keys/mpris-controller.h
new file mode 100644
index 0000000..5333d4e
--- /dev/null
+++ b/plugins/media-keys/mpris-controller.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright © 2013 Intel Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU Lesser General Public License,
+ * version 2.1, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 Lesser General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses>
+ *
+ * Author: Michael Wood <michael g wood intel com>
+ */
+
+#ifndef __MPRIS_CONTROLLER_H__
+#define __MPRIS_CONTROLLER_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define MPRIS_TYPE_CONTROLLER mpris_controller_get_type()
+#define MPRIS_CONTROLLER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MPRIS_TYPE_CONTROLLER, MprisController))
+#define MPRIS_CONTROLLER_CLASS(klass)  (G_TYPE_CHECK_CLASS_CAST ((klass), MPRIS_TYPE_CONTROLLER, 
MprisControllerClass))
+#define MPRIS_IS_CONTROLLER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MPRIS_TYPE_CONTROLLER))
+#define MPRIS_IS_CONTROLLER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MPRIS_TYPE_CONTROLLER))
+#define MPRIS_CONTROLLER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MPRIS_TYPE_CONTROLLER, 
MprisControllerClass))
+
+typedef struct _MprisController MprisController;
+typedef struct _MprisControllerClass MprisControllerClass;
+typedef struct _MprisControllerPrivate MprisControllerPrivate;
+
+struct _MprisController
+{
+  GObject parent;
+
+  MprisControllerPrivate *priv;
+};
+
+struct _MprisControllerClass
+{
+  GObjectClass parent_class;
+};
+
+GType mpris_controller_get_type (void) G_GNUC_CONST;
+
+MprisController *mpris_controller_new (void);
+gboolean         mpris_controller_key (MprisController *self, const gchar *key);
+
+G_END_DECLS
+
+#endif /* __MPRIS_CONTROLLER_H__ */


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]