[glib/gdbus] Add convenience API for watching and owning names



commit 2e840a52de2777f55248785317c77736ce976c51
Author: David Zeuthen <davidz redhat com>
Date:   Wed Apr 22 18:03:48 2009 -0400

    Add convenience API for watching and owning names
---
 docs/reference/gdbus/gdbus-docs.xml     |    2 +
 docs/reference/gdbus/gdbus-sections.txt |   17 ++-
 gdbus/Makefile.am                       |   14 ++
 gdbus/example-own-name.c                |   74 ++++++++
 gdbus/example-watch-name.c              |   65 +++++++
 gdbus/gbusnameowner.c                   |    4 +-
 gdbus/gbusnamewatcher.c                 |   11 +-
 gdbus/gdbus.h                           |    2 +
 gdbus/gdbus.symbols                     |   14 ++
 gdbus/gdbusconnection.c                 |    4 +-
 gdbus/gdbusnameowning.c                 |  288 +++++++++++++++++++++++++++++++
 gdbus/gdbusnameowning.h                 |   69 ++++++++
 gdbus/gdbusnamewatching.c               |  288 +++++++++++++++++++++++++++++++
 gdbus/gdbusnamewatching.h               |   71 ++++++++
 gdbus/tests/connection.c                |    3 -
 15 files changed, 910 insertions(+), 16 deletions(-)

diff --git a/docs/reference/gdbus/gdbus-docs.xml b/docs/reference/gdbus/gdbus-docs.xml
index 36638e8..2192a12 100644
--- a/docs/reference/gdbus/gdbus-docs.xml
+++ b/docs/reference/gdbus/gdbus-docs.xml
@@ -26,6 +26,8 @@
     </chapter>
     <chapter id="convenience">
         <title>GDBus Convenience</title>
+        <xi:include href="xml/gdbusnamewatching.xml"/>
+        <xi:include href="xml/gdbusnameowning.xml"/>
     </chapter>
     <chapter id="cmapping">
         <title>C Object Mapping</title>
diff --git a/docs/reference/gdbus/gdbus-sections.txt b/docs/reference/gdbus/gdbus-sections.txt
index 103a4f8..3ca5edf 100644
--- a/docs/reference/gdbus/gdbus-sections.txt
+++ b/docs/reference/gdbus/gdbus-sections.txt
@@ -33,7 +33,6 @@ g_bus_name_watcher_new_for_connection
 g_bus_name_watcher_new_for_connection_finish
 g_bus_name_watcher_get_name
 g_bus_name_watcher_get_name_owner
-g_bus_name_watcher_get_flags
 g_bus_name_watcher_get_connection
 <SUBSECTION Standard>
 G_BUS_NAME_WATCHER
@@ -95,3 +94,19 @@ g_dbus_error_set_dbus_error_valist
 g_dbus_error_new_for_gerror
 </SECTION>
 
+<SECTION>
+<FILE>gdbusnamewatching</FILE>
+GBusNameAppearedCallback
+GBusNameVanishedCallback
+g_bus_watch_name
+g_bus_unwatch_name
+</SECTION>
+
+<SECTION>
+<FILE>gdbusnameowning</FILE>
+GBusNameAcquiredCallback
+GBusNameLostCallback
+g_bus_own_name
+g_bus_unown_name
+</SECTION>
+
diff --git a/gdbus/Makefile.am b/gdbus/Makefile.am
index 33f614b..52578bc 100644
--- a/gdbus/Makefile.am
+++ b/gdbus/Makefile.am
@@ -81,6 +81,8 @@ gdbus_headers =				\
 	gdbusconnection.h		\
 	gbusnameowner.h			\
 	gbusnamewatcher.h		\
+	gdbusnamewatching.h		\
+	gdbusnameowning.h		\
 	$(NULL)
 
 libgdbus_2_0_la_SOURCES =						\
@@ -94,6 +96,8 @@ libgdbus_2_0_la_SOURCES =						\
 	gdbusconnection.h		gdbusconnection.c		\
 	gbusnameowner.h			gbusnameowner.c			\
 	gbusnamewatcher.h		gbusnamewatcher.c		\
+	gdbusnamewatching.h		gdbusnamewatching.c		\
+	gdbusnameowning.h		gdbusnameowning.c		\
 	gdbusprivate.h			gdbusprivate.c			\
 	gdbusalias.h 							\
 	gdbusaliasdef.c							\
@@ -187,5 +191,15 @@ gdbusenumtypes.c: $(gdbus_headers) gdbusenumtypes.c.template Makefile.am
 gdbus-2.0.lib: libgdbus-2.0.la gdbus.def
 	lib -machine:@LIB_EXE_MACHINE_FLAG@ -name:libgdbus-2.0-$(LT_CURRENT_MINUS_AGE).dll -def:gdbus.def -out:$@
 
