[rygel-grilo] Notify when provider comes and goes.



commit 1c09e8899a23277789a6486f07d26673b643ec08
Author: Juan A. Suarez Romero <jasuarez igalia com>
Date:   Thu Apr 22 16:13:12 2010 +0200

    Notify when provider comes and goes.
    
    Clients must be notified when a provider goes away.
    
    The same when a new provider appears.
    
    First part is through a signal, "destroy", which will tell user that provider
    have been removed.
    
    Second part is done with an observer, which will emit the "new" signal
    everytime a new provider comes up.

 lib/Makefile.am                                    |    6 +-
 lib/media-server2-client.c                         |   77 ++++++-
 lib/media-server2-client.h                         |    4 +
 lib/media-server2-observer.c                       |  224 ++++++++++++++++++++
 lib/media-server2-observer.h                       |   83 +++++++
 ...r2-server-private.h => media-server2-private.h} |   20 ++-
 lib/media-server2-server.c                         |    5 +-
 src/test-client.c                                  |   89 ++++++++-
 8 files changed, 490 insertions(+), 18 deletions(-)
---
diff --git a/lib/Makefile.am b/lib/Makefile.am
index e5df246..10db94d 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -30,14 +30,16 @@ libmediaserver2_la_LIBADD =	\
 	$(DEPS_LIBS)
 
 libmediaserver2_la_SOURCES =		\
+	media-server2-private.h		\
 	media-server2-common.h		\
 	media-server2-server-glue.h	\
-	media-server2-server-private.h	\
 	media-server2-server.h		\
 	media-server2-server.c		\
 	media-server2-client-glue.h	\
 	media-server2-client.h		\
-	media-server2-client.c
+	media-server2-client.c		\
+	media-server2-observer.h	\
+	media-server2-observer.c
 
 MAINTAINERCLEANFILES =	\
 	*.in
diff --git a/lib/media-server2-client.c b/lib/media-server2-client.c
index 0992335..b8e4da0 100644
--- a/lib/media-server2-client.c
+++ b/lib/media-server2-client.c
@@ -24,16 +24,10 @@
 #include <dbus/dbus-glib.h>
 #include <string.h>
 
+#include "media-server2-private.h"
 #include "media-server2-client-glue.h"
 #include "media-server2-client.h"
 
-#define MS2_DBUS_SERVICE_PREFIX "org.gnome.UPnP.MediaServer2."
-#define MS2_DBUS_PATH_PREFIX    "/org/gnome/UPnP/MediaServer2/"
-#define MS2_DBUS_IFACE          "org.gnome.UPnP.MediaServer2"
-
-#define ENTRY_POINT_IFACE "/org/gnome/UPnP/MediaServer2/"
-#define ENTRY_POINT_NAME  "org.gnome.UPnP.MediaServer2."
-
 #define DBUS_TYPE_G_ARRAY_OF_STRING                             \
   (dbus_g_type_get_collection ("GPtrArray", G_TYPE_STRING))
 
@@ -48,6 +42,7 @@
 
 enum {
   UPDATED,
+  DESTROY,
   LAST_SIGNAL
 };
 
