[glib/gdbus: 2/6] Add GBusNameWatcher and fix singleton handling in GDBusConnection
- From: David Zeuthen <davidz src gnome org>
- To: svn-commits-list gnome org
- Subject: [glib/gdbus: 2/6] Add GBusNameWatcher and fix singleton handling in GDBusConnection
- Date: Wed, 22 Apr 2009 11:45:09 -0400 (EDT)
commit 908ab223cfeb967ef3970231357371ae3ea4ec14
Author: David Zeuthen <davidz redhat com>
Date: Tue Apr 21 03:34:29 2009 -0400
Add GBusNameWatcher and fix singleton handling in GDBusConnection
---
docs/reference/gdbus/gdbus-docs.xml | 1 +
docs/reference/gdbus/gdbus-sections.txt | 21 +
docs/reference/gdbus/gdbus.types | 1 +
gdbus/Makefile.am | 2 +
gdbus/gbusnameowner.c | 89 +--
gdbus/gbusnamewatcher.c | 1231 +++++++++++++++++++++++++++++++
gdbus/gbusnamewatcher.h | 113 +++
gdbus/gdbus.h | 1 +
gdbus/gdbus.symbols | 11 +
gdbus/gdbusconnection.c | 112 +++-
gdbus/gdbustypes.h | 1 +
gdbus/tests/connection.c | 323 ++++++++-
12 files changed, 1827 insertions(+), 79 deletions(-)
diff --git a/docs/reference/gdbus/gdbus-docs.xml b/docs/reference/gdbus/gdbus-docs.xml
index 3747302..710ca4c 100644
--- a/docs/reference/gdbus/gdbus-docs.xml
+++ b/docs/reference/gdbus/gdbus-docs.xml
@@ -25,6 +25,7 @@
<chapter id="convenience">
<title>GDBus Convenience</title>
<xi:include href="xml/gbusnameowner.xml"/>
+ <xi:include href="xml/gbusnamewatcher.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 2640590..0550195 100644
--- a/docs/reference/gdbus/gdbus-sections.txt
+++ b/docs/reference/gdbus/gdbus-sections.txt
@@ -23,6 +23,27 @@ G_BUS_NAME_OWNER_GET_CLASS
</SECTION>
<SECTION>
+<FILE>gbusnamewatcher</FILE>
+<TITLE>GBusNameWatcher</TITLE>
+GBusNameWatcher
+GBusNameWatcherClass
+g_bus_name_watcher_new
+g_bus_name_watcher_new_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
+G_IS_BUS_NAME_WATCHER
+G_TYPE_BUS_NAME_WATCHER
+g_bus_name_watcher_get_type
+G_BUS_NAME_WATCHER_CLASS
+G_IS_BUS_NAME_WATCHER_CLASS
+G_BUS_NAME_WATCHER_GET_CLASS
+</SECTION>
+
+<SECTION>
<FILE>gdbusconnection</FILE>
<TITLE>GDBusConnection</TITLE>
GMessageBusType
diff --git a/docs/reference/gdbus/gdbus.types b/docs/reference/gdbus/gdbus.types
index 778d932..c57eda6 100644
--- a/docs/reference/gdbus/gdbus.types
+++ b/docs/reference/gdbus/gdbus.types
@@ -1,5 +1,6 @@
g_dbus_connection_get_type
g_bus_name_owner_get_type
+g_bus_name_watcher_get_type
g_message_bus_type_get_type
g_bus_name_owner_flags_get_type
g_dbus_error_get_type
diff --git a/gdbus/Makefile.am b/gdbus/Makefile.am
index 466e059..1d702ec 100644
--- a/gdbus/Makefile.am
+++ b/gdbus/Makefile.am
@@ -80,6 +80,7 @@ gdbus_headers = \
gdbusmainloop.h \
gdbusconnection.h \
gbusnameowner.h \
+ gbusnamewatcher.h \
$(NULL)
libgdbus_2_0_la_SOURCES = \
@@ -92,6 +93,7 @@ libgdbus_2_0_la_SOURCES = \
gdbusmainloop.h gdbusmainloop.c \
gdbusconnection.h gdbusconnection.c \
gbusnameowner.h gbusnameowner.c \
+ gbusnamewatcher.h gbusnamewatcher.c \
gdbusprivate.h gdbusprivate.c \
gdbusalias.h \
gdbusaliasdef.c \
diff --git a/gdbus/gbusnameowner.c b/gdbus/gbusnameowner.c
index c90f2ca..9f6185a 100644
--- a/gdbus/gbusnameowner.c
+++ b/gdbus/gbusnameowner.c
@@ -223,33 +223,6 @@ g_bus_name_owner_set_property (GObject *object,
}
}
-#if 0
-static void
-request_name_cb (GMessageBusConnection *bus,
- GAsyncResult *res,
- gpointer user_data)
-{
- GMessageBusRequestNameFlags reply;
- GBusNameOwner *owner = G_BUS_NAME_OWNER (user_data);
-
- reply = g_message_bus_connection_request_name_finish (bus,
- res,
- NULL);
- if (reply == G_MESSAGE_BUS_REQUEST_NAME_REPLY_PRIMARY_OWNER)
- {
- owner->priv->owns_name = TRUE;
- g_object_notify (G_OBJECT (owner), "owns-name");
- g_signal_emit (owner, signals[NAME_ACQUIRED_SIGNAL], 0);
- }
- else
- {
- //g_signal_emit (owner, signals[FAILED_TO_ACQUIRE_NAME_SIGNAL], 0);
- }
-
- g_object_unref (owner);
-}
-#endif
-
#define PRINT_MESSAGE(message) \
do { \
const gchar *message_type; \
@@ -556,9 +529,10 @@ g_bus_name_owner_class_init (GBusNameOwnerClass *klass)
}
static void
-g_bus_name_owner_init (GBusNameOwner *connection)
+g_bus_name_owner_init (GBusNameOwner *owner)
{
- connection->priv = G_TYPE_INSTANCE_GET_PRIVATE (connection, G_TYPE_BUS_NAME_OWNER, GBusNameOwnerPrivate);
+ owner->priv = G_TYPE_INSTANCE_GET_PRIVATE (owner, G_TYPE_BUS_NAME_OWNER, GBusNameOwnerPrivate);
+ owner->priv->in_construction_phase = TRUE;
}
/* ---------------------------------------------------------------------------------------------------- */
@@ -579,15 +553,17 @@ bus_release_name_cb (DBusPendingCall *pending,
g_assert (reply != NULL);
dbus_error_init (&dbus_error);
- if (dbus_message_get_args (reply,
- &dbus_error,
- DBUS_TYPE_UINT32, &release_name_reply,
- DBUS_TYPE_INVALID))
+ if (!dbus_set_error_from_message (reply, &dbus_error))
{
- g_simple_async_result_set_op_res_gpointer (simple, GINT_TO_POINTER (release_name_reply), NULL);
- goto done;
+ if (dbus_message_get_args (reply,
+ &dbus_error,
+ DBUS_TYPE_UINT32, &release_name_reply,
+ DBUS_TYPE_INVALID))
+ {
+ g_simple_async_result_set_op_res_gpointer (simple, GINT_TO_POINTER (release_name_reply), NULL);
+ goto done;
+ }
}
-
error = g_dbus_error_new_for_dbus_error (&dbus_error,
NULL,
NULL);
@@ -730,18 +706,21 @@ bus_release_name_sync (GDBusConnection *connection,
goto out;
}
- if (!dbus_message_get_args (reply,
- &dbus_error,
- DBUS_TYPE_UINT32, &release_name_reply,
- DBUS_TYPE_INVALID))
+ if (!dbus_set_error_from_message (&dbus_error, reply))
{
- g_dbus_error_set_dbus_error (error,
- &dbus_error,
- NULL,
- NULL);
- dbus_error_free (&dbus_error);
- goto out;
+ if (dbus_message_get_args (reply,
+ &dbus_error,
+ DBUS_TYPE_UINT32, &release_name_reply,
+ DBUS_TYPE_INVALID))
+ {
+ goto out;
+ }
}
+ g_dbus_error_set_dbus_error (error,
+ &dbus_error,
+ NULL,
+ NULL);
+ dbus_error_free (&dbus_error);
out:
if (message != NULL)
@@ -782,15 +761,17 @@ bus_request_name_cb (DBusPendingCall *pending,
g_assert (reply != NULL);
dbus_error_init (&dbus_error);
- if (dbus_message_get_args (reply,
- &dbus_error,
- DBUS_TYPE_UINT32, &request_name_reply,
- DBUS_TYPE_INVALID))
+ if (!dbus_set_error_from_message (&dbus_error, reply))
{
- g_simple_async_result_set_op_res_gpointer (simple, GINT_TO_POINTER (request_name_reply), NULL);
- goto done;
+ if (dbus_message_get_args (reply,
+ &dbus_error,
+ DBUS_TYPE_UINT32, &request_name_reply,
+ DBUS_TYPE_INVALID))
+ {
+ g_simple_async_result_set_op_res_gpointer (simple, GINT_TO_POINTER (request_name_reply), NULL);
+ goto done;
+ }
}
-
error = g_dbus_error_new_for_dbus_error (&dbus_error,
NULL,
NULL);
@@ -1039,7 +1020,6 @@ g_bus_name_owner_new (GMessageBusType bus_type,
NULL));
data->owner = owner;
- owner->priv->in_construction_phase = TRUE;
g_object_unref (connection);
@@ -1110,7 +1090,6 @@ g_bus_name_owner_new_for_connection (GDBusConnection *connection,
data->user_data = user_data;
data->is_for_connection = TRUE;
data->owner = owner;
- owner->priv->in_construction_phase = TRUE;
/* connection is open.. try to claim the name */
bus_request_name (data->owner->priv->connection,
diff --git a/gdbus/gbusnamewatcher.c b/gdbus/gbusnamewatcher.c
new file mode 100644
index 0000000..2e9f778
--- /dev/null
+++ b/gdbus/gbusnamewatcher.c
@@ -0,0 +1,1231 @@
+/* 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 "gdbusenumtypes.h"
+#include "gdbusconnection.h"
+#include "gdbuserror.h"
+#include "gdbusprivate.h"
+
+#include "gdbusalias.h"
+
+/**
+ * SECTION:gbusnamewatcher
+ * @short_description: Watch names on the bus
+ * @include: gdbus/gdbus.h
+ *
+ * #GBusNameWatcher is a utility class that makes it easy to track
+ * whether a name exists on a message bus. See
+ * g_bus_name_watcher_new() for an example.
+ */
+
+struct _GBusNameWatcherPrivate
+{
+ gchar *name;
+
+ GDBusConnection *connection;
+
+ gchar *name_owner;
+
+ gchar *match_rule;
+
+ /* these variables are used to store async callbacks for users calling _new() */
+ gboolean in_init_phase;
+ GPtrArray *init_phase_pending_simple_async_results;
+};
+
+enum
+{
+ PROP_0,
+ PROP_NAME,
+ PROP_NAME_OWNER,
+ PROP_CONNECTION,
+};
+
+enum
+{
+ NAME_APPEARED_SIGNAL,
+ NAME_VANISHED_SIGNAL,
+ LAST_SIGNAL,
+};
+
+G_LOCK_DEFINE_STATIC (watcher_lock);
+
+static DBusHandlerResult
+filter_function (DBusConnection *dbus_1_connection,
+ DBusMessage *message,
+ void *user_data);
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+static GBusNameWatcher *singletons_get_watcher (GDBusConnection *connection, const gchar *name);
+static void singletons_add_watcher (GBusNameWatcher *watcher);
+static void singletons_remove_watcher (GBusNameWatcher *watcher);
+
+static void bus_get_name_owner (GDBusConnection *connection,
+ const gchar *name,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+static gchar *bus_get_name_owner_finish (GDBusConnection *bus,
+ GAsyncResult *res,
+ GError **error);
+
+static void bus_add_match (GDBusConnection *connection,
+ const gchar *match_rule,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+#if 0
+static gboolean bus_add_match_finish (GDBusConnection *bus,
+ GAsyncResult *res,
+ GError **error);
+#endif
+
+static void bus_remove_match (GDBusConnection *connection,
+ const gchar *match_rule,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+#if 0
+static gboolean bus_remove_match_finish (GDBusConnection *bus,
+ GAsyncResult *res,
+ GError **error);
+#endif
+
+static void on_connection_opened (GDBusConnection *connection,
+ gpointer user_data);
+
+static void on_connection_closed (GDBusConnection *connection,
+ gpointer user_data);
+
+G_DEFINE_TYPE (GBusNameWatcher, g_bus_name_watcher, G_TYPE_OBJECT);
+
+static void
+g_bus_name_watcher_dispose (GObject *object)
+{
+ GBusNameWatcher *watcher = G_BUS_NAME_WATCHER (object);
+
+ G_LOCK (watcher_lock);
+ singletons_remove_watcher (watcher);
+ G_UNLOCK (watcher_lock);
+
+ if (G_OBJECT_CLASS (g_bus_name_watcher_parent_class)->dispose != NULL)
+ G_OBJECT_CLASS (g_bus_name_watcher_parent_class)->dispose (object);
+}
+
+static void
+g_bus_name_watcher_finalize (GObject *object)
+{
+ GBusNameWatcher *watcher = G_BUS_NAME_WATCHER (object);
+
+ if (watcher->priv->connection != NULL)
+ {
+ g_signal_handlers_disconnect_by_func (watcher->priv->connection, on_connection_opened, watcher);
+ g_signal_handlers_disconnect_by_func (watcher->priv->connection, on_connection_closed, watcher);
+
+ if (g_dbus_connection_get_is_open (watcher->priv->connection))
+ {
+ bus_remove_match (watcher->priv->connection,
+ watcher->priv->match_rule,
+ NULL,
+ NULL);
+
+ dbus_connection_remove_filter (g_dbus_connection_get_dbus_1_connection (watcher->priv->connection),
+ filter_function,
+ watcher);
+ }
+
+ g_object_unref (watcher->priv->connection);
+ }
+ g_free (watcher->priv->name);
+ g_free (watcher->priv->name_owner);
+ g_free (watcher->priv->match_rule);
+
+ if (G_OBJECT_CLASS (g_bus_name_watcher_parent_class)->finalize != NULL)
+ G_OBJECT_CLASS (g_bus_name_watcher_parent_class)->finalize (object);
+}
+
+static void
+g_bus_name_watcher_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GBusNameWatcher *watcher = G_BUS_NAME_WATCHER (object);
+
+ switch (prop_id)
+ {
+ case PROP_NAME:
+ g_value_set_string (value, g_bus_name_watcher_get_name (watcher));
+ break;
+
+ case PROP_NAME_OWNER:
+ g_value_set_string (value, g_bus_name_watcher_get_name_owner (watcher));
+ break;
+
+ case PROP_CONNECTION:
+ g_value_set_object (value, g_bus_name_watcher_get_connection (watcher));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+g_bus_name_watcher_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GBusNameWatcher *watcher = G_BUS_NAME_WATCHER (object);
+
+ switch (prop_id)
+ {
+ case PROP_NAME:
+ watcher->priv->name = g_value_dup_string (value);
+ break;
+
+ case PROP_CONNECTION:
+ watcher->priv->connection = g_value_dup_object (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+#define PRINT_MESSAGE(message) \
+ do { \
+ const gchar *message_type; \
+ switch (dbus_message_get_type (message)) \
+ { \
+ case DBUS_MESSAGE_TYPE_METHOD_CALL: \
+ message_type = "method_call"; \
+ break; \
+ case DBUS_MESSAGE_TYPE_METHOD_RETURN: \
+ message_type = "method_return"; \
+ break; \
+ case DBUS_MESSAGE_TYPE_ERROR: \
+ message_type = "error"; \
+ break; \
+ case DBUS_MESSAGE_TYPE_SIGNAL: \
+ message_type = "signal"; \
+ break; \
+ case DBUS_MESSAGE_TYPE_INVALID: \
+ message_type = "invalid"; \
+ break; \
+ default: \
+ message_type = "unknown"; \
+ break; \
+ } \
+ g_print ("new message:\n" \
+ " type: %s\n" \
+ " sender: %s\n" \
+ " destination: %s\n" \
+ " path: %s\n" \
+ " interface: %s\n" \
+ " member: %s\n", \
+ message_type, \
+ dbus_message_get_sender (message), \
+ dbus_message_get_destination (message), \
+ dbus_message_get_path (message), \
+ dbus_message_get_interface (message), \
+ dbus_message_get_member (message)); \
+ } while (FALSE)
+
+static DBusHandlerResult
+filter_function (DBusConnection *dbus_1_connection,
+ DBusMessage *message,
+ void *user_data)
+{
+ GBusNameWatcher *watcher = G_BUS_NAME_WATCHER (user_data);
+ DBusError dbus_error;
+ const gchar *name;
+ 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);
+
+ /* we only care about NameOwnerChanged */
+ if (!(dbus_message_is_signal (message,
+ DBUS_INTERFACE_DBUS,
+ "NameOwnerChanged") &&
+ g_strcmp0 (dbus_message_get_sender (message), DBUS_SERVICE_DBUS) == 0 &&
+ g_strcmp0 (dbus_message_get_path (message), DBUS_PATH_DBUS) == 0))
+ goto out;
+
+ /* extract parameters */
+ dbus_error_init (&dbus_error);
+ if (!dbus_message_get_args (message,
+ &dbus_error,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_STRING, &old_owner,
+ DBUS_TYPE_STRING, &new_owner,
+ DBUS_TYPE_INVALID))
+ {
+ g_warning ("Error extracting parameters for NameOwnerChanged signal: %s: %s",
+ dbus_error.name, dbus_error.message);
+ dbus_error_free (&dbus_error);
+ goto out;
+ }
+
+ /* we only care about a specific name */
+ 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);
+ watcher->priv->name_owner = NULL;
+ g_object_notify (G_OBJECT (watcher), "name-owner");
+ g_signal_emit (watcher, signals[NAME_VANISHED_SIGNAL], 0);
+ }
+
+ if (new_owner != NULL && strlen (new_owner) > 0)
+ {
+ g_warn_if_fail (watcher->priv->name_owner == NULL);
+ g_free (watcher->priv->name_owner);
+ watcher->priv->name_owner = g_strdup (new_owner);
+ g_object_notify (G_OBJECT (watcher), "name-owner");
+ g_signal_emit (watcher, signals[NAME_APPEARED_SIGNAL], 0);
+ }
+
+ out:
+
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
+static void
+on_connection_opened (GDBusConnection *connection,
+ gpointer user_data)
+{
+ GBusNameWatcher *watcher = G_BUS_NAME_WATCHER (user_data);
+
+ /* set up a filter function for listening on (some) NameOwnerChanged messages on the DBusConnection */
+ if (!dbus_connection_add_filter (g_dbus_connection_get_dbus_1_connection (watcher->priv->connection),
+ filter_function,
+ watcher,
+ NULL))
+ _g_dbus_oom ();
+
+ /* set up match rule */
+ bus_add_match (watcher->priv->connection,
+ watcher->priv->match_rule,
+ NULL,
+ NULL);
+
+ /* TODO: if this is for a bus reconnection, we actually need check bus
+ * name again since someone may acquire the name before our match
+ * rules are added
+ */
+}
+
+static void
+on_connection_closed (GDBusConnection *connection,
+ gpointer user_data)
+{
+ GBusNameWatcher *watcher = G_BUS_NAME_WATCHER (user_data);
+
+ /* no need to remove filter; it is removed when the DBusConnection is destroyed */
+
+ /* bus went down.. so emit ::name-vanished */
+ if (watcher->priv->name_owner != NULL)
+ {
+ g_free (watcher->priv->name_owner);
+ watcher->priv->name_owner = NULL;
+ g_object_notify (G_OBJECT (watcher), "name-owner");
+ g_signal_emit (watcher, signals[NAME_VANISHED_SIGNAL], 0);
+ }
+}
+
+static void
+g_bus_name_watcher_constructed (GObject *object)
+{
+ GBusNameWatcher *watcher = G_BUS_NAME_WATCHER (object);
+
+ watcher->priv->match_rule = g_strdup_printf ("type='signal',"
+ "sender='org.freedesktop.DBus',"
+ "member='NameOwnerChanged',"
+ "arg0='%s'",
+ watcher->priv->name);
+
+ g_signal_connect (watcher->priv->connection, "opened", G_CALLBACK (on_connection_opened), watcher);
+ g_signal_connect (watcher->priv->connection, "closed", G_CALLBACK (on_connection_closed), watcher);
+ if (g_dbus_connection_get_is_open (watcher->priv->connection))
+ on_connection_opened (watcher->priv->connection, watcher);
+
+ if (G_OBJECT_CLASS (g_bus_name_watcher_parent_class)->constructed != NULL)
+ G_OBJECT_CLASS (g_bus_name_watcher_parent_class)->constructed (object);
+}
+
+static void
+g_bus_name_watcher_class_init (GBusNameWatcherClass *klass)
+{
+ GObjectClass *gobject_class;
+
+ g_type_class_add_private (klass, sizeof (GBusNameWatcherPrivate));
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->dispose = g_bus_name_watcher_dispose;
+ gobject_class->finalize = g_bus_name_watcher_finalize;
+ gobject_class->constructed = g_bus_name_watcher_constructed;
+ gobject_class->get_property = g_bus_name_watcher_get_property;
+ gobject_class->set_property = g_bus_name_watcher_set_property;
+
+ /**
+ * GBusNameWatcher:name:
+ *
+ * The name (well-known or unique) to watch.
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_NAME,
+ g_param_spec_string ("name",
+ _("name"),
+ _("The name to watch"),
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB |
+ G_PARAM_STATIC_NICK));
+
+
+ /**
+ * GBusNameWatcher:name-owner:
+ *
+ * The unique name of the owner of the name being watched or %NULL
+ * if no-one is owning the name.
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_NAME_OWNER,
+ g_param_spec_string ("name-owner",
+ _("name-owner"),
+ _("Owner of the name being watched"),
+ NULL,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB |
+ G_PARAM_STATIC_NICK));
+
+
+ /**
+ * GBusNameWatcher:connection:
+ *
+ * The #GMessageBusConnection used for watching the name.
+ */
+ g_object_class_install_property (gobject_class,
+ PROP_CONNECTION,
+ g_param_spec_object ("connection",
+ _("connection"),
+ _("The connection used for watching the name"),
+ G_TYPE_DBUS_CONNECTION,
+ G_PARAM_READABLE |
+ G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_NAME |
+ G_PARAM_STATIC_BLURB |
+ G_PARAM_STATIC_NICK));
+
+ /**
+ * GBusNameWatcher::name-vanished:
+ * @watcher: The #GBusNameWatcher emitting the signal.
+ *
+ * Emitted when the name being watched vanishes or the connection is closed.
+ **/
+ signals[NAME_VANISHED_SIGNAL] = g_signal_new ("name-vanished",
+ G_TYPE_BUS_NAME_WATCHER,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GBusNameWatcherClass, name_vanished),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+ /**
+ * GBusNameWatcher::name-appeared:
+ * @watcher: The #GBusNameWatcher emitting the signal.
+ *
+ * Emitted when the name being watched appears or the connection is opened.
+ **/
+ signals[NAME_APPEARED_SIGNAL] = g_signal_new ("name-appeared",
+ G_TYPE_BUS_NAME_WATCHER,
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GBusNameWatcherClass, name_appeared),
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+}
+
+static void
+g_bus_name_watcher_init (GBusNameWatcher *watcher)
+{
+ watcher->priv = G_TYPE_INSTANCE_GET_PRIVATE (watcher, G_TYPE_BUS_NAME_WATCHER, GBusNameWatcherPrivate);
+ watcher->priv->in_init_phase = TRUE;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+bus_add_match_cb (DBusPendingCall *pending,
+ void *user_data)
+{
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+ DBusMessage *reply;
+ DBusError dbus_error;
+ GError *error;
+
+ reply = dbus_pending_call_steal_reply (pending);
+ g_assert (reply != NULL);
+
+ dbus_error_init (&dbus_error);
+ if (!dbus_set_error_from_message (&dbus_error, reply))
+ goto done;
+
+ error = g_dbus_error_new_for_dbus_error (&dbus_error,
+ NULL,
+ NULL);
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ dbus_error_free (&dbus_error);
+
+ done:
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+ dbus_message_unref (reply);
+}
+
+static void
+bus_add_match (GDBusConnection *connection,
+ const gchar *match_rule,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ DBusMessage *message;
+ DBusPendingCall *pending_call;
+
+ g_return_if_fail (G_IS_DBUS_CONNECTION (connection));
+ g_return_if_fail (match_rule != NULL);
+
+ simple = g_simple_async_result_new (G_OBJECT (connection),
+ callback,
+ user_data,
+ bus_add_match);
+
+ if (g_dbus_connection_get_dbus_1_connection (connection) == NULL)
+ {
+ g_simple_async_result_set_error (simple,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ _("Not connected to message bus"));
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+ goto out;
+ }
+
+ if ((message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "AddMatch")) == NULL)
+ _g_dbus_oom ();
+ if (!dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &match_rule,
+ DBUS_TYPE_INVALID))
+ _g_dbus_oom ();
+
+ if (!dbus_connection_send_with_reply (g_dbus_connection_get_dbus_1_connection (connection),
+ message,
+ &pending_call,
+ -1))
+ _g_dbus_oom ();
+
+ dbus_message_unref (message);
+
+ dbus_pending_call_set_notify (pending_call,
+ bus_add_match_cb,
+ simple,
+ NULL);
+
+ out:
+ ;
+}
+
+#if 0
+static gboolean
+bus_add_match_finish (GDBusConnection *bus,
+ GAsyncResult *res,
+ GError **error)
+{
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+ g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == bus_add_match);
+ return !g_simple_async_result_propagate_error (simple, error);
+}
+#endif
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+bus_remove_match_cb (DBusPendingCall *pending,
+ void *user_data)
+{
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+ DBusMessage *reply;
+ DBusError dbus_error;
+ GError *error;
+
+ reply = dbus_pending_call_steal_reply (pending);
+ g_assert (reply != NULL);
+
+ dbus_error_init (&dbus_error);
+ if (!dbus_set_error_from_message (&dbus_error, reply))
+ goto done;
+
+ error = g_dbus_error_new_for_dbus_error (&dbus_error,
+ NULL,
+ NULL);
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ dbus_error_free (&dbus_error);
+
+ done:
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+ dbus_message_unref (reply);
+}
+
+static void
+bus_remove_match (GDBusConnection *connection,
+ const gchar *match_rule,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ DBusMessage *message;
+ DBusPendingCall *pending_call;
+
+ g_return_if_fail (G_IS_DBUS_CONNECTION (connection));
+ g_return_if_fail (match_rule != NULL);
+
+ simple = g_simple_async_result_new (G_OBJECT (connection),
+ callback,
+ user_data,
+ bus_remove_match);
+
+ if (g_dbus_connection_get_dbus_1_connection (connection) == NULL)
+ {
+ g_simple_async_result_set_error (simple,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ _("Not connected to message bus"));
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+ goto out;
+ }
+
+ if ((message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "RemoveMatch")) == NULL)
+ _g_dbus_oom ();
+ if (!dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &match_rule,
+ DBUS_TYPE_INVALID))
+ _g_dbus_oom ();
+
+ if (!dbus_connection_send_with_reply (g_dbus_connection_get_dbus_1_connection (connection),
+ message,
+ &pending_call,
+ -1))
+ _g_dbus_oom ();
+
+ dbus_message_unref (message);
+
+ dbus_pending_call_set_notify (pending_call,
+ bus_remove_match_cb,
+ simple,
+ NULL);
+
+ out:
+ ;
+}
+
+#if 0
+static gboolean
+bus_remove_match_finish (GDBusConnection *bus,
+ GAsyncResult *res,
+ GError **error)
+{
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+ g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == bus_remove_match);
+ return !g_simple_async_result_propagate_error (simple, error);
+}
+#endif
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+bus_get_name_owner_cb (DBusPendingCall *pending,
+ void *user_data)
+{
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+ DBusMessage *reply;
+ DBusError dbus_error;
+ GError *error;
+ gchar *get_name_owner_reply;
+
+ reply = dbus_pending_call_steal_reply (pending);
+ g_assert (reply != NULL);
+
+ dbus_error_init (&dbus_error);
+ if (!dbus_set_error_from_message (&dbus_error, reply))
+ {
+ if (dbus_message_get_args (reply,
+ &dbus_error,
+ DBUS_TYPE_STRING, &get_name_owner_reply,
+ DBUS_TYPE_INVALID))
+ {
+ g_simple_async_result_set_op_res_gpointer (simple, g_strdup (get_name_owner_reply), g_free);
+ goto done;
+ }
+ }
+ error = g_dbus_error_new_for_dbus_error (&dbus_error,
+ NULL,
+ NULL);
+ g_simple_async_result_set_from_error (simple, error);
+ g_error_free (error);
+ dbus_error_free (&dbus_error);
+
+ done:
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+ dbus_message_unref (reply);
+}
+
+static void
+bus_get_name_owner (GDBusConnection *connection,
+ const gchar *name,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GSimpleAsyncResult *simple;
+ DBusMessage *message;
+ DBusPendingCall *pending_call;
+
+ g_return_if_fail (G_IS_DBUS_CONNECTION (connection));
+ g_return_if_fail (name != NULL);
+
+ simple = g_simple_async_result_new (G_OBJECT (connection),
+ callback,
+ user_data,
+ bus_get_name_owner);
+
+ if (g_dbus_connection_get_dbus_1_connection (connection) == NULL)
+ {
+ g_simple_async_result_set_error (simple,
+ G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ _("Not connected to message bus"));
+ g_simple_async_result_complete (simple);
+ g_object_unref (simple);
+ goto out;
+ }
+
+ if ((message = dbus_message_new_method_call (DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS,
+ "GetNameOwner")) == NULL)
+ _g_dbus_oom ();
+ if (!dbus_message_append_args (message,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID))
+ _g_dbus_oom ();
+
+ if (!dbus_connection_send_with_reply (g_dbus_connection_get_dbus_1_connection (connection),
+ message,
+ &pending_call,
+ -1))
+ _g_dbus_oom ();
+
+ dbus_message_unref (message);
+
+ dbus_pending_call_set_notify (pending_call,
+ bus_get_name_owner_cb,
+ simple,
+ NULL);
+
+ out:
+ ;
+}
+
+static gchar *
+bus_get_name_owner_finish (GDBusConnection *bus,
+ GAsyncResult *res,
+ GError **error)
+{
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+ gchar *ret;
+
+ ret = NULL;
+
+ g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == bus_get_name_owner);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ goto out;
+
+ ret = g_strdup (g_simple_async_result_get_op_res_gpointer (simple));
+
+ out:
+ return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+/* routines used for singleton handling */
+
+/* maps from connection to GHashTable (that maps from names to GBusNameWatcher) */
+static GHashTable *map_connection_to_map_of_watched_names_to_watcher = NULL;
+
+/* the following singletons_* functions must be called with watcher_lock held */
+
+/* called from _new() before creating a GBusNameWatcher - result is not reffed */
+static GBusNameWatcher *
+singletons_get_watcher (GDBusConnection *connection, const gchar *name)
+{
+ GBusNameWatcher *ret;
+ GHashTable *map_of_watched_names_to_watcher;
+
+ g_return_val_if_fail (connection != NULL, NULL);
+ g_return_val_if_fail (name != NULL, NULL);
+
+ ret = NULL;
+
+ if (map_connection_to_map_of_watched_names_to_watcher == NULL)
+ goto out;
+
+ map_of_watched_names_to_watcher = g_hash_table_lookup (map_connection_to_map_of_watched_names_to_watcher,
+ connection);
+ if (map_of_watched_names_to_watcher == NULL)
+ goto out;
+
+ ret = g_hash_table_lookup (map_of_watched_names_to_watcher,
+ name);
+
+ out:
+ return ret;
+}
+
+/* called from _new() after creating the GBusNameWatcher */
+static void
+singletons_add_watcher (GBusNameWatcher *watcher)
+{
+ GHashTable *map_of_watched_names_to_watcher;
+
+ g_return_if_fail (watcher != NULL);
+
+ if (map_connection_to_map_of_watched_names_to_watcher == NULL)
+ map_connection_to_map_of_watched_names_to_watcher = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+ map_of_watched_names_to_watcher = g_hash_table_lookup (map_connection_to_map_of_watched_names_to_watcher,
+ watcher->priv->connection);
+ if (map_of_watched_names_to_watcher == NULL)
+ {
+ map_of_watched_names_to_watcher = g_hash_table_new (g_str_hash, g_str_equal);
+ g_hash_table_insert (map_connection_to_map_of_watched_names_to_watcher,
+ watcher->priv->connection,
+ map_of_watched_names_to_watcher);
+ }
+
+ g_hash_table_insert (map_of_watched_names_to_watcher,
+ watcher->priv->name,
+ watcher);
+}
+
+/* called from dispose() - ie. can be called multiple times so don't assert anything */
+static void
+singletons_remove_watcher (GBusNameWatcher *watcher)
+{
+ GHashTable *map_of_watched_names_to_watcher;
+
+ if (map_connection_to_map_of_watched_names_to_watcher == NULL)
+ goto out;
+
+ map_of_watched_names_to_watcher = g_hash_table_lookup (map_connection_to_map_of_watched_names_to_watcher,
+ watcher->priv->connection);
+ if (map_of_watched_names_to_watcher == NULL)
+ goto out;
+
+ if (!g_hash_table_remove (map_of_watched_names_to_watcher,
+ watcher->priv->name))
+ goto out;
+
+ /* clean up as needed */
+ if (g_hash_table_size (map_of_watched_names_to_watcher) == 0)
+ {
+ g_hash_table_remove (map_connection_to_map_of_watched_names_to_watcher, watcher->priv->connection);
+ g_hash_table_destroy (map_of_watched_names_to_watcher);
+
+ if (g_hash_table_size (map_connection_to_map_of_watched_names_to_watcher) == 0)
+ {
+ g_hash_table_destroy (map_connection_to_map_of_watched_names_to_watcher);
+ map_connection_to_map_of_watched_names_to_watcher = NULL;
+ }
+ }
+
+ out:
+ ;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+typedef struct
+{
+ GBusNameWatcher *watcher;
+} ConstructData;
+
+static void
+construct_data_free (ConstructData *data)
+{
+ g_free (data);
+}
+
+static void
+get_name_owner_cb (GDBusConnection *connection,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ ConstructData *data = user_data;
+ GSimpleAsyncResult *simple;
+ GError *error;
+ gchar *name_owner;
+ guint n;
+
+ error = NULL;
+ name_owner = bus_get_name_owner_finish (connection,
+ result,
+ &error);
+
+ G_LOCK (watcher_lock);
+
+ /* NAME_HAS_NO_OWNER error => don't report this as an error, it just means there is no name owner */
+ if (error != NULL && (error->domain != G_DBUS_ERROR && error->code != G_DBUS_ERROR_NAME_HAS_NO_OWNER))
+ {
+ /* report error back */
+ for (n = 0; n < data->watcher->priv->init_phase_pending_simple_async_results->len; n++)
+ {
+ simple = G_SIMPLE_ASYNC_RESULT (data->watcher->priv->init_phase_pending_simple_async_results->pdata[n]);
+ g_simple_async_result_set_from_error (simple, error);
+ g_simple_async_result_complete_in_idle (simple);
+ g_object_unref (simple);
+ }
+ g_error_free (error);
+ }
+ else
+ {
+ g_free (data->watcher->priv->name_owner);
+ if (error != NULL)
+ {
+ g_error_free (error);
+ data->watcher->priv->name_owner = NULL;
+ }
+ else
+ {
+ data->watcher->priv->name_owner = name_owner; /* steals the returned string */
+ }
+
+ /* complete operations without error */
+ for (n = 0; n < data->watcher->priv->init_phase_pending_simple_async_results->len; n++)
+ {
+ simple = G_SIMPLE_ASYNC_RESULT (data->watcher->priv->init_phase_pending_simple_async_results->pdata[n]);
+ g_simple_async_result_complete_in_idle (simple);
+ g_object_unref (simple);
+ }
+ }
+
+ data->watcher->priv->in_init_phase = FALSE;
+ g_ptr_array_free (data->watcher->priv->init_phase_pending_simple_async_results, TRUE);
+ G_UNLOCK (watcher_lock);
+
+
+ construct_data_free (data);
+}
+
+static void
+get_connection_cb (GDBusConnection *connection,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ ConstructData *data = user_data;
+ GError *error;
+
+ if (data->watcher == NULL)
+ goto out;
+
+ error = NULL;
+ if (!g_dbus_connection_bus_get_finish (connection,
+ result,
+ &error))
+ {
+ GSimpleAsyncResult *simple;
+ guint n;
+
+ G_LOCK (watcher_lock);
+ /* connection is not open.. report back */
+ for (n = 0; n < data->watcher->priv->init_phase_pending_simple_async_results->len; n++)
+ {
+ simple = G_SIMPLE_ASYNC_RESULT (data->watcher->priv->init_phase_pending_simple_async_results->pdata[n]);
+ g_simple_async_result_set_from_error (simple, error);
+ g_simple_async_result_complete_in_idle (simple);
+ g_object_unref (simple);
+ }
+ data->watcher->priv->in_init_phase = FALSE;
+ g_ptr_array_free (data->watcher->priv->init_phase_pending_simple_async_results, TRUE);
+ G_UNLOCK (watcher_lock);
+
+ g_error_free (error);
+
+ construct_data_free (data);
+ }
+ else
+ {
+ /* connection is open.. check if the name exists */
+ bus_get_name_owner (data->watcher->priv->connection,
+ data->watcher->priv->name,
+ (GAsyncReadyCallback) get_name_owner_cb,
+ data);
+ }
+
+ out:
+ ;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/**
+ * g_bus_name_watcher_new:
+ * @bus_type: A #GMessageBusType specifying what message bus to connect to.
+ * @name: A bus name (well-known or unique).
+ * @cancellable: %NULL or a #GCancellable.
+ * @callback: The callback function to invoke when finished acquiring the name.
+ * @user_data: User data to pass to @callback.
+ *
+ * Creates a new #GBusNameWatcher and then check if someone owns @name
+ * on the bus specified by @bus_type. When the check is finished,
+ * @callback will be invoked (on the main thread) and you can use
+ * g_bus_name_watcher_new_finish() to get the result.
+ *
+ * Note that the returned object may be shared with other callers if
+ * @bus_type and @name matches.
+ *
+ * This class is the preferred way to implement D-Bus clients.
+ * The canonical example follows.
+ * <example><title>Using g_bus_name_watcher_new()</title><programlisting><xi:include xmlns:xi="http://www.w3.org/2001/XInclude" parse="text" href="../../../../gdbus/example-gbusnamewatcher.c"><xi:fallback>FIXME: MISSING XINCLUDE CONTENT</xi:fallback></xi:include></programlisting></example>
+ *
+ * Returns: A #GBusNameWatcher object. Free with g_object_unref().
+ **/
+GBusNameWatcher *
+g_bus_name_watcher_new (GMessageBusType bus_type,
+ const gchar *name,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ GBusNameWatcher *watcher;
+ GDBusConnection *connection;
+ ConstructData *data;
+ GSimpleAsyncResult *simple;
+
+ /* This is all a tad complicated since we support singletons... the problem here
+ * in a nutshell is that there's a window, an initialization phase, where we wait
+ * for two things
+ *
+ * - the connection for the bus to come up
+ * - HasNameOwner() to return
+ *
+ * Now, suppose the user creates two watcher objects with the same bus and name
+ * and no bus is up. The second one will just be a reference to the first one
+ * because we support singletons. So basically while we are initializing, just
+ * add the callback for the second watcher to a list on the first one. Then
+ * when initialization is done, fire all callbacks.
+ */
+
+ data = g_new0 (ConstructData, 1);
+
+ connection = g_dbus_connection_bus_get (bus_type,
+ cancellable,
+ (GAsyncReadyCallback) get_connection_cb,
+ data);
+
+ G_LOCK (watcher_lock);
+ /* check if a suitable watcher already exists */
+ watcher = singletons_get_watcher (connection, name);
+ if (watcher != NULL)
+ {
+ simple = g_simple_async_result_new (G_OBJECT (watcher),
+ callback,
+ user_data,
+ g_bus_name_watcher_new_finish);
+
+ if (watcher->priv->in_init_phase)
+ {
+ /* if still initializing, defer callback until we know what stuff looks like */
+ g_ptr_array_add (watcher->priv->init_phase_pending_simple_async_results, simple);
+ }
+ else
+ {
+ /* otherwise, return the result right away (e.g. in idle) */
+ g_simple_async_result_complete_in_idle (simple);
+ g_object_unref (simple);
+ }
+ g_object_ref (watcher);
+ goto out;
+ }
+
+ watcher = G_BUS_NAME_WATCHER (g_object_new (G_TYPE_BUS_NAME_WATCHER,
+ "name", name,
+ "connection", connection,
+ NULL));
+
+ data->watcher = watcher;
+ watcher->priv->in_init_phase = TRUE;
+ watcher->priv->init_phase_pending_simple_async_results = g_ptr_array_new ();
+
+ simple = g_simple_async_result_new (G_OBJECT (watcher),
+ callback,
+ user_data,
+ g_bus_name_watcher_new_finish);
+
+ g_ptr_array_add (watcher->priv->init_phase_pending_simple_async_results, simple);
+
+ singletons_add_watcher (watcher);
+
+ out:
+ G_UNLOCK (watcher_lock);
+
+ g_object_unref (connection);
+
+ return watcher;
+}
+
+/**
+ * g_bus_name_watcher_new_finish:
+ * @watcher: A #GBusNameWatcher.
+ * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback function passed to g_bus_name_watcher_get().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes attempting to determine the owner of the name being
+ * watched.
+ *
+ * Returns: %TRUE if the name being watched has an owner or %FALSE if
+ * there is no name owner or @error is set.
+ **/
+gboolean
+g_bus_name_watcher_new_finish (GBusNameWatcher *watcher,
+ GAsyncResult *res,
+ GError **error)
+{
+ GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+
+ g_return_val_if_fail (G_IS_BUS_NAME_WATCHER (watcher), FALSE);
+
+ g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_bus_name_watcher_new_finish);
+
+ if (g_simple_async_result_propagate_error (simple, error))
+ return FALSE;
+
+ return watcher->priv->name_owner != NULL;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/**
+ * g_bus_name_watcher_get_name:
+ * @watcher: A #GBusNameWatcher.
+ *
+ * Gets the name that @watcher is watching.
+ *
+ * Returns: The name that @watcher is watching. Do not free this
+ * string, it is owned by @watcher.
+ **/
+const gchar *
+g_bus_name_watcher_get_name (GBusNameWatcher *watcher)
+{
+ g_return_val_if_fail (G_IS_BUS_NAME_WATCHER (watcher), NULL);
+
+ return watcher->priv->name;
+}
+
+/**
+ * g_bus_name_watcher_get_name_owner:
+ * @watcher: A #GBusNameWatcher.
+ *
+ * Gets the unique name of the owner for the name that @watcher is watching, if any.
+ *
+ * To listen for changes, connect to the #GBusNameWatcher::name-appeared
+ * and #GBusNameWatcher::name-vanished signals or listen for notifications
+ * on the #GBusNameWatcher:name-owner property.
+ *
+ * Returns: The unique name for the owner of the name that @watcher is
+ * watching or %NULL if there is no owner. Do not free this string, it
+ * is owned by @watcher.
+ **/
+const gchar *
+g_bus_name_watcher_get_name_owner (GBusNameWatcher *watcher)
+{
+ g_return_val_if_fail (G_IS_BUS_NAME_WATCHER (watcher), NULL);
+
+ return watcher->priv->name_owner;
+}
+
+/**
+ * g_bus_name_watcher_get_connection:
+ * @watcher: A #GBusNameWatcher.
+ *
+ * Gets the #GDBusConnection used for @watcher.
+ *
+ * Returns: A #GDBusConnection object owned by @watcher. Do not unref.
+ **/
+GDBusConnection *
+g_bus_name_watcher_get_connection (GBusNameWatcher *watcher)
+{
+ g_return_val_if_fail (G_IS_BUS_NAME_WATCHER (watcher), NULL);
+
+ return watcher->priv->connection;
+}
+
+#define __G_BUS_NAME_WATCHER_C__
+#include "gdbusaliasdef.c"
diff --git a/gdbus/gbusnamewatcher.h b/gdbus/gbusnamewatcher.h
new file mode 100644
index 0000000..544f988
--- /dev/null
+++ b/gdbus/gbusnamewatcher.h
@@ -0,0 +1,113 @@
+/* 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_BUS_NAME_WATCHER_H__
+#define __G_BUS_NAME_WATCHER_H__
+
+#include <gdbus/gdbustypes.h>
+#include <dbus/dbus.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_BUS_NAME_WATCHER (g_bus_name_watcher_get_type ())
+#define G_BUS_NAME_WATCHER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_BUS_NAME_WATCHER, GBusNameWatcher))
+#define G_BUS_NAME_WATCHER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_BUS_NAME_WATCHER, GBusNameWatcherClass))
+#define G_BUS_NAME_WATCHER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_BUS_NAME_WATCHER, GBusNameWatcherClass))
+#define G_IS_BUS_NAME_WATCHER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_BUS_NAME_WATCHER))
+#define G_IS_BUS_NAME_WATCHER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_BUS_NAME_WATCHER))
+
+typedef struct _GBusNameWatcherClass GBusNameWatcherClass;
+typedef struct _GBusNameWatcherPrivate GBusNameWatcherPrivate;
+
+/**
+ * GBusNameWatcher:
+ *
+ * The #GBusNameWatcher structure contains only private data and
+ * should only be accessed using the provided API.
+ */
+struct _GBusNameWatcher
+{
+ /*< private >*/
+ GObject parent_instance;
+ GBusNameWatcherPrivate *priv;
+};
+
+/**
+ * GBusNameWatcherClass:
+ * @name_appeared: Signal class handler for the #GBusNameWatcher::name-appeared signal.
+ * @name_vanished: Signal class handler for the #GBusNameWatcher::name-vanished signal.
+ *
+ * Class structure for #GBusNameWatcher.
+ */
+struct _GBusNameWatcherClass
+{
+ /*< private >*/
+ GObjectClass parent_class;
+
+ /*< public >*/
+
+ /* Signals */
+ void (*name_appeared) (GBusNameWatcher *watcher);
+ void (*name_vanished) (GBusNameWatcher *watcher);
+
+ /*< private >*/
+ /* Padding for future expansion */
+ void (*_g_reserved1) (void);
+ void (*_g_reserved2) (void);
+ void (*_g_reserved3) (void);
+ void (*_g_reserved4) (void);
+ void (*_g_reserved5) (void);
+ void (*_g_reserved6) (void);
+ void (*_g_reserved7) (void);
+ void (*_g_reserved8) (void);
+};
+
+GType g_bus_name_watcher_get_type (void) G_GNUC_CONST;
+GBusNameWatcher *g_bus_name_watcher_new (GMessageBusType bus_type,
+ const gchar *name,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean g_bus_name_watcher_new_finish (GBusNameWatcher *watcher,
+ GAsyncResult *res,
+ GError **error);
+#if 0
+GBusNameWatcher *g_bus_name_watcher_new_for_connection (GDBusConnection *connection,
+ const gchar *name,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+gboolean g_bus_name_watcher_new_for_connection_finish (GBusNameWatcher *watcher,
+ GAsyncResult *res,
+ GError **error);
+#endif
+const gchar *g_bus_name_watcher_get_name (GBusNameWatcher *watcher);
+const gchar *g_bus_name_watcher_get_name_owner (GBusNameWatcher *watcher);
+GDBusConnection *g_bus_name_watcher_get_connection (GBusNameWatcher *watcher);
+
+G_END_DECLS
+
+#endif /* __G_BUS_NAME_WATCHER_H__ */
diff --git a/gdbus/gdbus.h b/gdbus/gdbus.h
index 6abebc3..334f5a5 100644
--- a/gdbus/gdbus.h
+++ b/gdbus/gdbus.h
@@ -31,6 +31,7 @@
#include <gdbus/gdbusconnection.h>
#include <gdbus/gdbuserror.h>
#include <gdbus/gbusnameowner.h>
+#include <gdbus/gbusnamewatcher.h>
#undef __G_DBUS_D_DBUS_H_INSIDE__
diff --git a/gdbus/gdbus.symbols b/gdbus/gdbus.symbols
index cf10bc3..018250c 100644
--- a/gdbus/gdbus.symbols
+++ b/gdbus/gdbus.symbols
@@ -43,6 +43,17 @@ g_bus_name_owner_get_type
#endif
#endif
+#if IN_HEADER(__G_BUS_NAME_WATCHER_H__)
+#if IN_FILE(__G_BUS_NAME_WATCHER_C__)
+g_bus_name_watcher_new
+g_bus_name_watcher_new_finish
+g_bus_name_watcher_get_connection
+g_bus_name_watcher_get_name
+g_bus_name_watcher_get_name_owner
+g_bus_name_watcher_get_type
+#endif
+#endif
+
#if IN_HEADER(__G_DBUS_MAINLOOP_H__)
#if IN_FILE(__G_DBUS_MAINLOOP_C__)
g_dbus_integrate_dbus_1_connection
diff --git a/gdbus/gdbusconnection.c b/gdbus/gdbusconnection.c
index a461862..5c4b5a3 100644
--- a/gdbus/gdbusconnection.c
+++ b/gdbus/gdbusconnection.c
@@ -60,6 +60,10 @@ struct _GDBusConnectionPrivate
gboolean exit_on_close;
gboolean is_private;
+
+ /* these variables are used to store async callbacks for users calling _new() */
+ gboolean in_init_phase;
+ GPtrArray *init_phase_pending_simple_async_results;
};
enum
@@ -341,6 +345,7 @@ static void
g_dbus_connection_init (GDBusConnection *connection)
{
connection->priv = G_TYPE_INSTANCE_GET_PRIVATE (connection, G_TYPE_DBUS_CONNECTION, GDBusConnectionPrivate);
+ connection->priv->in_init_phase = TRUE;
}
/**
@@ -429,8 +434,9 @@ attempt_connect (GDBusConnection *connection,
}
else
{
- dbus_1_connection = dbus_bus_get (connection->priv->bus_type,
- &dbus_error);
+ /* for now, get a private bus even if it's shared */
+ dbus_1_connection = dbus_bus_get_private (connection->priv->bus_type,
+ &dbus_error);
}
if (dbus_1_connection != NULL)
@@ -573,6 +579,11 @@ g_dbus_connection_set_dbus_1_connection (GDBusConnection *connection,
{
dbus_connection_close (connection->priv->dbus_1_connection);
}
+ else
+ {
+ /* see attempt_connext() where we still get a private bus even for shared stuff */
+ dbus_connection_close (connection->priv->dbus_1_connection);
+ }
dbus_connection_unref (connection->priv->dbus_1_connection);
}
@@ -600,24 +611,43 @@ g_dbus_connection_set_dbus_1_connection (GDBusConnection *connection,
static gboolean
open_bus_connection (gpointer user_data)
{
- GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
- GDBusConnection *connection;
+ GDBusConnection *connection = G_DBUS_CONNECTION (user_data);
GError *error;
-
- connection = G_DBUS_CONNECTION (g_simple_async_result_get_op_res_gpointer (simple));
+ GSimpleAsyncResult *simple;
+ guint n;
+ gboolean did_connect;
error = NULL;
- if (!attempt_connect (connection, &error))
+ did_connect = attempt_connect (connection, &error);
+
+ G_LOCK (connection_lock);
+ if (!did_connect)
{
- g_simple_async_result_set_from_error (simple, error);
+ for (n = 0; n < connection->priv->init_phase_pending_simple_async_results->len; n++)
+ {
+ simple = G_SIMPLE_ASYNC_RESULT (connection->priv->init_phase_pending_simple_async_results->pdata[n]);
+ g_simple_async_result_set_from_error (simple, error);
+ g_simple_async_result_complete_in_idle (simple);
+ g_object_unref (simple);
+ }
+
g_error_free (error);
/* set up timer for attempting to reconnect */
setup_reconnect_timer (connection);
}
-
- g_simple_async_result_complete (simple);
- g_object_unref (simple);
+ else
+ {
+ for (n = 0; n < connection->priv->init_phase_pending_simple_async_results->len; n++)
+ {
+ simple = G_SIMPLE_ASYNC_RESULT (connection->priv->init_phase_pending_simple_async_results->pdata[n]);
+ g_simple_async_result_complete_in_idle (simple);
+ g_object_unref (simple);
+ }
+ }
+ connection->priv->in_init_phase = FALSE;
+ g_ptr_array_free (connection->priv->init_phase_pending_simple_async_results, TRUE);
+ G_UNLOCK (connection_lock);
return FALSE;
}
@@ -632,6 +662,18 @@ g_dbus_connection_bus_get_internal (GMessageBusType bus_type,
GDBusConnection *connection;
GSimpleAsyncResult *simple;
+ /* The fact we support singletons makes this complicated... since there's
+ * a window, the initialization phase, where we wait for the connection
+ * to be established.
+ *
+ * For example, suppose that two calls to g_dbus_connection_new() is
+ * made for the same bus when the program starts up. The we return
+ * two identical objects (since we support singletons) but the
+ * GAsyncReadyCallback are distinct. Hence, when returning a reffed
+ * instance of the singleton, we need to add the callback to the
+ * singleton so callbacks are fired at the right time.
+ */
+
g_return_val_if_fail (bus_type != G_MESSAGE_BUS_TYPE_NONE, NULL);
connection = NULL;
@@ -662,9 +704,9 @@ g_dbus_connection_bus_get_internal (GMessageBusType bus_type,
}
/* singleton handling */
+ G_LOCK (connection_lock);
if (!private)
{
- G_LOCK (connection_lock);
if (bus_type == G_MESSAGE_BUS_TYPE_SESSION && the_session_bus != NULL)
{
connection = g_object_ref (the_session_bus);
@@ -672,8 +714,17 @@ g_dbus_connection_bus_get_internal (GMessageBusType bus_type,
callback,
user_data,
g_dbus_connection_bus_get_finish);
- g_simple_async_result_complete_in_idle (simple);
- g_object_unref (simple);
+ if (connection->priv->in_init_phase)
+ {
+ /* if still initializing, defer callback until we know what stuff looks like */
+ g_ptr_array_add (connection->priv->init_phase_pending_simple_async_results, simple);
+ }
+ else
+ {
+ /* otherwise, return the result right away (e.g. in idle) */
+ g_simple_async_result_complete_in_idle (simple);
+ g_object_unref (simple);
+ }
G_UNLOCK (connection_lock);
goto out;
}
@@ -684,8 +735,17 @@ g_dbus_connection_bus_get_internal (GMessageBusType bus_type,
callback,
user_data,
g_dbus_connection_bus_get_finish);
- g_simple_async_result_complete_in_idle (simple);
- g_object_unref (simple);
+ if (connection->priv->in_init_phase)
+ {
+ /* if still initializing, defer callback until we know what stuff looks like */
+ g_ptr_array_add (connection->priv->init_phase_pending_simple_async_results, simple);
+ }
+ else
+ {
+ /* otherwise, return the result right away (e.g. in idle) */
+ g_simple_async_result_complete_in_idle (simple);
+ g_object_unref (simple);
+ }
G_UNLOCK (connection_lock);
goto out;
}
@@ -714,11 +774,13 @@ g_dbus_connection_bus_get_internal (GMessageBusType bus_type,
callback,
user_data,
private ? g_dbus_connection_bus_get_private_finish : g_dbus_connection_bus_get_finish);
- g_simple_async_result_set_op_res_gpointer (simple,
- connection,
- NULL);
+ connection->priv->init_phase_pending_simple_async_results = g_ptr_array_new ();
+ g_ptr_array_add (connection->priv->init_phase_pending_simple_async_results, simple);
+ G_UNLOCK (connection_lock);
+
g_idle_add (open_bus_connection,
- simple);
+ connection);
+
out:
return connection;
@@ -875,7 +937,10 @@ g_dbus_connection_bus_get_finish (GDBusConnection *connection,
GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE);
g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_dbus_connection_bus_get_finish);
- return !g_simple_async_result_propagate_error (simple, error);
+ if (g_simple_async_result_propagate_error (simple, error))
+ return FALSE;
+ else
+ return g_dbus_connection_get_is_open (connection);
}
/**
@@ -896,7 +961,10 @@ g_dbus_connection_bus_get_private_finish (GDBusConnection *connection,
GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), FALSE);
g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_dbus_connection_bus_get_private_finish);
- return !g_simple_async_result_propagate_error (simple, error);
+ if (g_simple_async_result_propagate_error (simple, error))
+ return FALSE;
+ else
+ return g_dbus_connection_get_is_open (connection);
}
/**
diff --git a/gdbus/gdbustypes.h b/gdbus/gdbustypes.h
index 21dacb2..508613e 100644
--- a/gdbus/gdbustypes.h
+++ b/gdbus/gdbustypes.h
@@ -33,6 +33,7 @@ G_BEGIN_DECLS
typedef struct _GDBusConnection GDBusConnection;
typedef struct _GBusNameOwner GBusNameOwner;
+typedef struct _GBusNameWatcher GBusNameWatcher;
G_END_DECLS
diff --git a/gdbus/tests/connection.c b/gdbus/tests/connection.c
index 58f08c2..05118f9 100644
--- a/gdbus/tests/connection.c
+++ b/gdbus/tests/connection.c
@@ -30,9 +30,39 @@
#include <signal.h>
#include <stdio.h>
+/* TODO: clean up and move to gtestutils.c
+ *
+ * This is needed because libdbus-1 does not give predictable error messages - e.g. you
+ * get a different error message on connecting to a bus if the socket file is there vs
+ * if the socket file is missing.
+ */
+#define _g_assert_error_domain(err, dom) do { if (!err || (err)->domain != dom) \
+ g_assertion_message_error (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, \
+ #err, err, dom, -1); } while (0)
+
/* all tests rely on a shared mainloop */
static GMainLoop *loop = NULL;
+#if 0
+static gboolean
+wakeup_mainloop (gpointer user_data)
+{
+ GMainLoop *sleep_loop = user_data;
+ g_main_loop_quit (sleep_loop);
+ return FALSE;
+}
+
+static void
+sleep_in_mainloop (guint msec)
+{
+ GMainLoop *sleep_loop;
+ sleep_loop = g_main_loop_new (NULL, FALSE);
+ g_timeout_add (msec, wakeup_mainloop, sleep_loop);
+ g_main_loop_run (sleep_loop);
+ g_main_loop_unref (sleep_loop);
+}
+#endif
+
/* ---------------------------------------------------------------------------------------------------- */
/* Utilities for bringing up and tearing down session message bus instances */
@@ -367,7 +397,7 @@ get_connection_callback_no_bus_exists (GDBusConnection *connection,
ret = g_dbus_connection_bus_get_finish (connection,
result,
&error);
- g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FILE_NOT_FOUND);
+ _g_assert_error_domain (error, G_DBUS_ERROR);
g_error_free (error);
g_assert (!ret);
g_main_loop_quit (loop);
@@ -464,11 +494,37 @@ get_connection_callback_private (GDBusConnection *connection,
/* ---------------------------------------------------------------------------------------------------- */
static void
+get_connection_callback_no_bus_exists_singleton (GDBusConnection *connection,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ guint *counter = user_data;
+ GError *error;
+ gboolean ret;
+
+ error = NULL;
+ ret = g_dbus_connection_bus_get_finish (connection,
+ result,
+ &error);
+ _g_assert_error_domain (error, G_DBUS_ERROR);
+ g_error_free (error);
+ g_assert (!ret);
+
+ *counter += 1;
+
+ if (*counter == 2)
+ g_main_loop_quit (loop);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
test_message_bus_connections (void)
{
GDBusConnection *c;
GDBusConnection *c2;
gboolean val;
+ guint counter;
/* Check for correct behavior when no bus is present */
c = g_dbus_connection_bus_get (G_MESSAGE_BUS_TYPE_SESSION,
@@ -548,6 +604,25 @@ test_message_bus_connections (void)
g_assert (!val);
g_object_unref (c);
+
+ /* Check that the error paths are correct when using singletons */
+ counter = 0;
+ c = g_dbus_connection_bus_get (G_MESSAGE_BUS_TYPE_SESSION,
+ NULL,
+ (GAsyncReadyCallback) get_connection_callback_no_bus_exists_singleton,
+ &counter);
+ c2 = g_dbus_connection_bus_get (G_MESSAGE_BUS_TYPE_SESSION,
+ NULL,
+ (GAsyncReadyCallback) get_connection_callback_no_bus_exists_singleton,
+ &counter);
+ g_assert (!g_dbus_connection_get_is_open (c));
+ g_assert (!g_dbus_connection_get_is_open (c2));
+ g_main_loop_run (loop);
+ g_assert (counter == 2);
+ g_assert (!g_dbus_connection_get_is_open (c));
+ g_assert (!g_dbus_connection_get_is_open (c2));
+ g_object_unref (c);
+ g_object_unref (c2);
}
/* ---------------------------------------------------------------------------------------------------- */
@@ -581,7 +656,7 @@ on_owner_cb_expect_failure (GBusNameOwner *owner,
ret = g_bus_name_owner_new_finish (owner,
result,
&error);
- g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FILE_NOT_FOUND);
+ _g_assert_error_domain (error, G_DBUS_ERROR);
g_error_free (error);
g_assert (!ret);
@@ -874,6 +949,9 @@ test_bus_name_owner (void)
g_assert (val);
g_assert (!g_bus_name_owner_get_owns_name (o2));
}
+ /* make sure we don't exit when bringing down the bus */
+ g_dbus_connection_set_exit_on_close (g_bus_name_owner_get_connection (o), FALSE);
+ g_dbus_connection_set_exit_on_close (g_bus_name_owner_get_connection (o2), FALSE);
g_object_unref (o2);
g_object_unref (o);
@@ -881,6 +959,246 @@ test_bus_name_owner (void)
}
/* ---------------------------------------------------------------------------------------------------- */
+/* Test that GBusNameWatcher works correctly */
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+bnw_cb_expect_error (GBusNameWatcher *watcher,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error;
+ gboolean has_name_owner;
+
+ error = NULL;
+ has_name_owner = g_bus_name_watcher_new_finish (watcher,
+ res,
+ &error);
+ _g_assert_error_domain (error, G_DBUS_ERROR);
+ g_error_free (error);
+ g_assert (!has_name_owner);
+
+ g_main_loop_quit (loop);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+bnw_cb_expect_null_owner (GBusNameWatcher *watcher,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error;
+ gboolean has_name_owner;
+
+ error = NULL;
+ has_name_owner = g_bus_name_watcher_new_finish (watcher,
+ res,
+ &error);
+ g_assert_no_error (error);
+ g_assert (!has_name_owner);
+
+ g_main_loop_quit (loop);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+nbw_on_owner_cb_expect_success (GBusNameOwner *owner,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ gboolean ret;
+ GError *error;
+
+ error = NULL;
+ ret = g_bus_name_owner_new_finish (owner,
+ result,
+ &error);
+ g_assert_no_error (error);
+ g_assert (ret);
+
+ g_main_loop_quit (loop);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+bnw_cb_expect_owner (GBusNameWatcher *watcher,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GError *error;
+ gboolean has_name_owner;
+
+ error = NULL;
+ has_name_owner = g_bus_name_watcher_new_finish (watcher,
+ res,
+ &error);
+ g_assert_no_error (error);
+ g_assert (has_name_owner);
+
+ g_main_loop_quit (loop);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+bnw_on_name_vanished (GBusNameWatcher *watcher,
+ gpointer user_data)
+{
+ g_main_loop_quit (loop);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+bnw_on_name_appeared (GBusNameWatcher *watcher,
+ gpointer user_data)
+{
+ g_main_loop_quit (loop);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+bnw_cb_singleton (GBusNameWatcher *watcher,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ gboolean *val = user_data;
+ GError *error;
+ gboolean has_name_owner;
+
+ error = NULL;
+ has_name_owner = g_bus_name_watcher_new_finish (watcher,
+ res,
+ &error);
+ g_assert_no_error (error);
+ g_assert (has_name_owner);
+
+ *val = TRUE;
+
+ g_main_loop_quit (loop);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+test_bus_name_watcher (void)
+{
+ GBusNameWatcher *w;
+ GBusNameWatcher *w2;
+ GBusNameOwner *o;
+ gboolean val;
+
+ /* if no session bus is available, expect the callback to fail */
+ w = g_bus_name_watcher_new (G_MESSAGE_BUS_TYPE_SESSION,
+ "org.gtk.Test.Name1",
+ NULL,
+ (GAsyncReadyCallback) bnw_cb_expect_error,
+ NULL);
+ g_main_loop_run (loop);
+ g_dbus_connection_set_exit_on_close (g_bus_name_watcher_get_connection (w), FALSE);
+ g_object_unref (w);
+
+ /* if a session bus is available, expect the callback to succeed with name_owner==NULL */
+ session_bus_up ();
+ w = g_bus_name_watcher_new (G_MESSAGE_BUS_TYPE_SESSION,
+ "org.gtk.Test.Name1",
+ NULL,
+ (GAsyncReadyCallback) bnw_cb_expect_null_owner,
+ NULL);
+ g_main_loop_run (loop);
+ g_dbus_connection_set_exit_on_close (g_bus_name_watcher_get_connection (w), FALSE);
+ g_object_unref (w);
+
+ /* now start owning a name */
+ o = g_bus_name_owner_new (G_MESSAGE_BUS_TYPE_SESSION,
+ "org.gtk.Test.Name1",
+ G_BUS_NAME_OWNER_FLAGS_NONE,
+ NULL,
+ (GAsyncReadyCallback) nbw_on_owner_cb_expect_success,
+ NULL);
+ g_main_loop_run (loop);
+
+ /* now we should expect to get the name owner in the callback when constructing the watcher */
+ w = g_bus_name_watcher_new (G_MESSAGE_BUS_TYPE_SESSION,
+ "org.gtk.Test.Name1",
+ NULL,
+ (GAsyncReadyCallback) bnw_cb_expect_owner,
+ NULL);
+ g_main_loop_run (loop);
+ g_assert_cmpstr (g_bus_name_watcher_get_name_owner (w), !=, NULL);
+
+ /* now stop owning the name and check that the watcher emits the ::name-vanished signal */
+ g_signal_connect (w, "name-vanished", G_CALLBACK (bnw_on_name_vanished), NULL);
+ g_object_unref (o);
+ g_main_loop_run (loop);
+ g_assert_cmpstr (g_bus_name_watcher_get_name_owner (w), ==, NULL);
+ g_signal_handlers_disconnect_by_func (w, bnw_on_name_vanished, NULL);
+
+ /* start owning the name and check that the watcher emits the ::name-appeared signal */
+ g_signal_connect (w, "name-appeared", G_CALLBACK (bnw_on_name_appeared), NULL);
+ o = g_bus_name_owner_new (G_MESSAGE_BUS_TYPE_SESSION,
+ "org.gtk.Test.Name1",
+ G_BUS_NAME_OWNER_FLAGS_NONE,
+ NULL,
+ NULL,
+ NULL);
+ g_main_loop_run (loop);
+ g_assert_cmpstr (g_bus_name_watcher_get_name_owner (w), !=, NULL);
+ g_signal_handlers_disconnect_by_func (w, bnw_on_name_appeared, NULL);
+
+ /* now check that we get ::name-vanished when the bus goes away */
+ g_signal_connect (w, "name-vanished", G_CALLBACK (bnw_on_name_vanished), NULL);
+ session_bus_down ();
+ g_main_loop_run (loop);
+ g_assert_cmpstr (g_bus_name_watcher_get_name_owner (w), ==, NULL);
+ g_signal_handlers_disconnect_by_func (w, bnw_on_name_vanished, NULL);
+
+ /* and ::name-appeared when the bus comes back up (this works because
+ * GBusNameOwner will claim the name on reconnection)
+ */
+ g_signal_connect (w, "name-appeared", G_CALLBACK (bnw_on_name_appeared), NULL);
+ session_bus_up ();
+ g_main_loop_run (loop);
+ g_assert_cmpstr (g_bus_name_watcher_get_name_owner (w), !=, NULL);
+ g_signal_handlers_disconnect_by_func (w, bnw_on_name_appeared, NULL);
+
+ /* check that singleton handling works, e.g. a new watcher for the _same_
+ * connection and name gives the same object
+ */
+ val = FALSE;
+ w2 = g_bus_name_watcher_new (G_MESSAGE_BUS_TYPE_SESSION,
+ "org.gtk.Test.Name1",
+ NULL,
+ (GAsyncReadyCallback) bnw_cb_singleton,
+ &val);
+ g_assert (w == w2);
+ g_assert (!val);
+ g_main_loop_run (loop);
+ g_assert (val);
+ g_object_unref (w2);
+ w2 = g_bus_name_watcher_new (G_MESSAGE_BUS_TYPE_SESSION,
+ "org.gtk.Test.Name2",
+ NULL,
+ NULL,
+ NULL);
+ g_assert (w != w2);
+ g_object_unref (w2);
+
+ /* stop watching */
+ g_object_unref (w);
+
+ /* kill owner */
+ g_object_unref (o);
+
+ /* tear down bus */
+ session_bus_down ();
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
int
main (int argc,
@@ -900,5 +1218,6 @@ main (int argc,
g_test_add_func ("/gdbus/message-bus-connections", test_message_bus_connections);
g_test_add_func ("/gdbus/bus-name-owner", test_bus_name_owner);
+ g_test_add_func ("/gdbus/bus-name-watcher", test_bus_name_watcher);
return g_test_run();
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]