2005-01-30 Havoc Pennington <hp@redhat.com>
authorHavoc Pennington <hp@redhat.com>
Mon, 31 Jan 2005 02:55:12 +0000 (02:55 +0000)
committerHavoc Pennington <hp@redhat.com>
Mon, 31 Jan 2005 02:55:12 +0000 (02:55 +0000)
* tools/dbus-names-model.c: dynamically watch NameOwnerChanged

* autogen.sh: change to autotools 1.9

* glib/dbus-gproxy.c: completely change how signals work
(dbus_g_proxy_add_signal): new function to specify signature of a
signal
(dbus_g_proxy_emit_received): marshal the dbus message to GValues,
and g_warning if the incoming message has the wrong signature.

ChangeLog
autogen.sh
configure.in
dbus/dbus-glib.h
doc/TODO
glib/Makefile.am
glib/dbus-gmarshal.c [new file with mode: 0644]
glib/dbus-gmarshal.h [new file with mode: 0644]
glib/dbus-gmarshal.list [new file with mode: 0644]
glib/dbus-gproxy.c
tools/dbus-names-model.c

index fe4fd04..22ab88b 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,17 @@
 2005-01-30  Havoc Pennington  <hp@redhat.com>
 
+       * tools/dbus-names-model.c: dynamically watch NameOwnerChanged
+
+       * autogen.sh: change to autotools 1.9
+
+       * glib/dbus-gproxy.c: completely change how signals work
+       (dbus_g_proxy_add_signal): new function to specify signature of a
+       signal
+       (dbus_g_proxy_emit_received): marshal the dbus message to GValues,
+       and g_warning if the incoming message has the wrong signature.
+
+2005-01-30  Havoc Pennington  <hp@redhat.com>
+
        * tools/dbus-names-model.c (have_names_notify): fix this
 
        * dbus/dbus-message.c (_dbus_message_iter_get_args_valist): clean
index 3e0a585..6435fd5 100755 (executable)
@@ -21,8 +21,8 @@ DIE=0
        DIE=1
 }
 