@@ -72,9 +67,11 @@ typedef struct {
 /*
  * Private MS2Client structure
  *   proxy_provider: a dbus proxy of content provider
+ *   name: name of provider
  */
 struct _MS2ClientPrivate {
   DBusGProxy *proxy_provider;
+  gchar *name;
 };
 
 static guint32 signals[LAST_SIGNAL] = { 0 };
@@ -208,6 +205,8 @@ ms2_client_dispose (GObject *object)
 {
   MS2Client *client = MS2_CLIENT (object);
 
+  ms2_observer_remove_client (client, client->priv->name);
+
   if (client->priv->proxy_provider) {
     g_object_unref (client->priv->proxy_provider);
     client->priv->proxy_provider = NULL;
@@ -216,6 +215,16 @@ ms2_client_dispose (GObject *object)
   G_OBJECT_CLASS (ms2_client_parent_class)->dispose (object);
 }
 
+static void
+ms2_client_finalize (GObject *object)
+{
+  MS2Client *client = MS2_CLIENT (object);
+
+  g_free (client->priv->name);
+
+  G_OBJECT_CLASS (ms2_client_parent_class)->finalize (object);
+}
+
 /* Class init function */
 static void
 ms2_client_class_init (MS2ClientClass *klass)
@@ -225,6 +234,7 @@ ms2_client_class_init (MS2ClientClass *klass)
   g_type_class_add_private (klass, sizeof (MS2ClientPrivate));
 
   gobject_class->dispose = ms2_client_dispose;
+  gobject_class->finalize = ms2_client_finalize;
 
   /**
    * MS2Client::updated:
@@ -243,6 +253,27 @@ ms2_client_class_init (MS2ClientClass *klass)
                                    G_TYPE_NONE,
                                    1,
                                    G_TYPE_STRING);
+
+  /**
+   * MS2Client::destroy:
+   * @client: a #MS2Client
+   *
+   * Notifies when a client is going to be destroyed. Usually this happens when
+   * provider goes away.
+   *
+   * User should not use the object, as provider is not present.
+   *
+   * After this, client will be automatically destroyed.
+   **/
+  signals[DESTROY] = g_signal_new ("destroy",
+                                   G_TYPE_FROM_CLASS (klass),
+                                   G_SIGNAL_RUN_LAST | G_SIGNAL_RUN_CLEANUP,
+                                   G_STRUCT_OFFSET (MS2ClientClass, destroy),
+                                   NULL,
+                                   NULL,
+                                   g_cclosure_marshal_VOID__VOID,
+                                   G_TYPE_NONE,
+                                   0);
 }
 
 /* Object init function */
@@ -252,6 +283,18 @@ ms2_client_init (MS2Client *client)
   client->priv = MS2_CLIENT_GET_PRIVATE (client);
 }
 
+/****************** INTERNAL PUBLIC API (NOT TO BE EXPORTED) ******************/
+
+/* Notify destruction of client, and unref it */
+void
+ms2_client_notify_unref (MS2Client *client)
+{
+  g_return_if_fail (MS2_IS_CLIENT (client));
+
+  g_signal_emit (client, signals[DESTROY], 0);
+  g_object_unref (client);
+}
+
 /******************** PUBLIC API ********************/
 
 /**
@@ -372,16 +415,34 @@ MS2Client *ms2_client_new (const gchar *provider)
 
   client = g_object_new (MS2_TYPE_CLIENT, NULL);
   client->priv->proxy_provider = gproxy;
+  client->priv->name = g_strdup (provider);
+
+  ms2_observer_add_client (client, provider);
 
   /* Listen to "updated" signal */
   dbus_g_proxy_add_signal (gproxy, "Updated", G_TYPE_STRING, G_TYPE_INVALID);
