[glib/gdbus] Add convenience API for watching and owning names
- From: David Zeuthen <davidz src gnome org>
- To: svn-commits-list gnome org
- Subject: [glib/gdbus] Add convenience API for watching and owning names
- Date: Wed, 22 Apr 2009 18:06:22 -0400 (EDT)
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]