Signals and bindings

Olivier Andrieu oliv__a at users.sourceforge.net
Fri Oct 1 18:30:21 UTC 2004


 > Havoc Pennington [Fri, 24 Sep 2004]:
 > On Fri, 2004-09-24 at 12:26 +0200, Olivier Andrieu wrote:
 > > So, _new_for_service_owner needs a round-trip to be able to send
 > > method calls and _new_for_service needs a round-trip to be able to
 > > receive signals. For the latter, it could be possible to send the
 > > GetServiceOwner request asynchronously and avoid the round-trip but
 > > that means no signals would not be delivered until the reply arrives.
 > > 
 > 
 > Hmm, didn't think about that. It may be OK to simply do the
 > GetServiceOwner async. There isn't a real semantic guarantee about
 > whether you get a given signal emission or not anyhow, since the bus
 > itself is async.
 > 
 > Normally to track something you have to do:
 >  state = GetCurrentState()
 >  connect ("StateChanged")
 > 
 > The GetServiceOwner should be guaranteed to get a reply before
 > GetCurrentState does, so semantically it would work out most of the time
 > if GetServiceOwner were async. GetCurrentState() can be async or not
 > depending on what the app does.
 > 
 > Basically the GetServiceOwner is guaranteed to complete before any other
 > requests you make, so in most cases it would work as expected... ?

Cool. That sounds reasonable.

Here's an implementation of the service tracking. There are no changes
in the API, it's just that now signal emission by proxies work.

About the API for connecting a signal, the idea was to do something
like this I believe:

  dbus_g_proxy_connect_signal (proxy, "isd", callback);
with callback of type
  void (* callback) (GProxy *, gint, gchar *, gdouble, gpointer);

But that's not feasible as far as I can tell (?); it would need a
marshaller, like one generated by glib-genmarshal. 

Alternatively, one could use this prototype :
  void (*callback) (GProxy *, GValueArray *, gpointer);
but I'm not sure it's any better than the current
  void (*callback) (GProxy *, DBusMessage *, gpointer);

-- 
   Olivier
-------------- next part --------------
Index: glib/dbus-gproxy.c
===================================================================
RCS file: /cvs/dbus/dbus/glib/dbus-gproxy.c,v
retrieving revision 1.12
diff -u -r1.12 dbus-gproxy.c
--- glib/dbus-gproxy.c	10 Aug 2004 03:07:01 -0000	1.12
+++ glib/dbus-gproxy.c	1 Oct 2004 15:15:50 -0000
@@ -25,6 +25,12 @@
 #include "dbus-gutils.h"
 #include <string.h>
 