-AUTOMAKE=automake-1.7
-ACLOCAL=aclocal-1.7
+AUTOMAKE=automake-1.9
+ACLOCAL=aclocal-1.9
 
 ($AUTOMAKE --version) < /dev/null > /dev/null 2>&1 || {
         AUTOMAKE=automake
index 1df7699..87f214b 100644 (file)
@@ -819,6 +819,13 @@ fi
 AM_CONDITIONAL(HAVE_GLIB, test x$have_glib = xyes)
 AM_CONDITIONAL(HAVE_GLIB_THREADS, test x$have_glib_threads = xyes)
 
+if test x$have_glib = xyes; then
+   GLIB_GENMARSHAL=`$PKG_CONFIG --variable=glib_genmarshal glib-2.0`
+else
+   GLIB_GENMARSHAL=glib-not-enabled-so-there-is-no-genmarshal
+fi
+AC_SUBST(GLIB_GENMARSHAL)
+
 dnl GLib flags
 AC_SUBST(DBUS_GLIB_CFLAGS)
 AC_SUBST(DBUS_GLIB_LIBS)
index 6c3c3c5..5c002f4 100644 (file)
@@ -149,6 +149,9 @@ DBusGProxy*       dbus_g_proxy_new_for_name_owner    (DBusGConnection   *connect
 DBusGProxy*       dbus_g_proxy_new_for_peer          (DBusGConnection   *connection,
                                                       const char        *path_name,
                                                       const char        *interface_name);
+void              dbus_g_proxy_add_signal            (DBusGProxy        *proxy,
+                                                      const char        *signal_name,
+                                                      const char        *signature);
 void              dbus_g_proxy_connect_signal        (DBusGProxy        *proxy,
                                                       const char        *signal_name,
                                                       GCallback          handler,
index 0590bb4..b64409a 100644 (file)
--- a/doc/TODO
+++ b/doc/TODO
@@ -55,9 +55,9 @@ Important for 1.0 GLib Bindings
    dbus_malloc() memory, only g_malloc(). 
    dbus_g_proxy_end_call() is the major offender. 
 
- - dbus_gproxy_connect_signal() has to take a signature for the signal 
-   so it can figure out how to invoke the callback, or we have to rely
-   on having introspection data.
+ - DBusGProxy signals feature is a complete fiasco;
+   right now the problem is that it dynamically creates
+   signals on the global DBusGProxy class and never frees them
 
  - DBusGProxy doesn't emit "destroy" when it should
 
index 4db47de..6a42e6f 100644 (file)
@@ -5,6 +5,8 @@ lib_LTLIBRARIES=libdbus-glib-1.la
 libdbus_glib_1_la_SOURCES =                    \
        dbus-glib.c                             \
        dbus-gmain.c                            \
+       dbus-gmarshal.c                         \
+       dbus-gmarshal.h                         \
        dbus-gobject.c                          \
        dbus-gproxy.c                           \
        dbus-gtest.c                            \
@@ -41,6 +43,15 @@ dbus_binding_tool_SOURCES =                  \
 
 dbus_binding_tool_LDADD= -lexpat libdbus-gtool.la
 
+## we just rebuilt these manually and check them into cvs; easier than
+## convincing automake/make to do this properly
+regenerate-built-sources:
+       @GLIB_GENMARSHAL@ --prefix=_dbus_g_marshal dbus-gmarshal.list --header > dbus-gmarshal.h && \
+       echo '#include "dbus-gmarshal.h"' > dbus-gmarshal.c &&                                      \
+        @GLIB_GENMARSHAL@ --prefix=_dbus_g_marshal dbus-gmarshal.list --body >> dbus-gmarshal.c
+
+EXTRA_DIST=dbus-gmarshal.list
+
 if DBUS_BUILD_TESTS
 
 ## we use noinst_PROGRAMS not check_PROGRAMS for TESTS so that we
diff --git a/glib/dbus-gmarshal.c b/glib/dbus-gmarshal.c
new file mode 100644 (file)
index 0000000..13e98b1
--- /dev/null
@@ -0,0 +1,89 @@
+#include "dbus-gmarshal.h"
+
+#include       <glib-object.h>
+
+
+#ifdef G_ENABLE_DEBUG
+#define g_marshal_value_peek_boolean(v)  g_value_get_boolean (v)
+#define g_marshal_value_peek_char(v)     g_value_get_char (v)
+#define g_marshal_value_peek_uchar(v)    g_value_get_uchar (v)
+#define g_marshal_value_peek_int(v)      g_value_get_int (v)
+#define g_marshal_value_peek_uint(v)     g_value_get_uint (v)
+#define g_marshal_value_peek_long(v)     g_value_get_long (v)
+#define g_marshal_value_peek_ulong(v)    g_value_get_ulong (v)
+#define g_marshal_value_peek_int64(v)    g_value_get_int64 (v)
+#define g_marshal_value_peek_uint64(v)   g_value_get_uint64 (v)
+#define g_marshal_value_peek_enum(v)     g_value_get_enum (v)
+#define g_marshal_value_peek_flags(v)    g_value_get_flags (v)
+#define g_marshal_value_peek_float(v)    g_value_get_float (v)
+#define g_marshal_value_peek_double(v)   g_value_get_double (v)
+#define g_marshal_value_peek_string(v)   (char*) g_value_get_string (v)
+#define g_marshal_value_peek_param(v)    g_value_get_param (v)
+#define g_marshal_value_peek_boxed(v)    g_value_get_boxed (v)
+#define g_marshal_value_peek_pointer(v)  g_value_get_pointer (v)
+#define g_marshal_value_peek_object(v)   g_value_get_object (v)
+#else /* !G_ENABLE_DEBUG */
+/* WARNING: This code accesses GValues directly, which is UNSUPPORTED API.
+ *          Do not access GValues directly in your code. Instead, use the
+ *          g_value_get_*() functions
+ */
+#define g_marshal_value_peek_boolean(v)  (v)->data[0].v_int
+#define g_marshal_value_peek_char(v)     (v)->data[0].v_int
+#define g_marshal_value_peek_uchar(v)    (v)->data[0].v_uint
+#define g_marshal_value_peek_int(v)      (v)->data[0].v_int
+#define g_marshal_value_peek_uint(v)     (v)->data[0].v_uint
+#define g_marshal_value_peek_long(v)     (v)->data[0].v_long
+#define g_marshal_value_peek_ulong(v)    (v)->data[0].v_ulong
+#define g_marshal_value_peek_int64(v)    (v)->data[0].v_int64
+#define g_marshal_value_peek_uint64(v)   (v)->data[0].v_uint64
+#define g_marshal_value_peek_enum(v)     (v)->data[0].v_long
+#define g_marshal_value_peek_flags(v)    (v)->data[0].v_ulong
+#define g_marshal_value_peek_float(v)    (v)->data[0].v_float
+#define g_marshal_value_peek_double(v)   (v)->data[0].v_double
+#define g_marshal_value_peek_string(v)   (v)->data[0].v_pointer
+#define g_marshal_value_peek_param(v)    (v)->data[0].v_pointer
+#define g_marshal_value_peek_boxed(v)    (v)->data[0].v_pointer
+#define g_marshal_value_peek_pointer(v)  (v)->data[0].v_pointer
+#define g_marshal_value_peek_object(v)   (v)->data[0].v_pointer
+#endif /* !G_ENABLE_DEBUG */
+
+
+/* NONE:STRING,STRING,STRING (dbus-gmarshal.list:1) */
+void
+_dbus_g_marshal_VOID__STRING_STRING_STRING (GClosure     *closure,
+                                            GValue       *return_value,
+                                            guint         n_param_values,
+                                            const GValue *param_values,
+                                            gpointer      invocation_hint,
+                                            gpointer      marshal_data)
+{
+  typedef void (*GMarshalFunc_VOID__STRING_STRING_STRING) (gpointer     data1,
+                                                           gpointer     arg_1,
+                                                           gpointer     arg_2,
+                                                           gpointer     arg_3,
+                                                           gpointer     data2);
+  register GMarshalFunc_VOID__STRING_STRING_STRING callback;
+  register GCClosure *cc = (GCClosure*) closure;
+  register gpointer data1, data2;
+
+  g_return_if_fail (n_param_values == 4);
+
+  if (G_CCLOSURE_SWAP_DATA (closure))
+    {
+      data1 = closure->data;
+      data2 = g_value_peek_pointer (param_values + 0);
+    }
+  else
+    {
+      data1 = g_value_peek_pointer (param_values + 0);
+      data2 = closure->data;
+    }
+  callback = (GMarshalFunc_VOID__STRING_STRING_STRING) (marshal_data ? marshal_data : cc->callback);
+
+  callback (data1,
+            g_marshal_value_peek_string (param_values + 1),
+            g_marshal_value_peek_string (param_values + 2),
+            g_marshal_value_peek_string (param_values + 3),
+            data2);
+}
+
diff --git a/glib/dbus-gmarshal.h b/glib/dbus-gmarshal.h
new file mode 100644 (file)
index 0000000..2cdb3d7
--- /dev/null
@@ -0,0 +1,21 @@
+
+#ifndef ___dbus_g_marshal_MARSHAL_H__
+#define ___dbus_g_marshal_MARSHAL_H__
+
+#include       <glib-object.h>
+
+G_BEGIN_DECLS
+
+/* NONE:STRING,STRING,STRING (dbus-gmarshal.list:1) */
+extern void _dbus_g_marshal_VOID__STRING_STRING_STRING (GClosure     *closure,
+                                                        GValue       *return_value,
+                                                        guint         n_param_values,
+                                                        const GValue *param_values,
+                                                        gpointer      invocation_hint,
+                                                        gpointer      marshal_data);
+#define _dbus_g_marshal_NONE__STRING_STRING_STRING     _dbus_g_marshal_VOID__STRING_STRING_STRING
+
+G_END_DECLS
+
+#endif /* ___dbus_g_marshal_MARSHAL_H__ */
+
diff --git a/glib/dbus-gmarshal.list b/glib/dbus-gmarshal.list
new file mode 100644 (file)
index 0000000..12974e2
--- /dev/null
@@ -0,0 +1 @@
+NONE:STRING,STRING,STRING
index c38c702..01d67ac 100644 (file)
@@ -23,6 +23,8 @@
 #include <dbus/dbus-glib.h>
 #include <dbus/dbus-glib-lowlevel.h>
 #include "dbus-gutils.h"
+#include "dbus-gmarshal.h"
+#include "dbus-gvalue.h"
 #include <string.h>
 
 /**
@@ -48,6 +50,8 @@ struct DBusGProxy
   char *name;                 /**< Name messages go to or NULL */
   char *path;                 /**< Path messages go to or NULL */
   char *interface;            /**< Interface messages go to or NULL */
+
+  GData *signal_signatures;   /**< D-BUS signatures for each signal */
 };
 
 /**
@@ -58,14 +62,13 @@ struct DBusGProxyClass
   GObjectClass parent_class;  /**< Parent class */
 };
 
-static void dbus_g_proxy_init          (DBusGProxy      *proxy);
-static void dbus_g_proxy_class_init    (DBusGProxyClass *klass);
-static void dbus_g_proxy_finalize      (GObject         *object);
-static void dbus_g_proxy_dispose       (GObject         *object);
-static void dbus_g_proxy_destroy       (DBusGProxy      *proxy);
-static void dbus_g_proxy_emit_received (DBusGProxy      *proxy,
-                                       DBusMessage     *message);
-
+static void dbus_g_proxy_init               (DBusGProxy      *proxy);
+static void dbus_g_proxy_class_init         (DBusGProxyClass *klass);
+static void dbus_g_proxy_finalize           (GObject         *object);
+static void dbus_g_proxy_dispose            (GObject         *object);
+static void dbus_g_proxy_destroy            (DBusGProxy      *proxy);
+static void dbus_g_proxy_emit_remote_signal (DBusGProxy      *proxy,
+                                             DBusMessage     *message);
 
 /**
  * A list of proxies with a given name+path+interface, used to
@@ -641,7 +644,7 @@ dbus_g_proxy_manager_filter (DBusConnection    *connection,
               proxy = DBUS_G_PROXY (tmp->data);
 
               UNLOCK_MANAGER (manager);
-              dbus_g_proxy_emit_received (proxy, message);
+              dbus_g_proxy_emit_remote_signal (proxy, message);
               g_object_unref (G_OBJECT (proxy));
               LOCK_MANAGER (manager);
               
@@ -670,7 +673,6 @@ dbus_g_proxy_manager_filter (DBusConnection    *connection,
 enum
 {
   DESTROY,
-  RECEIVED,
   LAST_SIGNAL
 };
 
@@ -680,7 +682,7 @@ static guint signals[LAST_SIGNAL] = { 0 };
 static void
 dbus_g_proxy_init (DBusGProxy *proxy)
 {
-  /* Nothing */
+  g_datalist_init (&proxy->signal_signatures);
 }
 
 static void