+noinst_PROGRAMS = example-watch-name example-own-name
+
+example_watch_name_SOURCES = example-watch-name.c
+example_watch_name_CFLAGS  = $(DBUS_CFLAGS)
+example_watch_name_LDADD   = libgdbus-2.0.la
+
+example_own_name_SOURCES = example-own-name.c
+example_own_name_CFLAGS  = $(DBUS_CFLAGS)
+example_own_name_LDADD   = libgdbus-2.0.la
+
 clean-local :
 	rm -f *~
diff --git a/gdbus/example-own-name.c b/gdbus/example-own-name.c
new file mode 100644
index 0000000..11071c9
--- /dev/null
+++ b/gdbus/example-own-name.c
@@ -0,0 +1,74 @@
+#include <gdbus/gdbus.h>
+
+static void
+on_name_acquired (GDBusConnection *connection,
+                  const gchar     *name,
+                  gpointer         user_data)
+{
+  g_print ("Acquired the name %s on the session bus\n", name);
+}
+
+static void
+on_name_lost (GDBusConnection *connection,
+              const gchar     *name,
+              gpointer         user_data)
+{
+  g_print ("Lost the name %s on the session bus\n", name);
+}
+
+int
+main (int argc, char *argv[])
+{
+  guint owner_id;
+  GMainLoop *loop;
+  GBusNameOwnerFlags flags;
+  gboolean opt_replace;
+  gboolean opt_allow_replacement;
+  gchar *opt_name;
+  GOptionContext *opt_context;
+  GError *error;
+  GOptionEntry opt_entries[] =
+    {
+      { "replace", 'r', 0, G_OPTION_ARG_NONE, &opt_replace, "Replace existing name if possible", NULL },
+      { "allow-replacement", 'a', 0, G_OPTION_ARG_NONE, &opt_allow_replacement, "Allow replacement", NULL },
+      { "name", 'n', 0, G_OPTION_ARG_STRING, &opt_name, "Name to acquire", NULL },
+      { NULL}
+    };
+
+  g_type_init ();
+
+  error = NULL;
+  opt_name = NULL;
+  opt_context = g_option_context_new ("g_bus_own_name() example");
+  g_option_context_add_main_entries (opt_context, opt_entries, NULL);
+  if (!g_option_context_parse (opt_context, &argc, &argv, &error))
+    {
+      g_printerr ("Error parsing options: %s", error->message);
+      return 1;
+    }
+  if (opt_name == NULL)
+    {
+      g_printerr ("Incorrect usage, try --help.\n");
+      return 1;
+    }
+
+  flags = G_BUS_NAME_OWNER_FLAGS_NONE;
+  if (opt_replace)
+    flags |= G_BUS_NAME_OWNER_FLAGS_REPLACE;
+  if (opt_allow_replacement)
+    flags |= G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT;
+
+  owner_id = g_bus_own_name (G_BUS_TYPE_SESSION,
+                             opt_name,
+                             flags,
+                             on_name_acquired,
+                             on_name_lost,
+                             NULL);
+
+  loop = g_main_loop_new (NULL, FALSE);
+  g_main_loop_run (loop);
+
+  g_bus_unown_name (owner_id);
+
+  return 0;
+}
diff --git a/gdbus/example-watch-name.c b/gdbus/example-watch-name.c
new file mode 100644
index 0000000..2dfa3be
--- /dev/null
+++ b/gdbus/example-watch-name.c
@@ -0,0 +1,65 @@
+#include <gdbus/gdbus.h>
+
+static void
+on_name_appeared (GDBusConnection *connection,
+                  const gchar     *name,
+                  const gchar     *name_owner,
+                  gpointer         user_data)
+{
+  g_print ("Name %s on the session bus is owned by %s\n", name, name_owner);
+}
+
+static void
+on_name_vanished (GDBusConnection *connection,
+                  const gchar     *name,
+                  gpointer         user_data)
+{
+  g_print ("Name %s does not exists on the session bus\n", name);
+}
+
+int
+main (int argc, char *argv[])
+{
+  guint watcher_id;
+  GMainLoop *loop;
+  gchar *opt_name;
+  GOptionContext *opt_context;
+  GError *error;
+  GOptionEntry opt_entries[] =
+    {
+      { "name", 'n', 0, G_OPTION_ARG_STRING, &opt_name, "Name to watch", NULL },
+      { NULL}
+    };
+
+  g_type_init ();
+
+  error = NULL;
+  opt_name = NULL;
+  opt_context = g_option_context_new ("g_bus_watch_name() example");
+  g_option_context_add_main_entries (opt_context, opt_entries, NULL);
+  if (!g_option_context_parse (opt_context, &argc, &argv, &error))
+    {
+      g_printerr ("Error parsing options: %s", error->message);
+      return 1;
+    }
+  if (opt_name == NULL)
+    {
+      g_printerr ("Incorrect usage, try --help.\n");
+      return 1;
+    }
+
+  g_type_init ();
+
+  watcher_id = g_bus_watch_name (G_BUS_TYPE_SESSION,
+                                 opt_name,
+                                 on_name_appeared,
+                                 on_name_vanished,
+                                 NULL);
+
+  loop = g_main_loop_new (NULL, FALSE);
+  g_main_loop_run (loop);
+
+  g_bus_unwatch_name (watcher_id);
+
+  return 0;
+}
diff --git a/gdbus/gbusnameowner.c b/gdbus/gbusnameowner.c
index c074984..feee26b 100644
--- a/gdbus/gbusnameowner.c
+++ b/gdbus/gbusnameowner.c
@@ -260,8 +260,8 @@ filter_function (DBusConnection *dbus_1_connection,
   const gchar *name;
   gboolean old_owns_name;
 
-  g_debug ("in bus-name-owner's filter_function for dbus_1_connection %p", dbus_1_connection);
-  PRINT_MESSAGE (message);
+  //g_debug ("in bus-name-owner's filter_function for dbus_1_connection %p", dbus_1_connection);
+  //PRINT_MESSAGE (message);
 
   dbus_error_init (&dbus_error);
 
diff --git a/gdbus/gbusnamewatcher.c b/gdbus/gbusnamewatcher.c
index c046a07..914d8f6 100644
--- a/gdbus/gbusnamewatcher.c
+++ b/gdbus/gbusnamewatcher.c
@@ -249,8 +249,8 @@ filter_function (DBusConnection *dbus_1_connection,
   const gchar *old_owner;
   const gchar *new_owner;
 
-  g_debug ("in bus-name-watcher's filter_function for dbus_1_connection %p", dbus_1_connection);
-  PRINT_MESSAGE (message);
+  //g_debug ("in bus-name-watcher's filter_function for dbus_1_connection %p", dbus_1_connection);
+  //PRINT_MESSAGE (message);
 
   /* we only care about NameOwnerChanged */
   if (!(dbus_message_is_signal (message,
@@ -279,11 +279,6 @@ filter_function (DBusConnection *dbus_1_connection,
   if (g_strcmp0 (name, watcher->priv->name) != 0)
     goto out;
 
-  g_debug ("name='%s' old_owner='%s' new_owner='%s'",
-           name,
-           old_owner,
-           new_owner);
-
   if ((old_owner != NULL && strlen (old_owner) > 0) && watcher->priv->name_owner != NULL)
     {
       g_free (watcher->priv->name_owner);
@@ -854,7 +849,7 @@ g_bus_name_watcher_new (GBusType               bus_type,
  * watched.
  *
  * Returns: %TRUE if it was possible to check if name has an owner (the name may not have an
- * owner, check with g_dbus_name_watcher_get_name_owner()), otherwise %FALSE with @error
+ * owner, check with g_bus_name_watcher_get_name_owner()), otherwise %FALSE with @error
  * set.
  **/
 gboolean
diff --git a/gdbus/gdbus.h b/gdbus/gdbus.h
index 334f5a5..d77f088 100644
--- a/gdbus/gdbus.h
+++ b/gdbus/gdbus.h
@@ -32,6 +32,8 @@
 #include <gdbus/gdbuserror.h>
 #include <gdbus/gbusnameowner.h>
 #include <gdbus/gbusnamewatcher.h>
+#include <gdbus/gdbusnamewatching.h>
+#include <gdbus/gdbusnameowning.h>
 
 #undef __G_DBUS_D_DBUS_H_INSIDE__
 
diff --git a/gdbus/gdbus.symbols b/gdbus/gdbus.symbols
index c6b6045..13819ff 100644
--- a/gdbus/gdbus.symbols
+++ b/gdbus/gdbus.symbols
@@ -88,3 +88,17 @@ g_dbus_error_set_dbus_error
 g_dbus_error_set_dbus_error_valist
 #endif
 #endif
+
+#if IN_HEADER(__G_DBUS_NAME_WATCHING_H__)
+#if IN_FILE(__G_DBUS_NAME_WATCHING_C__)
+g_bus_watch_name
+g_bus_unwatch_name
+#endif
+#endif
+
+#if IN_HEADER(__G_DBUS_NAME_OWNING_H__)
+#if IN_FILE(__G_DBUS_NAME_OWNING_C__)
+g_bus_own_name
+g_bus_unown_name
+#endif
+#endif
diff --git a/gdbus/gdbusconnection.c b/gdbus/gdbusconnection.c
index 6d612b0..1f598d1 100644
--- a/gdbus/gdbusconnection.c
+++ b/gdbus/gdbusconnection.c
@@ -572,8 +572,8 @@ filter_function (DBusConnection *dbus_1_connection,
   GDBusConnection *connection = G_DBUS_CONNECTION (user_data);
   DBusError dbus_error;
 
-  g_debug ("in filter_function for dbus_1_connection %p", dbus_1_connection);
-  PRINT_MESSAGE (message);
+  //g_debug ("in filter_function for dbus_1_connection %p", dbus_1_connection);
+  //PRINT_MESSAGE (message);
 
   dbus_error_init (&dbus_error);
 
diff --git a/gdbus/gdbusnameowning.c b/gdbus/gdbusnameowning.c
new file mode 100644
index 0000000..51bdfa2
--- /dev/null
+++ b/gdbus/gdbusnameowning.c
@@ -0,0 +1,288 @@
+/* GDBus - GLib D-Bus Library
+ *
+ * Copyright (C) 2008-2009 Red Hat, Inc.
+ *
+ * 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; either
+ * version 2 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., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include <glib/gi18n.h>
+
+#include "gbusnameowner.h"
+#include "gdbusnameowning.h"
+#include "gdbuserror.h"
+#include "gdbusprivate.h"
+
+#include "gdbusalias.h"
+
+/**
+ * SECTION:gdbusnameowning
+ * @title: Owning Bus Names
+ * @short_description: Simple API for owning bus names
+ * @include: gdbus/gdbus.h
+ *
+ * Convenience API for owning bus names.
+ *
+ * <example id="gdbus-owning-names"><title>Simple application owning a name</title><programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude"; parse="text" href="../../../../gdbus/example-own-name.c"><xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting></example>
+ */
+
+G_LOCK_DEFINE_STATIC (lock);
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef struct
+{
+  volatile gint             ref_count;
+
+  guint                     id;
+  GBusNameAcquiredCallback  name_acquired_handler;
+  GBusNameLostCallback      name_lost_handler;
+  gpointer                  user_data;
+  GBusNameOwner            *owner;
+
+  gulong                    name_acquired_signal_handler_id;
+  gulong                    name_lost_signal_handler_id;
+
+  gboolean                  cancelled;
+} Client;
+
+static guint next_global_id = 1;
+static GHashTable *map_id_to_client = NULL;
+
+static Client *
+client_ref (Client *client)
+{
+  g_atomic_int_inc (&client->ref_count);
+  return client;
+}
+
+static void
+client_unref (Client *client)
+{
+  if (g_atomic_int_dec_and_test (&client->ref_count))
+    {
+      if (client->name_acquired_signal_handler_id > 0)
+        g_signal_handler_disconnect (client->owner, client->name_acquired_signal_handler_id);
+      if (client->name_lost_signal_handler_id > 0)
+        g_signal_handler_disconnect (client->owner, client->name_lost_signal_handler_id);
+      g_object_unref (client->owner);
+      g_free (client);
+    }
+}
+
+static void
+on_name_acquired (GBusNameOwner *owner,
+                  gpointer         user_data)
+{
+  Client *client = user_data;
+  if (client->name_acquired_handler != NULL)
+    {
+      client->name_acquired_handler (g_bus_name_owner_get_connection (client->owner),
+                                     g_bus_name_owner_get_name (client->owner),
+                                     client->user_data);
+    }
+}
+
+static void
+on_name_lost (GBusNameOwner *owner,
+                  gpointer         user_data)
+{
+  Client *client = user_data;
+  if (client->name_lost_handler != NULL)
+    {
+      client->name_lost_handler (g_bus_name_owner_get_connection (client->owner),
+                                 g_bus_name_owner_get_name (client->owner),
+                                 client->user_data);
+    }
+}
+
+static void
+owner_async_ready_cb (GBusNameOwner *owner,
+                        GAsyncResult    *res,
+                        gpointer         user_data)
+{
+  Client *client = user_data;
+
+  G_LOCK (lock);
+  if (client->cancelled)
+    goto out;
+  G_UNLOCK (lock);
+
+  /* connect signals */
+  client->name_acquired_signal_handler_id = g_signal_connect (client->owner,
+                                                              "name-acquired",
+                                                              G_CALLBACK (on_name_acquired),
+                                                              client);
+  client->name_lost_signal_handler_id = g_signal_connect (client->owner,
+                                                              "name-lost",
+                                                              G_CALLBACK (on_name_lost),
+                                                              client);
+
+  /* and then report the name */
+  if (g_bus_name_owner_get_owns_name (client->owner))
+    {
+      if (client->name_acquired_handler != NULL)
+        {
+          client->name_acquired_handler (g_bus_name_owner_get_connection (client->owner),
+                                         g_bus_name_owner_get_name (client->owner),
+                                         client->user_data);
+        }
+    }
+  else
+    {
+      if (client->name_lost_handler != NULL)
+        {
+          client->name_lost_handler (g_bus_name_owner_get_connection (client->owner),
+                                         g_bus_name_owner_get_name (client->owner),
+                                         client->user_data);
+        }
+    }
+
+
+ out:
+  client_unref (client);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/**
+ * g_bus_own_name:
+ * @bus_type: The type of bus to own a name on (can't be #G_BUS_TYPE_NONE).
+ * @name: The well-known name to own.
+ * @flags: A set of flags from the #GBusNameOwnerFlags enumeration.
+ * @name_acquired_handler: Handler to invoke when @name is acquired.
+ * @name_lost_handler: Handler to invoke when @name is lost.
+ * @user_data: User data to pass to handlers.
+ *
+ * Starts acquiring @name on the bus specified by @bus_type and calls
+ * @name_acquired_handler and @name_lost_handler when the name is
+ * acquired respectively lost.
+ *
+ * You are guaranteed that one of the handlers will be invoked (on the
+ * main thread) after calling this function. When you are done
+ * owning the name, just call g_bus_unown() with the owner id
+ * this function returns.
+ *
+ * If the name is acquired or lost (for example another application
+ * could acquire the name if you allow replacement), the handlers are
+ * also invoked. If the #GDBusConnection that is used for attempting
+ * to own the name is closed, then @name_lost_handler is invoked since
+ * it is no longer possible for other processes to access the
+ * process. Similarly, if the connection is opened again, the name
+ * will be requested and @name_acquired_handler is invoked if it was
+ * possible to acquire the name.
+ *
+ * Another guarantee is that invocations of @name_acquired_handler
+ * and @name_lost_handler are guaranteed to alternate; that
+ * is, if @name_acquired_handler is invoked then you are
+ * guaranteed that the next time one of the handlers is invoked, it
+ * will be @name_lost_handler. The reverse is also true.
+ *
+ * This behavior makes it very simple to write applications that wants
+ * to own names, see <xref linkend="gdbus-owning-names"/>. Simply
+ * register objects to be exported in @name_acquired_handler and
+ * unregister the objects (if any) in @name_lost_handler.
+ *
+ * Returns: An identifier (never 0) that an be used with
+ * g_bus_unown_name() to stop owning the name.
+ **/
+guint
+g_bus_own_name (GBusType                  bus_type,
+                const gchar              *name,
+                GBusNameOwnerFlags        flags,
+                GBusNameAcquiredCallback  name_acquired_handler,
+                GBusNameLostCallback      name_lost_handler,
+                gpointer                  user_data)
+{
+  Client *client;
+
+  g_return_val_if_fail (bus_type != G_BUS_TYPE_NONE, 0);
+  g_return_val_if_fail (name != NULL, 0);
+  //g_return_val_if_fail (TODO_is_well_known_name (), 0);
+  g_return_val_if_fail (name_acquired_handler != NULL, 0);
+  g_return_val_if_fail (name_lost_handler != NULL, 0);
+
+  G_LOCK (lock);
+
+  client = g_new0 (Client, 1);
+  client->ref_count = 1;
+  client->id = next_global_id++; /* TODO: uh oh, handle overflow */
+  client->name_acquired_handler = name_acquired_handler;
+  client->name_lost_handler = name_lost_handler;
+  client->user_data = user_data;
+  client->owner = g_bus_name_owner_new (bus_type,
+                                        name,
+                                        flags,
+                                        NULL,
+                                        (GAsyncReadyCallback) owner_async_ready_cb,
+                                        client_ref (client));
+
+  if (map_id_to_client == NULL)
+    {
+      map_id_to_client = g_hash_table_new_full (g_direct_hash,
+                                                g_direct_equal,
+                                                NULL,
+                                                (GDestroyNotify) client_unref);
+    }
+  g_hash_table_insert (map_id_to_client,
+                       GUINT_TO_POINTER (client->id),
+                       client);
+
+  G_UNLOCK (lock);
+
+  return client->id;
+}
+
+/**
+ * g_bus_unown_name:
+ * @owner_id: An identifier obtained from g_bus_own_name()
+ *
+ * Stops owning a name.
+ *
+ * Note that no handlers are invoked when this happens so remember to
+ * unregister exported objects and clean up state related to owning
+ * the name.
+ **/
+void
+g_bus_unown_name (guint owner_id)
+{
+  Client *client;
+
+  G_LOCK (lock);
+
+  if (owner_id == 0 ||
+      map_id_to_client == NULL ||
+      (client = g_hash_table_lookup (map_id_to_client, GUINT_TO_POINTER (owner_id))) == NULL)
+    {
+      g_warning ("Invalid id %d passed to g_bus_unown_name()", owner_id);
+      goto out;
+    }
+
+  client->cancelled = TRUE;
+
+  g_warn_if_fail (g_hash_table_remove (map_id_to_client, GUINT_TO_POINTER (owner_id)) == 1);
+
+ out:
+  G_UNLOCK (lock);
+}
+
+#define __G_DBUS_NAME_OWNING_C__
+#include "gdbusaliasdef.c"
diff --git a/gdbus/gdbusnameowning.h b/gdbus/gdbusnameowning.h
new file mode 100644
index 0000000..09040c5
--- /dev/null
+++ b/gdbus/gdbusnameowning.h
@@ -0,0 +1,69 @@
+/* GDBus - GLib D-Bus Library
+ *
+ * Copyright (C) 2008-2009 Red Hat, Inc.
+ *
+ * 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; either
+ * version 2 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., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#if !defined (__G_DBUS_G_DBUS_H_INSIDE__) && !defined (G_DBUS_COMPILATION)
+#error "Only <gdbus/gdbus.h> can be included directly."
+#endif
+
+#ifndef __G_DBUS_NAME_OWNING_H__
+#define __G_DBUS_NAME_OWNING_H__
+
+#include <gdbus/gdbustypes.h>
+#include <dbus/dbus.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GBusNameAcquiredCallback:
+ * @connection: The #GDBusConnection on which to acquired the name.
+ * @name: The name being owned.
+ * @user_data: User data passed to g_bus_own_name().
+ *
+ * Invoked when the name is acquired.
+ */
+typedef void (*GBusNameAcquiredCallback) (GDBusConnection *connection,
+                                          const gchar     *name,
+                                          gpointer         user_data);
+
+/**
+ * GBusNameLostCallback:
+ * @connection: The #GDBusConnection on which to acquire the name.
+ * @name: The name being owned.
+ * @user_data: User data passed to g_bus_own_name().
+ *
+ * Invoked when the name is lost.
+ */
+typedef void (*GBusNameLostCallback) (GDBusConnection *connection,
+                                      const gchar     *name,
+                                      gpointer         user_data);
+
+guint g_bus_own_name   (GBusType                  bus_type,
+                        const gchar              *name,
+                        GBusNameOwnerFlags        flags,
+                        GBusNameAcquiredCallback  name_acquired_handler,
+                        GBusNameLostCallback      name_lost_handler,
+                        gpointer                  user_data);
+void  g_bus_unown_name (guint                     owner_id);
+
+G_END_DECLS
+
+#endif /* __G_DBUS_NAME_OWNING_H__ */
diff --git a/gdbus/gdbusnamewatching.c b/gdbus/gdbusnamewatching.c
new file mode 100644
index 0000000..b3b202d
--- /dev/null
+++ b/gdbus/gdbusnamewatching.c
@@ -0,0 +1,288 @@
+/* GDBus - GLib D-Bus Library
+ *
+ * Copyright (C) 2008-2009 Red Hat, Inc.
+ *
+ * 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; either
+ * version 2 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., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+
+#include <glib/gi18n.h>
+
+#include "gbusnamewatcher.h"
+#include "gdbusnamewatching.h"
+#include "gdbuserror.h"
+#include "gdbusprivate.h"
+
+#include "gdbusalias.h"
+
+/**
+ * SECTION:gdbusnamewatching
+ * @title: Watching Bus Names
+ * @short_description: Simple API for watching bus names
+ * @include: gdbus/gdbus.h
+ *
+ * Convenience API for watching bus names.
+ *
+ * <example id="gdbus-watching-names"><title>Simple application watching a name</title><programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude"; parse="text" href="../../../../gdbus/example-watch-name.c"><xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting></example>
+ */
+
+G_LOCK_DEFINE_STATIC (lock);
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef struct
+{
+  volatile gint             ref_count;
+
+  guint                     id;
+  GBusNameAppearedCallback  name_appeared_handler;
+  GBusNameVanishedCallback  name_vanished_handler;
+  gpointer                  user_data;
+  GBusNameWatcher          *watcher;
+
+  gulong                    name_appeared_signal_handler_id;
+  gulong                    name_vanished_signal_handler_id;
+
+  gboolean                  cancelled;
+} Client;
+
+static guint next_global_id = 1;
+static GHashTable *map_id_to_client = NULL;
+
+static Client *
+client_ref (Client *client)
+{
+  g_atomic_int_inc (&client->ref_count);
+  return client;
+}
+
+static void
+client_unref (Client *client)
+{
+  if (g_atomic_int_dec_and_test (&client->ref_count))
+    {
+      if (client->name_appeared_signal_handler_id > 0)
+        g_signal_handler_disconnect (client->watcher, client->name_appeared_signal_handler_id);
+      if (client->name_vanished_signal_handler_id > 0)
+        g_signal_handler_disconnect (client->watcher, client->name_vanished_signal_handler_id);
+      g_object_unref (client->watcher);
+      g_free (client);
+    }
+}
+
+static void
+on_name_appeared (GBusNameWatcher *watcher,
+                  gpointer         user_data)
+{
+  Client *client = user_data;
+  if (client->name_appeared_handler != NULL)
+    {
+      client->name_appeared_handler (g_bus_name_watcher_get_connection (client->watcher),
+                                     g_bus_name_watcher_get_name (client->watcher),
+                                     g_bus_name_watcher_get_name_owner (client->watcher),
+                                     client->user_data);
+    }
+}
+
+static void
+on_name_vanished (GBusNameWatcher *watcher,
+                  gpointer         user_data)
+{
+  Client *client = user_data;
+  if (client->name_vanished_handler != NULL)
+    {
+      client->name_vanished_handler (g_bus_name_watcher_get_connection (client->watcher),
+                                     g_bus_name_watcher_get_name (client->watcher),
+                                     client->user_data);
+    }
+}
+
+static void
+watcher_async_ready_cb (GBusNameWatcher *watcher,
+                        GAsyncResult    *res,
+                        gpointer         user_data)
+{
+  Client *client = user_data;
+  const gchar *name_owner;
+
+  G_LOCK (lock);
+  if (client->cancelled)
+    goto out;
+  G_UNLOCK (lock);
+
+  /* connect signals */
+  client->name_appeared_signal_handler_id = g_signal_connect (client->watcher,
+                                                              "name-appeared",
+                                                              G_CALLBACK (on_name_appeared),
+                                                              client);
+  client->name_vanished_signal_handler_id = g_signal_connect (client->watcher,
+                                                              "name-vanished",
+                                                              G_CALLBACK (on_name_vanished),
+                                                              client);
+
+  /* and then report the name */
+  name_owner = g_bus_name_watcher_get_name_owner (watcher);
+  if (name_owner != NULL)
+    {
+      if (client->name_appeared_handler != NULL)
+        {
+          client->name_appeared_handler (g_bus_name_watcher_get_connection (client->watcher),
+                                         g_bus_name_watcher_get_name (client->watcher),
+                                         name_owner,
+                                         client->user_data);
+        }
+    }
+  else
+    {
+      if (client->name_vanished_handler != NULL)
+        {
+          client->name_vanished_handler (g_bus_name_watcher_get_connection (client->watcher),
+                                         g_bus_name_watcher_get_name (client->watcher),
+                                         client->user_data);
+        }
+    }
+
+
+ out:
+  client_unref (client);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/**
+ * g_bus_watch_name:
+ * @bus_type: The type of bus to watch a name on (can't be #G_BUS_TYPE_NONE).
+ * @name: The name (well-known or unique) to watch.
+ * @name_appeared_handler: Handler to invoke when @name is known to exist.
+ * @name_vanished_handler: Handler to invoke when @name is known to not exist.
+ * @user_data: User data to pass to handlers.
+ *
+ * Starts watching @name on the bus specified by @bus_type and calls
+ * @name_appeared_handler and @name_vanished_handler when the name is
+ * known to have a owner respectively known to lose its owner.
+ *
+ * You are guaranteed that one of the handlers will be invoked (on the
+ * main thread) after calling this function. When you are done
+ * watching the name, just call g_bus_unwatch() with the watcher id
+ * this function returns.
+ *
+ * If the name vanishes or appears (for example the application owning
+ * the name could restart), the handlers are also invoked. If the
+ * #GDBusConnection that is used for watching the name is closed, then
+ * @name_vanished_handler is invoked since it is no longer
+ * possible to access the name. Similarly, if the connection is opened
+ * again, @name_appeared_handler is invoked if and when it turns
+ * out someone owns the name.
+ *
+ * Another guarantee is that invocations of @name_appeared_handler
+ * and @name_vanished_handler are guaranteed to alternate; that
+ * is, if @name_appeared_handler is invoked then you are
+ * guaranteed that the next time one of the handlers is invoked, it
+ * will be @name_vanished_handler. The reverse is also true.
+ *
+ * This behavior makes it very simple to write applications that wants
+ * to take action when a certain name exists, see <xref
+ * linkend="gdbus-watching-names"/>. Basically, the application
+ * should create object proxies in @name_appeared_handler and destroy
+ * them again (if any) in @name_vanished_handler.
+ *
+ * Returns: An identifier (never 0) that an be used with
+ * g_bus_unwatch_name() to stop watching the name.
+ **/
+guint
+g_bus_watch_name (GBusType                  bus_type,
+                  const gchar              *name,
+                  GBusNameAppearedCallback  name_appeared_handler,
+                  GBusNameVanishedCallback  name_vanished_handler,
+                  gpointer                  user_data)
+{
+  Client *client;
+
+  g_return_val_if_fail (bus_type != G_BUS_TYPE_NONE, 0);
+  g_return_val_if_fail (name != NULL, 0);
+  g_return_val_if_fail (name_appeared_handler != NULL, 0);
+  g_return_val_if_fail (name_vanished_handler != NULL, 0);
+
+  G_LOCK (lock);
+
+  client = g_new0 (Client, 1);
+  client->ref_count = 1;
+  client->id = next_global_id++; /* TODO: uh oh, handle overflow */
+  client->name_appeared_handler = name_appeared_handler;
+  client->name_vanished_handler = name_vanished_handler;
+  client->user_data = user_data;
+  client->watcher = g_bus_name_watcher_new (bus_type,
+                                            name,
+                                            NULL,
+                                            (GAsyncReadyCallback) watcher_async_ready_cb,
+                                            client_ref (client));
+
+  if (map_id_to_client == NULL)
+    {
+      map_id_to_client = g_hash_table_new_full (g_direct_hash,
+                                                g_direct_equal,
+                                                NULL,
+                                                (GDestroyNotify) client_unref);
+    }
+  g_hash_table_insert (map_id_to_client,
+                       GUINT_TO_POINTER (client->id),
+                       client);
+
+  G_UNLOCK (lock);
+
+  return client->id;
+}
+
+/**
+ * g_bus_unwatch_name:
+ * @watcher_id: An identifier obtained from g_bus_watch_name()
+ *
+ * Stops watching a name.
+ *
+ * Note that no handlers are invoked when this happens so remember to
+ * destroy client side proxies and clean up state related to watching
+ * the name.
+ **/
+void
+g_bus_unwatch_name (guint watcher_id)
+{
+  Client *client;
+
+  G_LOCK (lock);
+
+  if (watcher_id == 0 ||
+      map_id_to_client == NULL ||
+      (client = g_hash_table_lookup (map_id_to_client, GUINT_TO_POINTER (watcher_id))) == NULL)
+    {
+      g_warning ("Invalid id %d passed to g_bus_unwatch_name()", watcher_id);
+      goto out;
+    }
+
+  client->cancelled = TRUE;
+
+  g_warn_if_fail (g_hash_table_remove (map_id_to_client, GUINT_TO_POINTER (watcher_id)) == 1);
+
+ out:
+  G_UNLOCK (lock);
+}
+
+#define __G_DBUS_NAME_WATCHING_C__
+#include "gdbusaliasdef.c"
diff --git a/gdbus/gdbusnamewatching.h b/gdbus/gdbusnamewatching.h
new file mode 100644
index 0000000..900a0c2
--- /dev/null
+++ b/gdbus/gdbusnamewatching.h
@@ -0,0 +1,71 @@
+/* GDBus - GLib D-Bus Library
+ *
+ * Copyright (C) 2008-2009 Red Hat, Inc.
+ *
+ * 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; either
+ * version 2 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., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#if !defined (__G_DBUS_G_DBUS_H_INSIDE__) && !defined (G_DBUS_COMPILATION)
+#error "Only <gdbus/gdbus.h> can be included directly."
+#endif
+
+#ifndef __G_DBUS_NAME_WATCHING_H__
+#define __G_DBUS_NAME_WATCHING_H__
+
+#include <gdbus/gdbustypes.h>
+#include <dbus/dbus.h>
+
+G_BEGIN_DECLS
+
+/**
+ * GBusNameAppearedCallback:
+ * @connection: The #GDBusConnection the name is being watched on.
+ * @name: The name being watched.
+ * @name_owner: Unique name of the owner of the name being watched.
+ * @user_data: User data passed to g_bus_watch_name().
+ *
+ * Invoked when the name being watched is known to have to have a owner.
+ */
+typedef void (*GBusNameAppearedCallback) (GDBusConnection *connection,
+                                          const gchar     *name,
+                                          const gchar     *name_owner,
+                                          gpointer         user_data);
+
+/**
+ * GBusNameVanishedCallback:
+ * @connection: The #GDBusConnection the name is being watched on.
+ * @name: The name being watched.
+ * @user_data: User data passed to g_bus_watch_name().
+ *
+ * Invoked when the name being watched is known not to have to have a owner.
+ */
+typedef void (*GBusNameVanishedCallback) (GDBusConnection *connection,
+                                          const gchar     *name,
+                                          gpointer         user_data);
+
+
+guint g_bus_watch_name   (GBusType                  bus_type,
+                          const gchar              *name,
+                          GBusNameAppearedCallback  name_appeared_handler,
+                          GBusNameVanishedCallback  name_vanished_handler,
+                          gpointer                  user_data);
+void  g_bus_unwatch_name (guint                     watcher_id);
+
+G_END_DECLS
+
+#endif /* __G_DBUS_NAME_WATCHING_H__ */
diff --git a/gdbus/tests/connection.c b/gdbus/tests/connection.c
index 9c473db..c65513f 100644
--- a/gdbus/tests/connection.c
+++ b/gdbus/tests/connection.c
@@ -1045,9 +1045,6 @@ test_bus_name_owner (void)
                                                           (GAsyncReadyCallback) get_connection_callback_private,
                                                           NULL);                           /* user_data */
   g_main_loop_run (loop);
-  g_debug ("orig %s, private %s",
-           g_dbus_connection_get_unique_name (g_bus_name_owner_get_connection (o)),
-           g_dbus_connection_get_unique_name (private_connection));
   val = FALSE;
   o2 = g_bus_name_owner_new_for_connection (private_connection,
                                             "org.gtk.Test.Name1",



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