+#if 0
+#define g_log_track(...) g_log ("libdbus-glib", G_LOG_LEVEL_DEBUG, __VA_ARGS__)
+#else
+#define g_log_track(...)
+#endif
+
 /**
  * @addtogroup DBusGLibInternals
  *
@@ -82,6 +88,260 @@
   
 } DBusGProxyList;
 
+
+typedef struct
+{
+  int refcount;
+  char *name;              /* key */
+  char *base_service;      /* owned by the other hash table */
+  DBusPendingCall *get_service_pending;
+} DBusGServiceTrackerData;
+
+typedef struct
+{
+  char *base_service;      /* key */
+  GSList *services;        /* data is owned by the other hash table */
+} DBusGServiceTrackerDataRev;
+
+static void
+dbus_g_service_tracker_data_rev_free (gpointer data)
+{
+  DBusGServiceTrackerDataRev *d = data;
+  g_slist_free (d->services);
+  g_free (d);
+}
+
+typedef struct
+{
+  DBusGProxyManager *manager;
+  GHashTable *service_to_base;         /**< (char * -> DBusGServiceTrackerData) mapping */
+  GHashTable *base_to_service; /**< (char * -> DBusGServiceTrackerDataRev) mapping */
+} DBusGServiceTracker;
+
+static void dbus_g_service_tracker_bind (DBusGServiceTracker *,
+                                         DBusGServiceTrackerData *,
+                                         const char *base);
+static void dbus_g_service_tracker_unbind (DBusGServiceTracker *,
+                                           DBusGServiceTrackerData *);
+static DBusPendingCall *
+bus_get_service_owner_async (DBusGServiceTracker *tracker, DBusGServiceTrackerData *d);
+
+
+static DBusGServiceTracker *
+dbus_g_service_tracker_new (DBusGProxyManager *manager)
+{
+  DBusGServiceTracker *t = g_new0 (DBusGServiceTracker, 1);
+  t->manager = manager;
+  t->service_to_base = g_hash_table_new_full (g_str_hash, g_str_equal,
+					g_free, g_free);
+  t->base_to_service = g_hash_table_new_full (g_str_hash, g_str_equal,
+						g_free,
+						dbus_g_service_tracker_data_rev_free);
+  return t;
+}
+
+static void 
+dbus_g_service_tracker_free (DBusGServiceTracker *t)
+{
+  g_hash_table_destroy (t->base_to_service);
+  g_hash_table_destroy (t->service_to_base);
+  g_free (t);
+}
+
+static gboolean
+dbus_g_service_tracker_is_tracked (DBusGServiceTracker *t,
+				  const char *service,
+                                  DBusGServiceTrackerData **ret)
+{
+  DBusGServiceTrackerData *d;
+  d = g_hash_table_lookup (t->service_to_base, service);
+  if (ret != NULL)
+    *ret = d;
+  return  d != NULL;
+}
+
+static void
+dbus_g_service_tracker_track (DBusGServiceTracker *t,
+			     const char *service)
+{
+  DBusGServiceTrackerData *d;
+
+  d = g_hash_table_lookup (t->service_to_base, service);
+  g_log_track ("tracking %s (%d)", service, d ? d->refcount + 1 : 1);
+  if (d == NULL)
+    {
+      d = g_new0 (DBusGServiceTrackerData, 1);
+      d->name = g_strdup (service);
+      g_hash_table_insert (t->service_to_base, d->name, d);
+      g_log_track ("requesting service owner for %s (async)", service);
+      d->get_service_pending = bus_get_service_owner_async (t, d);
+    }
+  d->refcount++;
+}
+
+static void
+dbus_g_service_tracker_untrack (DBusGServiceTracker *t,
+			       const char *service)
+{
+  DBusGServiceTrackerData *d;
+
+  d = g_hash_table_lookup (t->service_to_base, service);
+  g_log_track ("untracking %s (%d)", service, d ? d->refcount : 0);
+  if (d == NULL)
+    return;
+
+  d->refcount--;
+  if (d->refcount == 0)
+    {
+      if (d->get_service_pending != NULL)
+        dbus_pending_call_cancel (d->get_service_pending);
+      if (d->base_service != NULL)
+	dbus_g_service_tracker_unbind (t, d);
+      g_hash_table_remove (t->service_to_base, d->name);
+    }
+}
+
+static void 
+dbus_g_service_tracker_bind (DBusGServiceTracker *t,
+                            DBusGServiceTrackerData *d,
+			    const char *base)
+{
+  DBusGServiceTrackerDataRev *r;
+
+  g_return_if_fail (d != NULL);
+  g_return_if_fail (base != NULL);
+
+  g_log_track ("tracker mapping: %s -> %s", d->name, base);
+
+  if (d->base_service != NULL)
+    {
+      if (strcmp (d->base_service, base) == 0)
+	return;
+      else
+	dbus_g_service_tracker_unbind (t, d);
+    }
+
+  r = g_hash_table_lookup (t->base_to_service, base);
+  if (r == NULL)
+    {
+      r = g_new0 (DBusGServiceTrackerDataRev, 1);
+      r->base_service = g_strdup (base);
+      g_hash_table_insert (t->base_to_service, r->base_service, r);
+    }
+
+  g_assert (g_slist_find (r->services, d->name) == NULL);
+  r->services = g_slist_prepend (r->services, d->name);
+  d->base_service = r->base_service;
+}
+
+static void
+dbus_g_service_tracker_unbind (DBusGServiceTracker *t,
+                              DBusGServiceTrackerData *d)
+{
+  DBusGServiceTrackerDataRev *r;
+
+  g_log_track ("tracker remove mapping: %s -> %s", d->name, d->base_service);
+
+  if (d == NULL || d->base_service == NULL)
+    return;
+
+  r = g_hash_table_lookup (t->base_to_service, d->base_service);
+  if (r == NULL)
+    return;
+
+  d->base_service = NULL;
+
+  r->services = g_slist_remove (r->services, d->name);
+  if (r->services == NULL)
+    {
+      g_hash_table_remove (t->base_to_service, r->base_service);
+    }
+}
+
+static const char *
+dbus_g_service_tracker_lookup (DBusGServiceTracker *t,
+			      const char *service)
+{
+  DBusGServiceTrackerData *d;
+  d = g_hash_table_lookup (t->service_to_base, service);
+  return d ? d->base_service : NULL;
+}
+
+static GSList *
+dbus_g_service_tracker_expand (DBusGServiceTracker *t,
+			      const char *base)
+{
+  DBusGServiceTrackerDataRev *r;
+  r = g_hash_table_lookup (t->base_to_service, base);
+  return r ? r->services : NULL;
+}
+
+static void 
+dbus_g_service_tracker_handle_service_info (DBusGServiceTracker *tracker,
+					   DBusMessage *message)
+{
+  DBusError error;
+  char *service, *old_owner, *new_owner;
+  DBusGServiceTrackerData *d;
+
+  g_return_if_fail (tracker != NULL && message != NULL);
+  g_assert (dbus_message_is_signal (message, 
+				    DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS,
+				    "ServiceOwnerChanged"));
+
+  service = NULL;
+  old_owner = NULL;
+  new_owner = NULL;
+
+  dbus_error_init (&error);
+  if (! dbus_message_get_args (message, &error,
+                               DBUS_TYPE_STRING, &service,
+                               DBUS_TYPE_STRING, &old_owner,
+                               DBUS_TYPE_STRING, &new_owner,
+                               DBUS_TYPE_INVALID))
+    {
+      g_warning ("error while processing ServiceOwnerChanged message: '%s' (%s)",
+		 error.name, error.message);
+      dbus_error_free (&error);
+      dbus_free (service);
+      dbus_free (old_owner);
+      dbus_free (new_owner);
+      return;
+    }
+
+  if (dbus_g_service_tracker_is_tracked (tracker, service, &d))
+    {
+      g_log_track ("tracker service info '%s' [%s -> %s] (%s)", 
+                   service, old_owner, new_owner, "tracked");
+
+      if (d->get_service_pending != NULL)
+        {
+          dbus_pending_call_cancel (d->get_service_pending);
+          d->get_service_pending = NULL;
+        }
+
+      if (*old_owner != 0)
+	{
+#ifndef G_DISABLE_CHECKS
+	  if (d->base_service && strcmp (d->base_service, old_owner) != 0)
+	    g_warning ("inconsistencies in service tracking, %s was owned by %s, thought it was %s",
+		       service, old_owner, d->base_service);
+#endif
+	  dbus_g_service_tracker_unbind (tracker, d);
+	}
+      if (*new_owner != 0)
+	dbus_g_service_tracker_bind (tracker, d, new_owner);
+    }
+  else
+    g_log_track ("tracker service info '%s' [%s -> %s] (%s)", 
+                 service, old_owner, new_owner, "not tracked");
+
+
+  dbus_free (service);
+  dbus_free (old_owner);
+  dbus_free (new_owner);
+}
+
 /**
  * DBusGProxyManager's primary task is to route signals to the proxies
  * those signals are emitted on. In order to do this it also has to
@@ -97,6 +357,7 @@
                             *   and iterate over proxies
                             */
 