@@ -701,16 +703,6 @@ dbus_g_proxy_class_init (DBusGProxyClass *klass)
                  NULL, NULL,
                   g_cclosure_marshal_VOID__VOID,
                  G_TYPE_NONE, 0);
-  
-  signals[RECEIVED] =
-    g_signal_new ("received",
-                 G_OBJECT_CLASS_TYPE (object_class),
-                 G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
-                  0,
-                 NULL, NULL,
-                  g_cclosure_marshal_VOID__BOXED,
-                 G_TYPE_NONE, 1,
-                  DBUS_TYPE_MESSAGE);
 }
 
 
@@ -721,6 +713,8 @@ dbus_g_proxy_dispose (GObject *object)
 
   proxy = DBUS_G_PROXY (object);
 
+  g_datalist_clear (&proxy->signal_signatures);
+  
   g_signal_emit (object, signals[DESTROY], 0);
   
   G_OBJECT_CLASS (parent_class)->dispose (object);
@@ -755,28 +749,102 @@ dbus_g_proxy_destroy (DBusGProxy *proxy)
   g_object_run_dispose (G_OBJECT (proxy));
 }
 
+/* this is to avoid people using g_signal_connect() directly,
+ * to avoid confusion with local signal names, and because
+ * of the horribly broken current setup (signals are added
+ * globally to all proxies)
+ */
 static char*