-  g_return_val_if_fail (MS2_IS_CLIENT(client), NULL);
   dbus_g_proxy_connect_signal (gproxy, "Updated", G_CALLBACK (updated), client, NULL);
 
   return client;
 }
 
 /**
+ * ms2_client_get_provider_name:
+ * @client: a #MS2Client
+ *
+ * Returns name of provider which client is attending
+ *
+ * Returns: name of provider
+ **/
+const gchar *
+ms2_client_get_provider_name (MS2Client *client)
+{
+  g_return_val_if_fail (MS2_IS_CLIENT (client), NULL);
+
+  return client->priv->name;
+}
+
+/**
  * ms2_client_get_properties:
  * @client: a #MS2Client
  * @id: media identifier to obtain properties from
diff --git a/lib/media-server2-client.h b/lib/media-server2-client.h
index adeb623..64bfbfa 100644
--- a/lib/media-server2-client.h
+++ b/lib/media-server2-client.h
@@ -74,6 +74,8 @@ struct _MS2ClientClass {
 
   void (*updated) (MS2Client *client,
                    const gchar *id);
+
+  void (*destroy) (MS2Client *client);
 };
 
 GType ms2_client_get_type (void);
@@ -82,6 +84,8 @@ gchar **ms2_client_get_providers (void);
 
 MS2Client *ms2_client_new (const gchar *provider);
 
+const gchar *ms2_client_get_provider_name (MS2Client *client);
+
 GHashTable *ms2_client_get_properties (MS2Client *client,
                                        const gchar *id,
                                        const gchar **properties,
diff --git a/lib/media-server2-observer.c b/lib/media-server2-observer.c
new file mode 100644
index 0000000..9bf360c
--- /dev/null
+++ b/lib/media-server2-observer.c
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2010 Igalia S.L.
+ *
+ * Authors: Juan A. Suarez Romero <jasuarez igalia com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#include <dbus/dbus-glib-bindings.h>
+#include <dbus/dbus-glib.h>
+
+#include "media-server2-private.h"
+#include "media-server2-observer.h"
+#include "media-server2-client.h"
+
+#define ENTRY_POINT_NAME "org.gnome.UPnP.MediaServer2."
+#define ENTRY_POINT_NAME_LENGTH 28
+
+#define MS2_OBSERVER_GET_PRIVATE(o)                                     \
+  G_TYPE_INSTANCE_GET_PRIVATE((o), MS2_TYPE_OBSERVER, MS2ObserverPrivate)
+
+enum {
+  NEW,
+  LAST_SIGNAL
+};
+
+/*
+ * Private MS2Observer structure
+ *   clients: a table with the clients
+ *   proxy: proxy to dbus service
+ */
+struct _MS2ObserverPrivate {
+  GHashTable *clients;
+  DBusGProxy *proxy;
+};
+
+static MS2Observer *observer_instance = NULL;
+static guint32 signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (MS2Observer, ms2_observer, G_TYPE_OBJECT);
+
+/******************** PRIVATE API ********************/
+
+static void
+name_owner_changed (DBusGProxy *proxy,
+                    const gchar *name,
+                    const gchar *old_owner,
+                    const gchar *new_owner,
+                    MS2Observer *observer)
+{
+  GList *clients;
+
+  /* Check if it has something to do with the spec */
+  if (!g_str_has_prefix (name, MS2_DBUS_SERVICE_PREFIX)) {
+    return;
+  }
+
+  name += MS2_DBUS_SERVICE_PREFIX_LENGTH;
+
+  /* Check if it has been removed */
+  if (*new_owner == '\0') {
+    clients = g_hash_table_lookup (observer->priv->clients, name);
+    g_list_foreach (clients, (GFunc) ms2_client_notify_unref, NULL);
+    return;
+  }
+
+  /* Check if it has been added */
+  if (*old_owner == '\0') {
+    g_signal_emit (observer, signals[NEW], 0, name);
+  }
+}
+
+/* Creates an instance of observer */
+static MS2Observer *
+create_instance ()
+{
+  DBusGConnection *connection;
+  GError *error = NULL;
+  MS2Observer *observer;
+
+  connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error);
+  if (!connection) {
+    g_printerr ("Could not connect to session bus, %s\n", error->message);
+    g_error_free (error);
+    return NULL;
+  }
+
+  observer = g_object_new (MS2_TYPE_OBSERVER, NULL);
+
+  observer->priv->proxy = dbus_g_proxy_new_for_name (connection,
+                                                     DBUS_SERVICE_DBUS,
+                                                     DBUS_PATH_DBUS,
+                                                     DBUS_INTERFACE_DBUS);
+
+  /* Listen for name-owner-changed signal */
+  dbus_g_proxy_add_signal (observer->priv->proxy,
+                           "NameOwnerChanged",
+                           G_TYPE_STRING,
+                           G_TYPE_STRING,
+                           G_TYPE_STRING,
+                           G_TYPE_INVALID);
+  dbus_g_proxy_connect_signal (observer->priv->proxy,
+                               "NameOwnerChanged",
+                               G_CALLBACK (name_owner_changed),
+                               observer,
+                               NULL);
+
+  return observer;
+}
+
+/* Class init function */
+static void
+ms2_observer_class_init (MS2ObserverClass *klass)
+{
+  g_type_class_add_private (klass, sizeof (MS2ObserverPrivate));
+
+  /**
+   * MS2Observer::observer:
+   * @observer: the #MS2Observer
+   * @provider: name of provider that has been added to dbus
+   *
+   * Notifies when a new provider comes up.
+   **/
+  signals[NEW] = g_signal_new ("new",
+                               G_TYPE_FROM_CLASS (klass),
+                               G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE,
+                               G_STRUCT_OFFSET (MS2ObserverClass, new),
+                               NULL,
+                               NULL,
+                               g_cclosure_marshal_VOID__STRING,
+                               G_TYPE_NONE,
+                               1,
+                               G_TYPE_STRING);
+}
+
+/* Object init function */
+static void
+ms2_observer_init (MS2Observer *client)
+{
+  client->priv = MS2_OBSERVER_GET_PRIVATE (client);
+  client->priv->clients = g_hash_table_new_full (g_str_hash,
+                                                 g_str_equal,
+                                                 g_free,
+                                                 NULL);
+}
+
+/****************** INTERNAL PUBLIC API (NOT TO BE EXPORTED) ******************/
+
+/* Register a client */
+void
+ms2_observer_add_client (MS2Client *client,
+                         const gchar *provider)
+{
+  GList *clients;
+  MS2Observer *observer;
+
+  observer = ms2_observer_get_instance ();
+  if (!observer) {
+    return;
+  }
+
+  clients = g_hash_table_lookup (observer->priv->clients, provider);
+  clients = g_list_prepend (clients, client);
+  g_hash_table_insert (observer->priv->clients, g_strdup (provider), clients);
+}
+
+/* Remove a client */
+void
+ms2_observer_remove_client (MS2Client *client,
+                            const gchar *provider)
+{
+  GList *clients;
+  GList *remove_client;
+  MS2Observer *observer;
+
+  observer = ms2_observer_get_instance ();
+  if (!observer) {
+    return;
+  }
+
+  clients = g_hash_table_lookup (observer->priv->clients, provider);
+  remove_client = g_list_find (clients, client);
+  if (remove_client) {
+    clients = g_list_delete_link (clients, remove_client);
+    /* Check if there are more clients */
+    if (clients) {
+      g_hash_table_insert (observer->priv->clients, g_strdup (provider), clients);
+    } else {
+      g_hash_table_remove (observer->priv->clients, provider);
+    }
+  }
+}
+
+/******************** PUBLIC API ********************/
+
+/**
+ * ms2_observer_get_instance:
+ *
+ * Returns the observer instance
+ *
+ * Returns: the observer instance or @NULL if it could not be created
+ **/
+MS2Observer *ms2_observer_get_instance ()
+{
+  if (!observer_instance) {
+    observer_instance = create_instance ();
+  }
+
+  return observer_instance;
+}
diff --git a/lib/media-server2-observer.h b/lib/media-server2-observer.h
new file mode 100644
index 0000000..619c136
--- /dev/null
+++ b/lib/media-server2-observer.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2010 Igalia S.L.
+ *
+ * Authors: Juan A. Suarez Romero <jasuarez igalia com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA
+ *
+ */
+
+#ifndef _MEDIA_SERVER2_OBSERVER_H_
+#define _MEDIA_SERVER2_OBSERVER_H_
+
+/* #include <gio/gio.h> */
+#include <glib-object.h>
+/* #include <glib.h> */
+
+#include "media-server2-common.h"
+
+#define MS2_TYPE_OBSERVER                       \
+  (ms2_observer_get_type ())
+
+#define MS2_OBSERVER(obj)                               \
+  (G_TYPE_CHECK_INSTANCE_CAST ((obj),                   \
+                               MS2_TYPE_OBSERVER,       \
+                               MS2Observer))
+
+#define MS2_IS_OBSERVER(obj)                            \
+  (G_TYPE_CHECK_INSTANCE_TYPE ((obj),                   \
+                               MS2_TYPE_OBSERVER))
+
+#define MS2_OBSERVER_CLASS(klass)               \
+  (G_TYPE_CHECK_CLASS_CAST((klass),             \
+                           MS2_TYPE_OBSERVER,   \
+                           MS2ObserverClass))
+
+#define MS2_IS_OBSERVER_CLASS(klass)            \
+  (G_TYPE_CHECK_CLASS_TYPE((klass),             \
+                           MS2_TYPE_OBSERVER))
+
+#define MS2_OBSERVER_GET_CLASS(obj)                     \
+  (G_TYPE_INSTANCE_GET_CLASS ((obj),                    \
+                              MS2_TYPE_OBSERVER,        \
+                              MS2ObserverClass))
+
+typedef struct _MS2Observer        MS2Observer;
+typedef struct _MS2ObserverPrivate MS2ObserverPrivate;
+
+struct _MS2Observer {
+
+  GObject parent;
+
+  /*< private >*/
+  MS2ObserverPrivate *priv;
+};
+
+typedef struct _MS2ObserverClass MS2ObserverClass;
+
+struct _MS2ObserverClass {
+
+  GObjectClass parent_class;
+
+  void (*new) (MS2Observer *observer,
+               const gchar *provider);
+};
+
+GType ms2_observer_get_type (void);
+
+MS2Observer *ms2_observer_get_instance (void);
+
+#endif /* _MEDIA_SERVER2_OBSERVER_H_ */
diff --git a/lib/media-server2-server-private.h b/lib/media-server2-private.h
similarity index 73%
rename from lib/media-server2-server-private.h
rename to lib/media-server2-private.h
index 9de4d02..b136c63 100644
--- a/lib/media-server2-server-private.h
+++ b/lib/media-server2-private.h
@@ -20,10 +20,18 @@
  *
  */
 