+  DBusGServiceTracker *service_tracker;
 };
 
 static DBusGProxyManager *dbus_g_proxy_manager_ref    (DBusGProxyManager *manager);
@@ -139,6 +400,8 @@
   manager->refcount = 1;
   manager->connection = connection;
   
+  manager->service_tracker = dbus_g_service_tracker_new (manager);
+
   g_static_mutex_init (&manager->lock);
 
   /* Proxy managers keep the connection alive, which means that
@@ -153,6 +416,14 @@
   dbus_connection_add_filter (connection, dbus_g_proxy_manager_filter,
                               manager, NULL);
   
+  /* Subscribe to ServiceOwnerChanged signals, for the service tracker.
+   * Should be in dbus_g_service_tracker_new() probably.
+   */
+  dbus_bus_add_match (connection, 
+                      "type=signal,member=ServiceOwnerChanged,interface="
+                      DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS, NULL);
+
+
   g_static_mutex_unlock (&connection_g_proxy_lock);
   
   return manager;
@@ -196,6 +467,8 @@
           manager->proxy_lists = NULL;
         }
       
+      dbus_g_service_tracker_free (manager->service_tracker);
+
       g_static_mutex_free (&manager->lock);
 
       g_static_mutex_lock (&connection_g_proxy_lock);