-create_signal_detail (const char *interface,
-                      const char *signal)
+create_signal_name (const char *interface,
+                    const char *signal)
 {
   GString *str;
+  char *p;
 
   str = g_string_new (interface);
 
-  g_string_append (str, ".");
-
+  g_string_append (str, "-");
+  
   g_string_append (str, signal);
 
+  /* GLib will silently barf on '.' in signal names */
+  p = str->str;
+  while (*p)
+    {
+      if (*p == '.')
+        *p = '-';
+      ++p;
+    }
+  
   return g_string_free (str, FALSE);
 }
 
 static void
-dbus_g_proxy_emit_received (DBusGProxy  *proxy,
-                           DBusMessage *message)
+emit_remote_internal (DBusGProxy  *proxy,
+                      DBusMessage *message,
+                      guint        signal_id,
+                      gboolean     marshal_args)
+{
+#define MAX_SIGNATURE_ARGS 20
+  GValue values[MAX_SIGNATURE_ARGS];
+  int arg;
+  int i;
+
+  memset (&values[0], 0, sizeof (values));
+  
+  arg = 0;
+      
+  g_value_init (&values[arg], G_TYPE_FROM_INSTANCE (proxy));
+  g_value_set_instance (&values[arg], proxy);
+  ++arg;
+
+  if (marshal_args)
+    {
+      DBusMessageIter iter;
+      int dtype;
+
+      dbus_message_iter_init (message, &iter);
+      
+      while ((dtype = dbus_message_iter_get_arg_type (&iter)) != DBUS_TYPE_INVALID)
+        {
+          if (arg == MAX_SIGNATURE_ARGS)
+            {
+              g_warning ("Don't support more than %d signal args\n", MAX_SIGNATURE_ARGS);
+              goto out;
+            }
+          
+          if (!dbus_gvalue_demarshal (&iter, &values[arg]))
+            {
+              g_warning ("Unable to convert arg type %d to GValue to emit DBusGProxy signal", dtype);
+              goto out;
+            }
+          
+          ++arg;
+          dbus_message_iter_next (&iter);
+        }
+    }
+      
+  g_signal_emitv (&values[0],
+                  signal_id,
+                  0,
+                  NULL);
+
+ out:
+  i = 0;
+  while (i < arg)
+    {
+      g_value_unset (&values[i]);
+      ++i;
+    }
+}
+
+static void
+dbus_g_proxy_emit_remote_signal (DBusGProxy  *proxy,
+                                 DBusMessage *message)
 {
   const char *interface;
   const char *signal;
-  char *detail;
+  char *name;
   GQuark q;
   
   interface = dbus_message_get_interface (message);
@@ -785,21 +853,43 @@ dbus_g_proxy_emit_received (DBusGProxy  *proxy,
   g_assert (interface != NULL);
   g_assert (signal != NULL);
 
-  detail = create_signal_detail (interface, signal);
+  name = create_signal_name (interface, signal);
 
   /* If the quark isn't preexisting, there's no way there
    * are any handlers connected. We don't want to create
    * extra quarks for every possible signal.
    */
-  q = g_quark_try_string (detail);
+  q = g_quark_try_string (name);
 
   if (q != 0)
-    g_signal_emit (G_OBJECT (proxy),
-                   signals[RECEIVED],
-                   q,
-                   message);
+    {
+      const char *signature;
 
-  g_free (detail);
+      signature = g_datalist_id_get_data (&proxy->signal_signatures, q);
+      if (signature == NULL)
+        {
+          g_warning ("Signal '%s' has not been added to this proxy object\n",
+                     name);
+        }
+      else if (!dbus_message_has_signature (message, signature))
+        {
+          g_warning ("Signature '%s' expected for signal '%s', actual signature '%s'\n",
+                     signature,
+                     name,
+                     dbus_message_get_signature (message));
+        }
+      else
+        {
+          guint signal_id;
+
+          signal_id = g_signal_lookup (name, G_OBJECT_TYPE (proxy));
+          g_assert (signal_id != 0); /* because we have the signature */
+          
+          emit_remote_internal (proxy, message, signal_id, signature != NULL);
+        }
+    }
+
+  g_free (name);
 }
 
 /** @} End of DBusGLibInternals */
@@ -1264,8 +1354,8 @@ dbus_g_proxy_call_no_reply (DBusGProxy               *proxy,
  * @param client_serial return location for message's serial, or #NULL */
 void
 dbus_g_proxy_send (DBusGProxy          *proxy,
-                  DBusMessage         *message,
-                  dbus_uint32_t       *client_serial)
+                   DBusMessage         *message,
+                   dbus_uint32_t       *client_serial)
 {
   g_return_if_fail (DBUS_IS_G_PROXY (proxy));
   
@@ -1289,15 +1379,103 @@ dbus_g_proxy_send (DBusGProxy          *proxy,
     g_error ("Out of memory\n");
 }
 
+static gboolean
+siginfo_from_signature (const char         *signature,
+                        GSignalCMarshaller *c_marshaller,
+                        GType              *return_type,
+                        guint              *n_params,
+                        GType             **param_types)
+{
+  /* FIXME (which marshalers should we include?
+   * probably need public API to add your own
+   */
+  
+  if (strcmp (signature, "sss") == 0)
+    {
+      *c_marshaller = _dbus_g_marshal_NONE__STRING_STRING_STRING;
+      *return_type = G_TYPE_NONE;
+      *n_params = 3;
+      *param_types = g_new0 (GType, *n_params);
+      (*param_types)[0] = G_TYPE_STRING;
+      (*param_types)[1] = G_TYPE_STRING;
+      (*param_types)[2] = G_TYPE_STRING;
+
+      return TRUE;
+    }
+
+  return FALSE;
+}
+
+/**
+ * Specifies the signature of a signal, such that it's possible to
+ * connect to the signal on this proxy.
+ *
+ * @param proxy the proxy for a remote interface
+ * @param signal_name the name of the signal
+ * @param signature D-BUS signature of the signal
+ */
+void
+dbus_g_proxy_add_signal  (DBusGProxy        *proxy,
+                          const char        *signal_name,
+                          const char        *signature)
+{
+  GSignalCMarshaller c_marshaller;
+  GType return_type;
+  int n_params;
+  GType *params;
+  
+  g_return_if_fail (DBUS_IS_G_PROXY (proxy));
+  g_return_if_fail (signal_name != NULL);
+  g_return_if_fail (signature != NULL);
+
+  if (siginfo_from_signature (signature,
+                              &c_marshaller,
+                              &return_type,
+                              &n_params,
+                              &params))
+    {
+      GQuark q;
+      char *name;
+
+      name = create_signal_name (proxy->interface, signal_name);
+      
+      q = g_quark_from_string (name);
+      
+      g_return_if_fail (g_datalist_id_get_data (&proxy->signal_signatures, q) == NULL);
+      
+      g_datalist_id_set_data_full (&proxy->signal_signatures,
+                                   q, g_strdup (signature),
+                                   g_free);
+
+      /* hackaround global nature of g_signal_newv()... this whole thing needs unhosing */
+
+      if (g_signal_lookup (name,
+                           G_OBJECT_TYPE (proxy)) == 0)
+        {
+          g_signal_newv (name,
+                         G_OBJECT_TYPE (proxy),
+                         G_SIGNAL_RUN_LAST,
+                         0,
+                         NULL, NULL,
+                         c_marshaller,
+                         return_type, n_params, params);
+        }
+
+      g_free (params);
+      g_free (name);
+    }
+  else
+    {
+      g_warning ("DBusGProxy doesn't know how to create a signal with signature '%s'\n",
+                 signature);
+    }
+}
+
 /**
  * Connect a signal handler to a proxy for a remote interface.  When
  * 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
@@ -1306,27 +1484,40 @@ dbus_g_proxy_send (DBusGProxy          *proxy,
  */
 void
 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;
+  char *name;
+  guint signal_id;
 
   g_return_if_fail (DBUS_IS_G_PROXY (proxy));
   g_return_if_fail (signal_name != NULL);
   g_return_if_fail (handler != NULL);
   
-  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);
+  name = create_signal_name (proxy->interface, signal_name);
 
-  g_free (detail);
+  g_printerr ("Looking up signal '%s'\n", name);
+  signal_id = g_signal_lookup (name,
+                               G_OBJECT_TYPE (proxy));
+  if (signal_id != 0)
+    {
+      GClosure *closure;
+      
+      closure = g_cclosure_new (G_CALLBACK (handler), data, free_data_func);
+      g_signal_connect_closure_by_id (G_OBJECT (proxy),
+                                      signal_id,
+                                      0,
+                                      closure, FALSE);
+    }
+  else
+    {
+      g_warning ("You have to add signal '%s' with dbus_g_proxy_add_signal() before you can connect to it\n",
+                 name);
+    }
+  
+  g_free (name);
 }
 
 /**
@@ -1340,38 +1531,38 @@ dbus_g_proxy_connect_signal (DBusGProxy             *proxy,
  */
 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;