-#ifndef _MEDIA_SERVER2_SERVER_PRIVATE_H_
-#define _MEDIA_SERVER2_SERVER_PRIVATE_H_
+#ifndef _MEDIA_SERVER2_PRIVATE_H_
+#define _MEDIA_SERVER2_PRIVATE_H_
+
+#define MS2_DBUS_SERVICE_PREFIX "org.gnome.UPnP.MediaServer2."
+#define MS2_DBUS_PATH_PREFIX    "/org/gnome/UPnP/MediaServer2/"
+
+#define MS2_DBUS_SERVICE_PREFIX_LENGTH 28
+
+#define MS2_DBUS_IFACE "org.gnome.UPnP.MediaServer"
 
 #include "media-server2-server.h"
+#include "media-server2-client.h"
 
 gboolean ms2_server_get_properties (MS2Server *server,
                                     const gchar *id,
@@ -39,4 +47,10 @@ gboolean ms2_server_get_children (MS2Server *server,
                                   DBusGMethodInvocation *context,
                                   GError **error);
 
-#endif /* _MEDIA_SERVER2_SERVER_PRIVATE_H_ */
+void ms2_client_notify_unref (MS2Client *client);
+
+void ms2_observer_add_client (MS2Client *client, const gchar *provider);
+
+void ms2_observer_remove_client (MS2Client *client, const gchar *provider);
+
+#endif /* _MEDIA_SERVER2_PRIVATE_H_ */
diff --git a/lib/media-server2-server.c b/lib/media-server2-server.c
index f173fa7..3f4e2cb 100644
--- a/lib/media-server2-server.c
+++ b/lib/media-server2-server.c
@@ -24,13 +24,10 @@
 #include <dbus/dbus-glib.h>
 #include <string.h>
 
-#include "media-server2-server-private.h"
+#include "media-server2-private.h"
 #include "media-server2-server-glue.h"
 #include "media-server2-server.h"
 
-#define MS2_DBUS_SERVICE_PREFIX "org.gnome.UPnP.MediaServer2."
-#define MS2_DBUS_PATH_PREFIX    "/org/gnome/UPnP/MediaServer2/"
-
 #define DBUS_TYPE_G_ARRAY_OF_STRING                             \
   (dbus_g_type_get_collection ("GPtrArray", G_TYPE_STRING))
 
diff --git a/src/test-client.c b/src/test-client.c
index f8d5078..472765c 100644
--- a/src/test-client.c
+++ b/src/test-client.c
@@ -1,4 +1,5 @@
 #include <media-server2-client.h>
+#include <media-server2-observer.h>
 #include <glib.h>
 #include <string.h>
 
@@ -238,6 +239,90 @@ test_children_sync ()
   g_strfreev (providers);
 }
 