@@ -349,10 +622,10 @@
 }
 
 static char*
-tristring_from_message (DBusMessage *message)
+tristring_from_message (DBusMessage *message, const char *sender)
 {
   return tristring_alloc_from_strings (0,
-                                       dbus_message_get_sender (message),
+                                       sender,
                                        dbus_message_get_path (message),
                                        dbus_message_get_interface (message));
 }
@@ -395,14 +668,168 @@
                             proxy->path, proxy->interface);
 }
 
+struct GetServiceData {
+  DBusGServiceTracker *tracker;
+  DBusGServiceTrackerData *d;
+};
+
+static void
+on_get_service_owner (DBusPendingCall *pending, void *user_data)
+{
+  DBusError error;
+  struct GetServiceData *data = user_data;
+  DBusMessage *reply;
+  char *base_service;
+
+  g_log_track ("tracker: reply for service owner query for %s",
+               data->d->name);
+
+  LOCK_MANAGER (data->tracker->manager);
+
+  reply = dbus_pending_call_get_reply (pending);
+  g_assert (reply != NULL);
+  
+  dbus_error_init (&error);
+  if (dbus_message_get_args (reply, &error, 
+                             DBUS_TYPE_STRING, &base_service, 
+                             DBUS_TYPE_INVALID))
+    {
+      dbus_g_service_tracker_bind (data->tracker, data->d, base_service);
+      dbus_free (base_service);
+    }
+  else if (dbus_error_has_name (&error, DBUS_ERROR_NO_MEMORY))
+    g_error ("Out of memory");
+  else
+    {
+      g_warning ("Error '%s' while processing GetServiceOwner reply: %s\n",
+                 error.name, error.message);
+      dbus_error_free (&error);
+    }
+  data->d->get_service_pending = NULL;
+
+  UNLOCK_MANAGER (data->tracker->manager);
+}
+
+static DBusPendingCall *
+bus_get_service_owner_async (DBusGServiceTracker *tracker, DBusGServiceTrackerData *d)
+{
+  DBusMessage *request;
+  DBusPendingCall *pending;
+  struct GetServiceData *data;
+
+  g_return_val_if_fail (d != NULL, NULL);
+  g_return_val_if_fail (d->name != NULL, NULL);
+
+  request = dbus_message_new_method_call (DBUS_SERVICE_ORG_FREEDESKTOP_DBUS,
+					  DBUS_PATH_ORG_FREEDESKTOP_DBUS,
+					  DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS,
+					  "GetServiceOwner");
+  if (request == NULL)
+    goto oom;
+  
+  if (! dbus_message_append_args (request, 
+				  DBUS_TYPE_STRING, d->name, 
+				  DBUS_TYPE_INVALID))
+    goto oom;
+
+  if (! dbus_connection_send_with_reply (tracker->manager->connection, request, &pending, -1))
+    goto oom;
+
+  data = g_new (struct GetServiceData, 1);
+  data->tracker = tracker;
+  data->d = d;
+  if (! dbus_pending_call_set_notify (pending, on_get_service_owner, data, g_free))
+    goto oom;
+
+  dbus_message_unref (request);
+  return pending;
+
+ oom:
+    g_error ("Out of memory");
+    return NULL;
+}
+
+static gboolean
+bus_get_service_owner (DBusConnection *connection,
+		       const char     *service,
+		       char          **base_service,
+		       GError        **error)
+{
+  DBusMessage *request, *reply;
+  DBusError derror;
+  char *base_service_name;
+  gboolean retval;
+
+  g_return_val_if_fail (service != NULL, FALSE);
+  g_return_val_if_fail (base_service != NULL, FALSE);
+
+  dbus_error_init (&derror);
+
+  reply = NULL;
+  base_service_name = NULL;
+  retval = FALSE;
+
+  g_log_track ("tracker: GetServiceOwner query for %s", service);
+
+  request = dbus_message_new_method_call (DBUS_SERVICE_ORG_FREEDESKTOP_DBUS,
+					  DBUS_PATH_ORG_FREEDESKTOP_DBUS,
+					  DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS,
+					  "GetServiceOwner");
+  if (request == NULL)
+    g_error ("Out of memory");
+  
+  if (! dbus_message_append_args (request, 
+				  DBUS_TYPE_STRING, service, 
+				  DBUS_TYPE_INVALID))
+    g_error ("Out of memory");
+
+  reply = dbus_connection_send_with_reply_and_block (connection, request,
+						     -1, &derror);
+  if (reply == NULL)
+    goto error;
+  
+  if (! dbus_message_get_args (reply, &derror, 
+			       DBUS_TYPE_STRING, &base_service_name, 
+			       DBUS_TYPE_INVALID))
+    goto error;
+
+  retval = TRUE;
+  goto out;
+
+ error:
+  g_assert (dbus_error_is_set (&derror));
+  dbus_set_g_error (error, &derror);
+  dbus_error_free (&derror);
+  dbus_free (base_service_name);
+  base_service_name = NULL;
+
+ out:
+  if (request)
+    dbus_message_unref (request);
+  if (reply)
+    dbus_message_unref (reply);
+
+  *base_service = base_service_name;
+  return retval;
+}
+
 static void
 dbus_g_proxy_manager_register (DBusGProxyManager *manager,
-                              DBusGProxy        *proxy)
+                               DBusGProxy        *proxy)
 {
   DBusGProxyList *list;
 
   LOCK_MANAGER (manager);
 
+  if (proxy->service && *proxy->service != ':' && 
+      strcmp (proxy->service, DBUS_SERVICE_ORG_FREEDESKTOP_DBUS) != 0)
+    {
+      /* we'll need service tracking */
+      dbus_g_service_tracker_track (manager->service_tracker,
+                                   proxy->service);
+
+    }
+
   if (manager->proxy_lists == NULL)
     {
       list = NULL;
@@ -430,12 +857,8 @@
                             list->name, list);
     }
 