+  char *name;
+  guint signal_id;
   
   g_return_if_fail (DBUS_IS_G_PROXY (proxy));
   g_return_if_fail (signal_name != NULL);
   g_return_if_fail (handler != NULL);
 
-  detail = create_signal_detail (proxy->interface, signal_name);
-  q = g_quark_try_string (detail);
-  g_free (detail);
+  name = create_signal_name (proxy->interface, signal_name);
 
-#ifndef G_DISABLE_CHECKS
-  if (q == 0)
+  signal_id = g_signal_lookup (name, G_OBJECT_TYPE (proxy));
+  if (signal_id != 0)
     {
-      g_warning ("%s: No signal handlers for %s found on this DBusGProxy",
-                 G_GNUC_FUNCTION, signal_name);
-      return;
+      g_signal_handlers_disconnect_matched (G_OBJECT (proxy),
+                                            G_SIGNAL_MATCH_DETAIL |
+                                            G_SIGNAL_MATCH_FUNC   |
+                                            G_SIGNAL_MATCH_DATA,
+                                            signal_id,
+                                            0,
+                                            NULL,
+                                            G_CALLBACK (handler), data);
+    }
+  else
+    {
+      g_warning ("Attempt to disconnect from signal '%s' which is not registered\n",
+                 name);
     }