+static void
+destroy_cb (MS2Client *client, gpointer user_data)
+{
+  g_print ("End of provider %s\n", ms2_client_get_provider_name(client));
+}
+
+static void
+new_cb (MS2Observer *observer, const gchar *provider, gpointer user_data)
+{
+  MS2Client *client;
+
+  client = ms2_client_new (provider);
+  if (!client) {
+    g_printerr ("Unable to create client for %s\n", provider);
+  } else {
+    g_print ("New provider %s\n", provider);
+    g_signal_connect (client, "destroy", G_CALLBACK (destroy_cb), NULL);
+  }
+}
+
+static void
+test_provider_free ()
+{
+  MS2Client *client;
+  gchar **provider;
+  gchar **providers;
+
+  providers = ms2_client_get_providers ();
+
+  if (!providers) {
+    g_print ("There is no MediaServer2 provider\n");
+    return;
+  }
+
+  for (provider = providers; *provider; provider++) {
+    client = ms2_client_new (*provider);
+    if (!client) {
+      g_printerr ("Unable to create a client\n");
+      continue;
+    }
+
+    g_print ("Provider %s\n", *provider);
+    g_signal_connect (G_OBJECT (client), "destroy", G_CALLBACK (destroy_cb), NULL);
+  }
+
+  g_strfreev (providers);
+}
+
+static void
+test_dynamic_providers ()
+{
+  MS2Client *client;
+  MS2Observer *observer;
+  gchar **provider;
+  gchar **providers;
+
+  observer = ms2_observer_get_instance ();
+  if (!observer) {
+    g_printerr ("Unable to get the observer\n");
+    return;
+  }
+
+  g_signal_connect (observer, "new", G_CALLBACK (new_cb), NULL);
+
+  providers = ms2_client_get_providers ();
+  if (!providers) {
+    g_print ("There is no MediaServer2 providers\n");
+    return;
+  }
+
+  for (provider = providers; *provider; provider++) {
+    client = ms2_client_new (*provider);
+    if (!client) {
+      g_printerr ("Unable to create a client for %s\n", *provider);
+      continue;
+    }
+
+    g_print ("New provider %s\n", *provider);
+    g_signal_connect (client, "destroy", G_CALLBACK (destroy_cb), NULL);
+  }
+
+  g_strfreev (providers);
+}
+
 int main (int argc, char **argv)
 {
   GMainLoop *mainloop;
@@ -246,8 +331,10 @@ int main (int argc, char **argv)
 
   if (0) test_properties_sync ();
   if (0) test_children_sync ();
-  if (1) test_properties_async ();
+  if (0) test_properties_async ();
   if (0) test_children_async ();
+  if (0) test_provider_free ();
+  if (1) test_dynamic_providers ();
 
   mainloop = g_main_loop_new (NULL, FALSE);
   g_main_loop_run (mainloop);



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