-  if (list->proxies == NULL)
+  if (list->proxies == NULL && proxy->service != NULL)
     {
-      /* We have to add the match rule to the server,
-       * but FIXME only if the server is a message bus,
-       * not if it's a peer.
-       */
       char *rule;
 
       rule = g_proxy_get_match_rule (proxy);
@@ -458,13 +881,17 @@
 
 static void
 dbus_g_proxy_manager_unregister (DBusGProxyManager *manager,
-                                DBusGProxy        *proxy)
+                                 DBusGProxy        *proxy)
 {
   DBusGProxyList *list;
   char *tri;
   
   LOCK_MANAGER (manager);
 
+  if (proxy->service && *proxy->service != ':' &&
+      strcmp (proxy->service, DBUS_SERVICE_ORG_FREEDESKTOP_DBUS) != 0)
+    dbus_g_service_tracker_untrack (manager->service_tracker, proxy->service);
+
 #ifndef G_DISABLE_CHECKS
   if (manager->proxy_lists == NULL)
     {
@@ -567,6 +994,14 @@
   LOCK_MANAGER (manager);
   
   if (dbus_message_is_signal (message,
+                              DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS,
+                              "ServiceOwnerChanged"))
+    {
+      dbus_g_service_tracker_handle_service_info (manager->service_tracker,
+                                                 message);
+    }
+
+  if (dbus_message_is_signal (message,
                               DBUS_INTERFACE_ORG_FREEDESKTOP_LOCAL,
                               "Disconnected"))
     {
@@ -602,53 +1037,73 @@
     }
   else
     {
-      char *tri;
+      char *tri, *base_service;
+      GSList *services_list;
       DBusGProxyList *list;
       
-      tri = tristring_from_message (message);
-
-      if (manager->proxy_lists)
-        list = g_hash_table_lookup (manager->proxy_lists, tri);
-      else
-        list = NULL;
+      services_list = dbus_g_service_tracker_expand (manager->service_tracker, 
+						    dbus_message_get_sender (message));
+      
+      /* we have to process the base service also,
+	 in case some proxy was registered with _for_service_owner () */
+      base_service = g_strdup (dbus_message_get_sender (message));
+      services_list = g_slist_prepend (services_list, base_service);
+
+      while (services_list != NULL)
+	{
+
+	  tri = tristring_from_message (message, services_list->data);
+
+	  if (manager->proxy_lists)
+	    list = g_hash_table_lookup (manager->proxy_lists, tri);
+	  else
+	    list = NULL;
 
 #if 0
-      g_print ("proxy got %s,%s,%s = list %p\n",
-               tri,
-               tri + strlen (tri) + 1,
-               tri + strlen (tri) + 1 + strlen (tri + strlen (tri) + 1) + 1,
-               list);
+	  g_print ("proxy got %s,%s,%s = list %p\n",
+		   tri,
+		   tri + strlen (tri) + 1,
+		   tri + strlen (tri) + 1 + strlen (tri + strlen (tri) + 1) + 1,
+		   list);
 #endif
       
-      g_free (tri);
+          g_free (tri);
 
-      /* Emit the signal */
+          /* Emit the signal */
       
-      if (list != NULL)
-        {
-          GSList *tmp;
-          GSList *copy;
-
-          copy = g_slist_copy (list->proxies);
-          g_slist_foreach (copy, (GFunc) g_object_ref, NULL);
-          
-          tmp = copy;
-          while (tmp != NULL)
+          if (list != NULL)
             {
-              DBusGProxy *proxy;
+              GSList *tmp;
+              GSList *copy;
 
-              proxy = DBUS_G_PROXY (tmp->data);
+              copy = g_slist_copy (list->proxies);
+              g_slist_foreach (copy, (GFunc) g_object_ref, NULL);
+          
+              tmp = copy;
+              while (tmp != NULL)
+                {
+                  DBusGProxy *proxy;
+
+                  proxy = DBUS_G_PROXY (tmp->data);
+
+                  UNLOCK_MANAGER (manager);
+                  dbus_g_proxy_emit_received (proxy, message);
+                  g_object_unref (G_OBJECT (proxy));
+                  LOCK_MANAGER (manager);
+                  
+                  tmp = tmp->next;
+                }
 
-              UNLOCK_MANAGER (manager);
-              dbus_g_proxy_emit_received (proxy, message);
-              g_object_unref (G_OBJECT (proxy));
-              LOCK_MANAGER (manager);
-              
-              tmp = tmp->next;
+              g_slist_free (copy);
             }
 
-          g_slist_free (copy);
+          services_list = services_list->next;
         }
+
+      /* just free the element we allocated to store
+	 the base service. The rest is owned by the tracker. */
+      g_free (base_service);
+      g_slist_free_1 (services_list);
     }
 
   UNLOCK_MANAGER (manager);
@@ -771,7 +1226,7 @@
 
 static void
 dbus_g_proxy_emit_received (DBusGProxy  *proxy,
-                           DBusMessage *message)
+                            DBusMessage *message)
 {
   const char *interface;
   const char *signal;
@@ -796,7 +1251,7 @@
     g_signal_emit (G_OBJECT (proxy),
                    signals[RECEIVED],
                    q,
-                   message);
+                   DBUS_G_MESSAGE_FROM_MESSAGE (message));
 
   g_free (detail);
 }
@@ -842,9 +1297,9 @@
 
 static DBusGProxy*
 dbus_g_proxy_new (DBusGConnection *connection,
-                 const char      *service_name,
-                 const char      *path_name,
-                 const char      *interface_name)
+                  const char      *service_name,
+                  const char      *path_name,
+                  const char      *interface_name)
 {
   DBusGProxy *proxy;
 
@@ -891,9 +1346,9 @@
  */
 DBusGProxy*
 dbus_g_proxy_new_for_service (DBusGConnection *connection,
-                             const char      *service_name,
-                             const char      *path_name,
-                             const char      *interface_name)
+                              const char      *service_name,
+                              const char      *path_name,
+                              const char      *interface_name)
 {
   DBusGProxy *proxy;
 
@@ -923,6 +1378,8 @@
  * will fail if the service has no owner. If the service has an owner,
  * dbus_g_proxy_new_for_service_owner() will bind to the unique name
  * of that owner rather than the generic service name.
+ *
+ * @todo emit "destroy" signal when the remote service disappears ?
  * 
  * @param connection the connection to the remote bus
  * @param service_name name of the service on the message bus
@@ -933,72 +1390,46 @@
  */
 DBusGProxy*
 dbus_g_proxy_new_for_service_owner (DBusGConnection          *connection,
-                                   const char               *service_name,
-                                   const char               *path_name,
-                                   const char               *interface_name,
-                                   GError                  **error)
+                                    const char               *service_name,
+                                    const char               *path_name,
+                                    const char               *interface_name,
+                                    GError                  **error)
 {
+  DBusGProxyManager *manager;
   DBusGProxy *proxy;
-  DBusMessage *request, *reply;
-  DBusError derror;
-  char *base_service_name;
+  const char *base_service_name;
 
   g_return_val_if_fail (connection != NULL, NULL);
   g_return_val_if_fail (service_name != NULL, NULL);
   g_return_val_if_fail (path_name != NULL, NULL);
   g_return_val_if_fail (interface_name != NULL, NULL);
 
-  dbus_error_init (&derror);
-
   proxy = NULL;
-  base_service_name = NULL;
-  reply = NULL;
-
-  request = dbus_message_new_method_call (DBUS_SERVICE_ORG_FREEDESKTOP_DBUS,
-					  DBUS_PATH_ORG_FREEDESKTOP_DBUS,
-					  DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS,
-					  "GetServiceOwner");
-  if (request == NULL)
-    g_error ("Out of memory");
-  
-  if (! dbus_message_append_args (request, 
-				  DBUS_TYPE_STRING, service_name, 
-				  DBUS_TYPE_INVALID))
-    g_error ("Out of memory");
 
-  reply =
-    dbus_connection_send_with_reply_and_block (DBUS_CONNECTION_FROM_G_CONNECTION (connection),
-                                               request,
-                                               2000, &derror);
-  if (reply == NULL)
-    goto error;
-
-  if (dbus_set_error_from_message (&derror, reply))
-    goto error;
+  manager = dbus_g_proxy_manager_get (DBUS_CONNECTION_FROM_G_CONNECTION (connection));
+  base_service_name = dbus_g_service_tracker_lookup (manager->service_tracker,
+                                                     service_name);
+
+  if (base_service_name == NULL)
+    {
+      char *base_service;
+      if (! bus_get_service_owner (manager->connection, service_name,
+				   &base_service,
+				   error))
+	goto error;
+      proxy = dbus_g_proxy_new (connection, base_service,
+                                path_name, interface_name);
 
-  if (! dbus_message_get_args (reply, &derror, 
-			       DBUS_TYPE_STRING, &base_service_name, 
-			       DBUS_TYPE_INVALID))
-    goto error;
-      
-
-  proxy = dbus_g_proxy_new (connection, base_service_name,
-                           path_name, interface_name);
-
-  goto out;
+      dbus_free (base_service);
+    }
+  else
+    {
+      proxy = dbus_g_proxy_new (connection, base_service_name,
+                                path_name, interface_name);
+    }
 
  error:
-  g_assert (dbus_error_is_set (&derror));
-  dbus_set_g_error (error, &derror);
-  dbus_error_free (&derror);
-
- out:
-  if (request)
-    dbus_message_unref (request);
-  if (reply)
-    dbus_message_unref (reply);
-  dbus_free (base_service_name);
-
+  dbus_g_proxy_manager_unref (manager);
   return proxy;
 }
 