-#endif
 
-  g_signal_handlers_disconnect_matched (G_OBJECT (proxy),
-                                        G_SIGNAL_MATCH_DETAIL |
-                                        G_SIGNAL_MATCH_FUNC   |
-                                        G_SIGNAL_MATCH_DATA,
-                                        signals[RECEIVED],
-                                        q,
-                                        NULL,
-                                        G_CALLBACK (handler), data);
+  g_free (name);
 }
 
 /** @} End of DBusGLib public */
index 4353daa..38c121b 100644 (file)
@@ -22,6 +22,7 @@
  */
 #include "dbus-names-model.h"
 #include <glib/gi18n.h>
+#include <string.h>
 
 enum
 {
@@ -112,6 +113,81 @@ have_names_notify (DBusGPendingCall *call,
   g_strfreev (names);
 }
 
+static gboolean
+names_model_find_name (NamesModel  *names_model,
+                       const char  *name,
+                       GtkTreeIter *iter_p)
+{
+  GtkTreeIter iter;
+  
+  if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (names_model),
+                                      &iter))
+    return FALSE;
+  
+  do
+    {
+      char *s;
+      
+      gtk_tree_model_get (GTK_TREE_MODEL (names_model),
+                          &iter,
+                          MODEL_COLUMN_NAME, &s,
+                          -1);
+      if (s && strcmp (s, name) == 0)
+        {
+          *iter_p = iter;
+          g_free (s);
+          return TRUE;
+        }
+      
+      g_free (s);
+    }
+  while (gtk_tree_model_iter_next (GTK_TREE_MODEL (names_model),
+                                   &iter));
+
+  return FALSE;
+}
+
+static void
+name_owner_changed (DBusGProxy *driver_proxy,
+                    const char *name,
+                    const char *old_owner,
+                    const char *new_owner,
+                    void       *data)
+{
+  NamesModel *names_model = NAMES_MODEL (data);
+
+#if 0
+  g_printerr ("Name '%s' changed owner '%s' -> '%s'\n",
+              name, old_owner, new_owner);
+#endif
+
+  if (*new_owner == '\0')
+    {
+      /* this name has vanished */
+      GtkTreeIter iter;
+
+      if (names_model_find_name (names_model, name, &iter))
+        gtk_tree_store_remove (GTK_TREE_STORE (names_model),
+                               &iter);
+    }
+  else if (*old_owner == '\0')
+    {
+      /* this name has been added */
+      GtkTreeIter iter;
+      
+      if (!names_model_find_name (names_model, name, &iter))
+        {
+          gtk_tree_store_append (GTK_TREE_STORE (names_model),
+                                 &iter, NULL);
+          
+          gtk_tree_store_set (GTK_TREE_STORE (names_model),
+                              &iter,
+                              MODEL_COLUMN_NAME, name,
+                              -1);
+        }
+    }
+}
+
 static void
 names_model_reload (NamesModel *names_model)
 {
@@ -144,8 +220,6 @@ static void
 names_model_set_connection (NamesModel      *names_model,
                             DBusGConnection *connection)
 {
-  const char *match_rule = "type='signal',member='NameOwnerChanged'";
-
   g_return_if_fail (IS_NAMES_MODEL (names_model));
   
   if (connection == names_model->connection)
@@ -153,10 +227,11 @@ names_model_set_connection (NamesModel      *names_model,
 
   if (names_model->connection)
     {
-      dbus_g_proxy_call_no_reply (names_model->driver_proxy,
-                                  "RemoveMatch", 
-                                  DBUS_TYPE_STRING, &match_rule,
-                                  DBUS_TYPE_INVALID);
+      dbus_g_proxy_disconnect_signal (names_model->driver_proxy,
+                                      "NameOwnerChanged",
+                                      G_CALLBACK (name_owner_changed),
+                                      names_model);
+      
       g_object_unref (names_model->driver_proxy);
       names_model->driver_proxy = NULL;
       dbus_g_connection_unref (names_model->connection);
@@ -174,6 +249,18 @@ names_model_set_connection (NamesModel      *names_model,
                                    DBUS_PATH_ORG_FREEDESKTOP_DBUS,
                                    DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS);
       g_assert (names_model->driver_proxy);
+
+      dbus_g_proxy_add_signal (names_model->driver_proxy,
+                               "NameOwnerChanged",
+                               DBUS_TYPE_STRING_AS_STRING
+                               DBUS_TYPE_STRING_AS_STRING
+                               DBUS_TYPE_STRING_AS_STRING);
+      
+      dbus_g_proxy_connect_signal (names_model->driver_proxy,
+                                   "NameOwnerChanged", 
+                                   G_CALLBACK (name_owner_changed),
+                                   names_model,
+                                   NULL);
     }
 
   names_model_reload (names_model);