@@ -1028,7 +1459,7 @@
   g_return_val_if_fail (interface_name != NULL, NULL);
 
   proxy = dbus_g_proxy_new (connection, NULL,
-                           path_name, interface_name);
+                            path_name, interface_name);
 
   return proxy;
 }
@@ -1083,6 +1514,8 @@
                                         -1))
     goto oom;
 
+  dbus_message_unref (message);
+
   return DBUS_G_PENDING_CALL_FROM_PENDING_CALL (pending);
 
  oom:
@@ -1212,6 +1645,8 @@
                              NULL))
     goto oom;
 
+  dbus_message_unref (message);
+
   return;
   
  oom:
@@ -1268,39 +1703,37 @@
  * the remote interface emits the specified signal, the proxy will
  * emit a corresponding GLib signal.
  *
- * @todo Right now there's no way to specify the signature to use
- * for invoking the GCallback. Need to either rely on introspection,
- * or require signature here.
- *
  * @param proxy a proxy for a remote interface
  * @param signal_name the DBus signal name to listen for
  * @param handler the handler to connect
  * @param data data to pass to handler
  * @param free_data_func callback function to destroy data
  */
-void
+gulong
 dbus_g_proxy_connect_signal (DBusGProxy             *proxy,
-                            const char             *signal_name,
-                            GCallback               handler,
-                            void                   *data,
-                            GClosureNotify          free_data_func)
+                             const char             *signal_name,
+                             GCallback               handler,
+                             void                   *data,
+                             GClosureNotify          free_data_func)
 {
   GClosure *closure;
   char *detail;
+  gulong handler_id;
 
-  g_return_if_fail (DBUS_IS_G_PROXY (proxy));
-  g_return_if_fail (signal_name != NULL);
-  g_return_if_fail (handler != NULL);
+  g_return_val_if_fail (DBUS_IS_G_PROXY (proxy) ,0);
+  g_return_val_if_fail (signal_name != NULL ,0);
+  g_return_val_if_fail (handler != NULL, 0);
   
   detail = create_signal_detail (proxy->interface, signal_name);
   
   closure = g_cclosure_new (G_CALLBACK (handler), data, free_data_func);
-  g_signal_connect_closure_by_id (G_OBJECT (proxy),
-                                  signals[RECEIVED],
-                                  g_quark_from_string (detail),
-                                  closure, FALSE);
+  handler_id = g_signal_connect_closure_by_id (G_OBJECT (proxy),
+                                               signals[RECEIVED],
+                                               g_quark_from_string (detail),
+                                               closure, FALSE);
 
   g_free (detail);
+  return handler_id;
 }
 
 /**
@@ -1314,9 +1747,9 @@
  */
 void
 dbus_g_proxy_disconnect_signal (DBusGProxy             *proxy,
-                               const char             *signal_name,
-                               GCallback               handler,
-                               void                   *data)
+                                const char             *signal_name,
+                                GCallback               handler,
+                                void                   *data)
 {
   char *detail;
   GQuark q;


More information about the dbus mailing list