Add the droute and dbind libraries as static libraries within this repository.
authorMark Doffman <mark.doffman@codethink.co.uk>
Sat, 7 Nov 2009 17:49:15 +0000 (17:49 +0000)
committerMark Doffman <mark.doffman@codethink.co.uk>
Sat, 7 Nov 2009 17:49:15 +0000 (17:49 +0000)
Previously these were shared libraries in at-spi2-core.

23 files changed:
Makefile.am
README
atk-adaptor/Makefile.am
atk-adaptor/accessible-adaptor.c
common/Makefile.am
configure.ac
cspi/Makefile.am
dbind/Makefile.am [new file with mode: 0644]
dbind/dbind-any.c [new file with mode: 0644]
dbind/dbind-any.h [new file with mode: 0644]
dbind/dbind.c [new file with mode: 0644]
dbind/dbind.h [new file with mode: 0644]
dbind/dbtest.c [new file with mode: 0644]
droute/Makefile.am [new file with mode: 0644]
droute/droute-pairhash.c [new file with mode: 0644]
droute/droute-pairhash.h [new file with mode: 0644]
droute/droute-test.c [new file with mode: 0644]
droute/droute-variant.c [new file with mode: 0644]
droute/droute-variant.h [new file with mode: 0644]
droute/droute.c [new file with mode: 0644]
droute/droute.h [new file with mode: 0644]
droute/test.interface.One [new file with mode: 0644]
droute/test.interface.Two [new file with mode: 0644]

index a05ffab..305d97d 100644 (file)
@@ -1 +1 @@
-SUBDIRS=common atk-adaptor tests
+SUBDIRS=droute dbind common atk-adaptor tests
diff --git a/README b/README
index 5fb501f..21e57d0 100644 (file)
--- a/README
+++ b/README
@@ -69,6 +69,22 @@ The directories within this package are arranged as follows:
         the ATK adaptor and 'Cspi'.
         Structures are mainly related to events.
 
+    droute
+
+        Contains a framework for registering objects
+        with a D-Bus connection and for routing messages to
+        the implementing object.
+
+        Used by the ATK adaptor.
+
+    dbind
+
+        Library to ease making D-Bus method calls, contains
+        marshalling code to convert function arguments
+        and a provided D-Bus signature into a D-Bus message.
+
+        Used by cspi.
+
     atk-adaptor
 
         This directory contains code that bridges 
index 5d89a2d..f345355 100644 (file)
@@ -21,6 +21,8 @@ libatk_bridge_la_LIBADD = $(DBUS_GLIB_LIBS) \
                          $(ATK_LIBS)       \
                          $(DBIND_LIBS)     \
                          $(DROUTE_LIBS)    \
+                         $(top_builddir)/droute/libdroute.la \
+                         $(top_builddir)/dbind/libdbind.la \
                          $(top_builddir)/common/libspicommon.la
 
 libatk_bridge_la_SOURCES =     \
index c59f39e..1940d9b 100644 (file)
@@ -495,19 +495,27 @@ impl_getState (DBusConnection *bus,
                void *user_data)
 {
   AtkObject *object = (AtkObject *) user_data;
-  dbus_uint32_t rv[2];
-  dbus_uint32_t *array = rv;
-  DBusMessage *reply;
+
+  DBusMessage    *reply = NULL;
+  DBusMessageIter iter, iter_array;
+
+  dbus_uint32_t  states [2];
+
+  guint count;
 
   g_return_val_if_fail (ATK_IS_OBJECT (user_data),
                         droute_not_yet_handled_error (message));
-  spi_atk_state_to_dbus_array (object, rv);
+
   reply = dbus_message_new_method_return (message);
-  if (reply)
+  dbus_message_iter_init_append (reply, &iter);
+
+  spi_atk_state_to_dbus_array (object, states);
+  dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "u", &iter_array);
+  for (count = 0; count < 2; count++)
     {
-      dbus_message_append_args (reply, DBUS_TYPE_ARRAY, DBUS_TYPE_UINT32, &array,
-                               2, DBUS_TYPE_INVALID);
+      dbus_message_iter_append_basic (&iter_array, DBUS_TYPE_UINT32, &states[count]);
     }
+  dbus_message_iter_close_container (&iter, &iter_array);
   return reply;
 }
 
@@ -517,7 +525,7 @@ impl_getAttributes (DBusConnection *bus,
                     void *user_data)
 {
   AtkObject *object = (AtkObject *) user_data;
-  DBusMessage *reply;
+  DBusMessage *reply = NULL;
 
   AtkAttributeSet *attributes;
   DBusMessageIter iter;
index cbf5671..066c30c 100644 (file)
@@ -8,30 +8,19 @@ libspicommon_la_CFLAGS = $(DBUS_GLIB_CFLAGS)\
 
 libspicommon_la_LIBADD = $(DBUS_GLIB_LIBS)
 
-#spicommonincludedir = $(includedir)/at-spi-1.0/libspi
-#
-#spicommoninclude_HEADERS =            \
-#      bitarray.h \
-#      event-types.h \
-#      generated-types.h \
-#      keymasks.h \
-#      spi-dbus.h \
-#      spi-stateset.h \
-#      spi-types.h
-
-#BUILT_SOURCES = generated-types.h
-#CLEANFILES = generated-types.h
-
-nodist_libspicommon_la_SOURCES = generated-types.h
-
 libspicommon_la_SOURCES =      \
-       bitarray.h \
+       bitarray.h              \
+        keymasks.h              \
+        generated-types.h       \
        event-types.h           \
        spi-types.h             \
        spi-dbus.h              \
-       spi-dbus.c \
-       spi-stateset.h \
+       spi-dbus.c              \
+       spi-stateset.h          \
        spi-stateset.c
 
+#BUILT_SOURCES = generated-types.h
+#CLEANFILES = generated-types.h
+
 #generated-types.h: $(top_srcdir)/xml/spec.xml $(top_srcdir)/tools/c-constants-generator.xsl
 #      xsltproc --stringparam mixed-case-prefix Accessibility_ $(top_srcdir)/tools/c-constants-generator.xsl $< >$@
index 81e2af0..e27f605 100644 (file)
@@ -33,14 +33,6 @@ PKG_CHECK_MODULES(DBUS, [dbus-1 >= 1.0])
 AC_SUBST(DBUS_LIBS)
 AC_SUBST(DBUS_CFLAGS)
 
-PKG_CHECK_MODULES(DROUTE, [droute-0.1 >= 0.1])
-AC_SUBST(DROUTE_LIBS)
-AC_SUBST(DROUTE_CFLAGS)
-
-PKG_CHECK_MODULES(DBIND, [dbind-0.1 >= 0.1])
-AC_SUBST(DBIND_LIBS)
-AC_SUBST(DBIND_CFLAGS)
-
 PKG_CHECK_MODULES(GLIB, [glib-2.0])
 AC_SUBST(GLIB_LIBS)
 AC_SUBST(GLIB_CFLAGS)
@@ -98,7 +90,23 @@ else
 fi
 AC_SUBST(EXTRA_SOCKET_LIBS)
 
+dnl find sizes & alignments
+orig_CPPFLAGS=$CPPFLAGS
+CPPFLAGS="$CPPFLAGS $DBUS_CFLAGS"
+DBIND_CHECK_ALIGNOF(char)
+DBIND_CHECK_ALIGNOF(dbus_bool_t)
+DBIND_CHECK_ALIGNOF(dbus_int16_t)
+DBIND_CHECK_ALIGNOF(dbus_int32_t)
+DBIND_CHECK_ALIGNOF(dbus_int64_t)
+DBIND_CHECK_ALIGNOF(double)
+DBIND_CHECK_ALIGNOF(dbind_pointer)
+DBIND_CHECK_ALIGNOF(dbind_struct)
+CPPFLAGS=$orig_CPPFLAGS 
+
 AC_CONFIG_FILES([Makefile
+                droute/Makefile
+                dbind/Makefile
+                dbind/dbind-config.h
                 common/Makefile
                 atk-adaptor/Makefile
                 tests/Makefile
index 2ce79ef..49a56e6 100644 (file)
@@ -44,4 +44,6 @@ libcspi_la_LIBADD =                   \
         $(X_LIBS)                       \
         $(DBUS_GLIB_LIBS)              \
         $(ATK_LIBS)                     \
+        $(top_builddir)/droute/libdbind.la \
+        $(top_builddir)/droute/libdroute.la \
         $(top_builddir)/common/libspicommon.la
diff --git a/dbind/Makefile.am b/dbind/Makefile.am
new file mode 100644 (file)
index 0000000..f7e6f53
--- /dev/null
@@ -0,0 +1,22 @@
+noinst_LTLIBRARIES = libdbind.la
+
+AM_CPPFLAGS = \
+       -DG_LOG_DOMAIN=\"dbind\" \
+       -I$(top_srcdir) \
+       $(WARN_CFLAGS) \
+       $(DBUS_CFLAGS) \
+       $(GLIB_CFLAGS)
+
+libdbind_la_SOURCES = \
+        dbind-config.h \
+        dbind.h \
+       dbind.c \
+        dbind-any.h \
+       dbind-any.c
+libdbind_la_LIBADD = $(DBUS_LIBS) $(GLIB_LIBS)
+
+TESTS = dbtest
+
+check_PROGRAMS = dbtest
+dbtest_SOURCES = dbtest.c
+dbtest_LDFLAGS = libdbind.la
diff --git a/dbind/dbind-any.c b/dbind/dbind-any.c
new file mode 100644 (file)
index 0000000..79a8832
--- /dev/null
@@ -0,0 +1,553 @@
+/* type driven marshalling */
+#include <stdio.h>
+#include <glib.h>
+
+#include "config.h"
+#include "dbind-config.h"
+#include "dbind-any.h"
+
+#undef DEBUG
+
+/*  Align a value upward to a boundary, expressed as a number of bytes.
+ *  E.g. align to an 8-byte boundary with argument of 8.
+ *
+ *   (this + boundary - 1)
+ *          &
+ *    ~(boundary - 1)
+ */
+#define ALIGN_VALUE(this, boundary) \
+  (( ((gulong)(this)) + (((gulong)(boundary)) -1)) & (~(((gulong)(boundary))-1)))
+
+#define ALIGN_ADDRESS(this, boundary) \
+  ((gpointer)ALIGN_VALUE(this, boundary))
+
+#define PTR_PLUS(ptr, offset) \
+        ((gpointer) (((guchar *)(ptr)) + (offset)))
+
+#define DBIND_POD_CASES \
+         DBUS_TYPE_BYTE: \
+    case DBUS_TYPE_INT16: \
+    case DBUS_TYPE_UINT16: \
+    case DBUS_TYPE_INT32: \
+    case DBUS_TYPE_UINT32: \
+    case DBUS_TYPE_BOOLEAN: \
+    case DBUS_TYPE_INT64: \
+    case DBUS_TYPE_UINT64: \
+    case DBUS_TYPE_DOUBLE
+
+/*---------------------------------------------------------------------------*/
+
+static void
+warn_braces ()
+{
+    fprintf (stderr, "Error: dbus flags structures & dicts with braces rather than "
+             " an explicit type member of 'struct'\n");
+}
+
+/*---------------------------------------------------------------------------*/
+
+static unsigned int
+dbind_find_c_alignment_r (char **type)
+{
+    unsigned int retval = 1;
+
+    char t = **type;
+    (*type)++;
+
+#ifdef DEBUG
+    fprintf (stderr, "\tfind align for %c (0x%x)\n", t, t);
+#endif
+
+        switch (t) {
+    case DBUS_TYPE_BYTE:
+        return DBIND_ALIGNOF_CHAR;
+    case DBUS_TYPE_BOOLEAN:
+        return DBIND_ALIGNOF_DBUS_BOOL_T;
+    case DBUS_TYPE_INT16:
+    case DBUS_TYPE_UINT16:
+        return DBIND_ALIGNOF_DBUS_INT16_T;
+    case DBUS_TYPE_INT32:
+    case DBUS_TYPE_UINT32:
+        return DBIND_ALIGNOF_DBUS_INT32_T;
+    case DBUS_TYPE_INT64:
+    case DBUS_TYPE_UINT64:
+        return DBIND_ALIGNOF_DBUS_INT64_T;
+    case DBUS_TYPE_DOUBLE:
+        return DBIND_ALIGNOF_DOUBLE;
+    /* ptr types */
+    case DBUS_TYPE_STRING:
+    case DBUS_TYPE_OBJECT_PATH:
+    case DBUS_TYPE_SIGNATURE:
+    case DBUS_TYPE_ARRAY:
+        return DBIND_ALIGNOF_DBIND_POINTER;
+    case DBUS_STRUCT_BEGIN_CHAR:
+#if DBIND_ALIGNOF_DBIND_STRUCT > 1
+                retval = MAX (retval, DBIND_ALIGNOF_DBIND_STRUCT);
+#endif
+        while (**type != DBUS_STRUCT_END_CHAR) {
+            int elem_align = dbind_find_c_alignment_r (type);
+                        retval = MAX (retval, elem_align);
+        }
+        (*type)++;
+        return retval;
+    case DBUS_TYPE_STRUCT:
+    case DBUS_TYPE_DICT_ENTRY:
+        warn_braces ();
+        return DBIND_ALIGNOF_DBIND_POINTER;
+    case '\0':
+        g_assert_not_reached();
+        break;
+    default:
+                return 1;
+  }
+}
+
+/*---------------------------------------------------------------------------*/
+
+/* gather immediate allocation information for this type */
+static size_t
+dbind_gather_alloc_info_r (char **type)
+{
+  char t = **type;
+  (*type)++;
+  if (t == DBUS_TYPE_ARRAY)
+    {
+      switch (**type)
+        {
+          case DBUS_STRUCT_BEGIN_CHAR:
+              while (**type != DBUS_STRUCT_END_CHAR && **type != '\0') (*type)++;
+              if (**type != '\0') (*type)++;
+              break;
+          case '\0':
+              break;
+          default:
+              (*type)++;
+              break;
+        }
+    }
+
+  switch (t) {
+    case DBUS_TYPE_BYTE:
+        return sizeof (char);
+    case DBUS_TYPE_BOOLEAN:
+        return sizeof (dbus_bool_t);
+    case DBUS_TYPE_INT16:
+    case DBUS_TYPE_UINT16:
+        return sizeof (dbus_int16_t);
+    case DBUS_TYPE_INT32:
+    case DBUS_TYPE_UINT32:
+        return sizeof (dbus_int32_t);
+    case DBUS_TYPE_INT64:
+    case DBUS_TYPE_UINT64:
+        return sizeof (dbus_int64_t);
+    case DBUS_TYPE_DOUBLE:
+        return sizeof (double);
+    /* ptr types */
+    case DBUS_TYPE_STRING:
+    case DBUS_TYPE_OBJECT_PATH:
+    case DBUS_TYPE_SIGNATURE:
+    case DBUS_TYPE_ARRAY:
+        return sizeof (void *);
+    case DBUS_STRUCT_BEGIN_CHAR: {
+                int sum = 0, stralign;
+
+        stralign = dbind_find_c_alignment (*type - 1);
+
+        while (**type != DBUS_STRUCT_END_CHAR) {
+                        sum = ALIGN_VALUE (sum, dbind_find_c_alignment (*type));
+                        sum += dbind_gather_alloc_info_r (type);
+        }
+                sum = ALIGN_VALUE (sum, stralign);
+
+        g_assert (**type == DBUS_STRUCT_END_CHAR);
+        (*type)++;
+
+                return sum;
+    }
+    case DBUS_TYPE_STRUCT:
+    case DBUS_TYPE_DICT_ENTRY:
+        warn_braces ();
+    default:
+        return 0;
+  }
+}
+
+static size_t
+dbind_gather_alloc_info (char *type)
+{
+  return dbind_gather_alloc_info_r (&type);
+}
+
+/*---------------------------------------------------------------------------*/
+
+static void
+dbind_any_free_r (char **type, void **data)
+{
+#ifdef DEBUG
+    fprintf (stderr, "any free '%c' to %p\n", **type, *data);
+#endif
+
+    switch (**type) {
+    case DBIND_POD_CASES:
+        *data = ((guchar *)*data) + dbind_gather_alloc_info (*type);
+        (*type)++;
+        break;
+    case DBUS_TYPE_STRING:
+    case DBUS_TYPE_OBJECT_PATH:
+    case DBUS_TYPE_SIGNATURE:
+#ifdef DEBUG
+        fprintf (stderr, "string free %p\n", **(void ***)data);
+#endif
+        g_free (**(void ***)data);
+        *data = ((guchar *)*data) + dbind_gather_alloc_info (*type);
+        (*type)++;
+        break;
+    case DBUS_TYPE_ARRAY: {
+        int i;
+        GArray *vals = **(void ***)data;
+        size_t elem_size, elem_align;
+        char *saved_child_type;
+
+        (*type)++;
+        saved_child_type = *type;
+
+        elem_size = dbind_gather_alloc_info (*type);
+        elem_align = dbind_find_c_alignment_r (type); 
+
+        for (i = 0; i < vals->len; i++) {
+            void *ptr = vals->data + elem_size * i;
+            *type = saved_child_type; /* rewind type info */
+            ptr = ALIGN_ADDRESS (ptr, elem_align);
+            dbind_any_free_r (type, &ptr);
+        }
+        g_array_free (vals, TRUE);
+        break;
+    }
+    case DBUS_STRUCT_BEGIN_CHAR: {
+                gconstpointer data0 = *data;
+                int offset = 0, stralign;
+
+        stralign = dbind_find_c_alignment (*type);
+        (*type)++;
+
+        offset = 0 ;
+        while (**type != DBUS_STRUCT_END_CHAR) {
+            char *subt = *type;
+                        offset = ALIGN_VALUE (offset, dbind_find_c_alignment (*type));
+                        *data = PTR_PLUS (data0, offset);
+            dbind_any_free_r (type, data);
+            offset += dbind_gather_alloc_info (subt);
+        }
+
+                offset = ALIGN_VALUE (offset, stralign);
+                *data = PTR_PLUS (data0, offset);
+
+        g_assert (**type == DBUS_STRUCT_END_CHAR);
+        (*type)++;
+
+        break;
+    }
+    case DBUS_TYPE_STRUCT:
+    case DBUS_TYPE_DICT_ENTRY:
+        warn_braces ();
+        break;
+    }
+}
+
+/*---------------------------------------------------------------------------*/
+
+void
+dbind_any_marshal (DBusMessageIter *iter,
+                   char           **type,
+                   void           **data)
+{
+    size_t len;
+
+#ifdef DEBUG
+    fprintf (stderr, "any marshal '%c' to %p\n", **type, *data);
+#endif
+
+    switch (**type) {
+    case DBIND_POD_CASES:
+    case DBUS_TYPE_STRING:
+    case DBUS_TYPE_OBJECT_PATH:
+    case DBUS_TYPE_SIGNATURE:
+        len = dbind_gather_alloc_info (*type);
+        dbus_message_iter_append_basic (iter, **type, *data);
+        *data = ((guchar *)*data) + len;
+        (*type)++;
+        break;
+    case DBUS_TYPE_ARRAY: {
+        int i;
+        GArray *vals = **(void ***)data;
+        size_t elem_size, elem_align;
+        DBusMessageIter sub;
+        char *saved_child_type, *child_type_string;
+
+        (*type)++;
+        saved_child_type = *type;
+
+        elem_size = dbind_gather_alloc_info (*type);
+        elem_align = dbind_find_c_alignment_r (type); 
+
+        /* wow this part of the API sucks too ... */
+        child_type_string = g_strndup (saved_child_type, *type - saved_child_type);
+        /* fprintf (stderr, "array child type '%s'\n", child_type_string); */
+        dbus_message_iter_open_container (iter, DBUS_TYPE_ARRAY,
+                                          child_type_string, &sub);
+        for (i = 0; i < vals->len; i++) {
+            void *ptr = vals->data + elem_size * i;
+            *type = saved_child_type; /* rewind type info */
+            ptr = ALIGN_ADDRESS (ptr, elem_align);
+            dbind_any_marshal (&sub, type, &ptr);
+        }
+
+        dbus_message_iter_close_container (iter, &sub);
+        g_free (child_type_string);
+        break;
+    }
+    case DBUS_STRUCT_BEGIN_CHAR: {
+                gconstpointer data0 = *data;
+                int offset = 0, stralign;
+        DBusMessageIter sub;
+
+        stralign = dbind_find_c_alignment (*type);
+
+        (*type)++;
+
+        dbus_message_iter_open_container (iter, DBUS_TYPE_STRUCT, NULL, &sub);
+
+        offset = 0 ;
+        while (**type != DBUS_STRUCT_END_CHAR) {
+            char *subt = *type;
+                        offset = ALIGN_VALUE (offset, dbind_find_c_alignment (*type));
+                        *data = PTR_PLUS (data0, offset);
+            dbind_any_marshal (&sub, type, data);
+            offset += dbind_gather_alloc_info (subt);
+        }
+
+                offset = ALIGN_VALUE (offset, stralign);
+                *data = PTR_PLUS (data0, offset);
+
+        dbus_message_iter_close_container (iter, &sub);
+
+        g_assert (**type == DBUS_STRUCT_END_CHAR);
+        (*type)++;
+
+        break;
+    }
+    case DBUS_TYPE_STRUCT:
+    case DBUS_TYPE_DICT_ENTRY:
+        warn_braces ();
+        break;
+    }
+}
+
+/*---------------------------------------------------------------------------*/
+
+void
+dbind_any_marshal_va (DBusMessageIter *iter,
+                      char           **arg_types,
+                      va_list          args)
+{
+    char *p = *arg_types;
+
+    /* Guard against null arg types 
+       Fix for - http://bugs.freedesktop.org/show_bug.cgi?id=23027
+     */
+    if (p == NULL)
+        p = "";
+
+    {
+        /* special case base-types since we need to walk the stack worse-luck */
+        for (;*p != '\0' && *p != '=';) {
+            int intarg;
+            void *ptrarg;
+            double doublearg;
+            dbus_int64_t int64arg;
+            void *arg = NULL;
+
+            switch (*p) {
+            case DBUS_TYPE_BYTE:
+            case DBUS_TYPE_BOOLEAN:
+            case DBUS_TYPE_INT16:
+            case DBUS_TYPE_UINT16:
+            case DBUS_TYPE_INT32:
+            case DBUS_TYPE_UINT32:
+                intarg = va_arg (args, int);
+                arg = &intarg;
+                break;
+            case DBUS_TYPE_INT64:
+            case DBUS_TYPE_UINT64:
+                int64arg = va_arg (args, dbus_int64_t);
+                arg = &int64arg;
+                break;
+            case DBUS_TYPE_DOUBLE:
+                doublearg = va_arg (args, double);
+                arg = &doublearg;
+                break;
+            /* ptr types */
+            case DBUS_TYPE_STRING:
+            case DBUS_TYPE_OBJECT_PATH:
+            case DBUS_TYPE_SIGNATURE:
+            case DBUS_TYPE_ARRAY:
+            case DBUS_TYPE_DICT_ENTRY:
+                ptrarg = va_arg (args, void *);
+                arg = &ptrarg;
+                break;
+            case DBUS_STRUCT_BEGIN_CHAR:
+                ptrarg = va_arg (args, void *);
+                arg = ptrarg;
+                break;
+
+            case DBUS_TYPE_VARIANT:
+                fprintf (stderr, "No variant support yet - very toolkit specific\n");
+                ptrarg = va_arg (args, void *);
+                arg = &ptrarg;
+                break;
+            default:
+                fprintf (stderr, "Unknown / invalid arg type %c\n", *p);
+                break;
+            }
+            if (arg != NULL)
+                dbind_any_marshal (iter, &p, &arg);
+            }
+    }
+}
+
+/*---------------------------------------------------------------------------*/
+
+void
+dbind_any_demarshal (DBusMessageIter *iter,
+                     char           **type,
+                     void           **data)
+{
+    size_t len;
+
+#ifdef DEBUG
+    fprintf (stderr, "any demarshal '%c' to %p\n", **type, *data);
+#endif
+
+    switch (**type) {
+    case DBIND_POD_CASES:
+        len = dbind_gather_alloc_info (*type);
+        dbus_message_iter_get_basic (iter, *data);
+        *data = ((guchar *)*data) + len;
+        (*type)++;
+        break;
+    case DBUS_TYPE_STRING:
+    case DBUS_TYPE_OBJECT_PATH:
+    case DBUS_TYPE_SIGNATURE:
+        len = dbind_gather_alloc_info (*type);
+        dbus_message_iter_get_basic (iter, *data);
+#ifdef DEBUG
+        fprintf (stderr, "dup string '%s' (%p)\n", **(void ***)data, **(void ***)data);
+#endif
+        **(void ***)data = g_strdup (**(void ***)data);
+        *data = ((guchar *)*data) + len;
+        (*type)++;
+        break;
+    case DBUS_TYPE_ARRAY: {
+        GArray *vals;
+        DBusMessageIter child;
+        size_t elem_size, elem_align;
+        char *stored_child_type;
+        int i;
+
+        (*type)++;
+        stored_child_type = *type;
+
+        elem_size = dbind_gather_alloc_info (*type);
+        elem_align = dbind_find_c_alignment_r (type);
+        vals = g_array_new (FALSE, FALSE, elem_size);
+        (**(void ***)data) = vals;
+        *data = ((guchar *)*data) + sizeof (void *);
+
+        i = 0;
+        dbus_message_iter_recurse (iter, &child);
+        while (dbus_message_iter_get_arg_type (&child) != DBUS_TYPE_INVALID) {
+            void *ptr;
+            char *subt = stored_child_type;
+            g_array_set_size (vals, i + 1);
+            ptr = vals->data + elem_size * i;
+            ptr = ALIGN_ADDRESS (ptr, elem_align);
+            dbind_any_demarshal (&child, &subt, &ptr);
+            i++;
+        };
+        break;
+    }
+    case DBUS_STRUCT_BEGIN_CHAR: {
+                gconstpointer data0 = *data;
+                int offset = 0, stralign;
+        DBusMessageIter child;
+
+        stralign = dbind_find_c_alignment (*type);
+
+        (*type)++;
+
+        dbus_message_iter_recurse (iter, &child);
+
+        while (**type != DBUS_STRUCT_END_CHAR) {
+            char *subt = *type;
+                        offset = ALIGN_VALUE (offset, dbind_find_c_alignment (*type));
+                        *data = PTR_PLUS (data0, offset);
+            dbind_any_demarshal (&child, type, data);
+            offset += dbind_gather_alloc_info (subt);
+        }
+
+                offset = ALIGN_VALUE (offset, stralign);
+                *data = PTR_PLUS (data0, offset);
+
+        g_assert (**type == DBUS_STRUCT_END_CHAR);
+        (*type)++;
+
+        break;
+    }
+    case DBUS_TYPE_STRUCT:
+    case DBUS_TYPE_DICT_ENTRY:
+        warn_braces ();
+        break;
+    }
+    dbus_message_iter_next (iter);
+}
+
+/*---------------------------------------------------------------------------*/
+
+void
+dbind_any_demarshal_va (DBusMessageIter *iter,
+                        char           **arg_types,
+                        va_list          args)
+{
+    char *p = *arg_types;
+    for (;*p != '\0';) {
+        void *arg = va_arg (args, void *);
+        dbind_any_demarshal (iter, &p, &arg);
+    }
+}
+
+/*---------------------------------------------------------------------------*/
+
+/* nice deep free ... */
+void
+dbind_any_free (char *type,
+                void *ptr)
+{
+    dbind_any_free_r (&type, &ptr);
+}
+
+/* should this be the default normalization ? */
+void
+dbind_any_free_ptr (char *type, void *ptr)
+{
+    dbind_any_free (type, &ptr);
+}
+
+/*---------------------------------------------------------------------------*/
+
+unsigned int
+dbind_find_c_alignment (char *type)
+{
+    return dbind_find_c_alignment_r (&type);
+}
+
+/*END------------------------------------------------------------------------*/
diff --git a/dbind/dbind-any.h b/dbind/dbind-any.h
new file mode 100644 (file)
index 0000000..975b244
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef _DBIND_ANY_H_
+#define _DBIND_ANY_H_
+
+#define DBUS_API_SUBJECT_TO_CHANGE
+#include <dbus/dbus.h>
+
+unsigned int dbind_find_c_alignment (char *type);
+
+void   dbind_any_marshal       (DBusMessageIter *iter,
+                                char           **type,
+                                void           **val);
+
+void   dbind_any_marshal_va    (DBusMessageIter *iter,
+                                char           **arg_types,
+                                va_list          args);
+
+void   dbind_any_demarshal     (DBusMessageIter *iter,
+                                char           **type,
+                                void           **val);
+
+void   dbind_any_demarshal_va  (DBusMessageIter *iter,
+                                char           **arg_types,
+                                va_list          args);
+
+void   dbind_any_free          (char            *type,
+                                void            *ptr_to_ptr);
+
+void   dbind_any_free_ptr      (char            *type,
+                                void            *ptr);
+
+#endif /* _DBIND_ANY_H_ */
diff --git a/dbind/dbind.c b/dbind/dbind.c
new file mode 100644 (file)
index 0000000..f405888
--- /dev/null
@@ -0,0 +1,230 @@
+
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <glib.h>
+
+#include "config.h"
+#include "dbind/dbind.h"
+
+/*
+ * FIXME: compare types - to ensure they match &
+ *        do dynamic padding of structures etc.
+ */
+
+/*---------------------------------------------------------------------------*/
+
+static void
+set_reply (DBusPendingCall *pending, void *user_data)
+{
+    void **replyptr = (void **)user_data;
+
+    *replyptr = dbus_pending_call_steal_reply (pending);
+}
+
+static DBusMessage *
+send_and_allow_reentry (DBusConnection *bus, DBusMessage *message, DBusError *error)
+{
+    DBusPendingCall *pending;
+    DBusMessage *reply = NULL;
+
+    if (!dbus_connection_send_with_reply (bus, message, &pending, -1))
+    {
+        return NULL;
+    }
+    dbus_pending_call_set_notify (pending, set_reply, (void *)&reply, NULL);
+    while (!reply)
+    {
+      if (!dbus_connection_read_write_dispatch (bus, -1)) return NULL;
+    }
+    return reply;
+}
+
+dbus_bool_t
+dbind_method_call_reentrant_va (DBusConnection *cnx,
+                                const char     *bus_name,
+                                const char     *path,
+                                const char     *interface,
+                                const char     *method,
+                                DBusError      *opt_error,
+                                const char     *arg_types,
+                                va_list         args)
+{
+    dbus_bool_t success = FALSE;
+    DBusMessage *msg = NULL, *reply = NULL;
+    DBusMessageIter iter;
+    DBusError *err, real_err;
+    const char *p;
+
+    if (opt_error)
+        err = opt_error;
+    else {
+        dbus_error_init (&real_err);
+        err = &real_err;
+    }
+
+    msg = dbus_message_new_method_call (bus_name, path, interface, method);
+    if (!msg)
+        goto out;
+
+    p = arg_types;
+    dbus_message_iter_init_append (msg, &iter);
+    dbind_any_marshal_va (&iter, &p, args);
+
+    reply = send_and_allow_reentry (cnx, msg, err);
+    if (!reply)
+        goto out;
+
+    if (dbus_message_get_type (reply) == DBUS_MESSAGE_TYPE_ERROR)
+    {
+      const char *name = dbus_message_get_error_name (reply);
+      dbus_set_error (err, name, g_strdup (""));
+      goto out;
+    }
+    /* demarshal */
+    if (p[0] == '=' && p[1] == '>')
+    {
+        DBusMessageIter iter;
+        p += 2;
+        dbus_message_iter_init (reply, &iter);
+        dbind_any_demarshal_va (&iter, &p, args);
+    }
+
+    success = TRUE;
+out:
+    if (msg)
+        dbus_message_unref (msg);
+
+    if (reply)
+        dbus_message_unref (reply);
+
+    if (err == &real_err)
+        dbus_error_free (err);
+
+    return success;
+}
+
+/**
+ * dbind_method_call_reentrant:
+ *
+ * @cnx:       A D-Bus Connection used to make the method call.
+ * @bus_name:  The D-Bus bus name of the program where the method call should
+ *             be made.
+ * @path:      The D-Bus object path that should handle the method.
+ * @interface: The D-Bus interface used to scope the method name.
+ * @method:    Method to be invoked.
+ * @opt_error: D-Bus error.
+ * @arg_types: Variable length arguments interleaving D-Bus argument types
+ *             and pointers to argument data.
+ *
+ * Makes a D-Bus method call using the supplied location data, method name and
+ * argument data.This function is re-entrant. It continuously reads from the D-Bus
+ * bus and dispatches messages until a reply has been recieved.
+ **/
+dbus_bool_t
+dbind_method_call_reentrant (DBusConnection *cnx,
+                             const char     *bus_name,
+                             const char     *path,
+                             const char     *interface,
+                             const char     *method,
+                             DBusError      *opt_error,
+                             const char     *arg_types,
+                             ...)
+{
+    dbus_bool_t success = FALSE;
+    va_list args;
+
+    va_start (args, arg_types);
+    success = dbind_method_call_reentrant_va (cnx,
+                                              bus_name,
+                                              path,
+                                              interface,
+                                              method,
+                                              opt_error,
+                                              arg_types,
+                                              args);
+    va_end (args);
+
+    return success;
+}
+
+/*---------------------------------------------------------------------------*/
+
+dbus_bool_t
+dbind_emit_signal_va (DBusConnection *cnx,
+                      const char     *path,
+                      const char     *interface,
+                      const char     *signal,
+                      DBusError      *opt_error,
+                      const char     *arg_types,
+                      va_list         args)
+{
+    dbus_bool_t success = FALSE;
+    DBusMessage *msg = NULL;
+    DBusMessageIter iter;
+    DBusError *err, real_err;
+    char *p;
+
+    if (opt_error)
+        err = opt_error;
+    else {
+        dbus_error_init (&real_err);
+        err = &real_err;
+    }
+
+    msg = dbus_message_new_signal (path, interface, signal);
+    if (!msg)
+        goto out;
+
+    p = arg_types;
+    dbus_message_iter_init_append (msg, &iter);
+    dbind_any_marshal_va (&iter, &p, args);
+
+    if (!dbus_connection_send (cnx, msg, NULL))
+       goto out;
+
+    success = TRUE;
+out:
+
+    if (msg)
+        dbus_message_unref (msg);
+
+    if (err == &real_err)
+        dbus_error_free (err);
+
+    return success;
+}
+
+/**
+ * dbind_emit_signal:
+ *
+ * @cnx:       A D-Bus Connection used to make the method call.
+ * @path:      The D-Bus object path that this signal is emitted from.
+ * @interface: The D-Bus interface used to scope the method name.
+ * @signal:    Name of signal to emit.
+ * @opt_error: D-Bus error.
+ * @arg_types: Variable length arguments interleaving D-Bus argument types
+ *             and pointers to argument data.
+ *
+ * Emits a D-Bus signal  using the supplied signal name and argument data.
+ **/
+dbus_bool_t
+dbind_emit_signal (DBusConnection *cnx,
+                   const char     *path,
+                   const char     *interface,
+                   const char     *signal,
+                   DBusError      *opt_error,
+                   const char     *arg_types,
+                   ...)
+{
+    dbus_bool_t success = FALSE;
+    va_list args;
+
+    va_start (args, arg_types);
+    success = dbind_emit_signal_va (cnx, path, interface, signal, opt_error, arg_types, args);
+    va_end (args);
+
+    return success;
+}
+
+/*END------------------------------------------------------------------------*/
diff --git a/dbind/dbind.h b/dbind/dbind.h
new file mode 100644 (file)
index 0000000..cc318c4
--- /dev/null
@@ -0,0 +1,46 @@
+#ifndef _DBIND_H_
+#define _DBIND_H_
+
+#define DBUS_API_SUBJECT_TO_CHANGE
+#include <dbus/dbus.h>
+#include <dbind/dbind-any.h>
+
+dbus_bool_t
+dbind_method_call_reentrant_va (DBusConnection *cnx,
+                                const char     *bus_name,
+                                const char     *path,
+                                const char     *interface,
+                                const char     *method,
+                                DBusError      *opt_error,
+                                const char     *arg_types,
+                                va_list         args);
+
+dbus_bool_t
+dbind_method_call_reentrant (DBusConnection *cnx,
+                             const char     *bus_name,
+                             const char     *path,
+                             const char     *interface,
+                             const char     *method,
+                             DBusError      *opt_error,
+                             const char     *arg_types,
+                             ...);
+
+dbus_bool_t
+dbind_emit_signal_va (DBusConnection *cnx,
+                      const char     *path,
+                      const char     *interface,
+                      const char     *signal,
+                      DBusError      *opt_error,
+                      const char     *arg_types,
+                      va_list         args);
+
+dbus_bool_t
+dbind_emit_signal (DBusConnection *cnx,
+                   const char     *path,
+                   const char     *interface,
+                   const char     *signal,
+                   DBusError      *opt_error,
+                   const char     *arg_types,
+                   ...);
+
+#endif /* _DBIND_H_ */
diff --git a/dbind/dbtest.c b/dbind/dbtest.c
new file mode 100644 (file)
index 0000000..d00486a
--- /dev/null
@@ -0,0 +1,403 @@
+#include <stdio.h>
+#include <glib.h>
+#include <string.h>
+#include <dbind/dbind.h>
+
+/* Wow! dbus is unpleasant to use */
+
+#define DESKICE_PATH      "/Novell/ICEDesktop/Daemon"
+#define DESKICE_NAMESPACE "Novell.ICEDesktop.Daemon"
+
+void marshal (DBusMessage *msg, char *type, void *ptr)
+{
+    DBusMessageIter iter;
+
+    dbus_message_iter_init_append (msg, &iter);
+    dbind_any_marshal (&iter, &type, &ptr);
+}
+
+void demarshal (DBusMessage *msg, char *type, void *ptr)
+{
+    DBusMessageIter iter;
+
+    if (!dbus_message_iter_init (msg, &iter))
+        fprintf (stderr, "no data in msg\n");
+    else
+        dbind_any_demarshal (&iter, &type, &ptr);
+}
+
+#if 0
+dbus_bool_t  dbus_message_marshal   (DBusMessage  *msg,
+                                     char        **marshalled_data_p,
+                                     int          *len_p);
+
+void dump_msg (DBusMessage *msg)
+{
+    char *data = NULL;
+    int   len, i, j;
+
+    dbus_message_marshal (msg, &data, &len);
+    for (i = 0; i < (len+15)/16; i++) {
+        fprintf (stderr, "%4.d | ", i * 16);
+        for (j = 0; j < 16; j++) {
+            unsigned char c = (i*16+j <= len) ? data[i*16+j] : 0;
+            fprintf (stderr, "0x%.2x ", c);
+        }
+        fprintf (stderr, " | ");
+        for (j = 0; j < 16; j++) {
+            char c = (i*16+j <= len) ? data[i*16+j] : '\0';
+            fprintf (stderr, "%c", g_ascii_isprint (c) ? c : '.');
+        }
+    }
+}
+#endif
+
+void test_simple ()
+{
+    dbus_int32_t v1, v2;
+    DBusMessage *msg;
+
+    msg = dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_CALL);
+    v1 = 42;
+    marshal (msg, "i", &v1);
+    demarshal (msg, "i", &v2);
+    g_assert (v2 == 42);
+    g_assert (v1 == v2);
+
+    dbind_any_free ("i", &v2); /* nop */
+    dbus_message_unref (msg);
+
+    fprintf (stderr, "simple ok\n");
+}
+
+void test_array ()
+{
+    GArray *a1, *a2;
+    DBusMessage *msg;
+
+    /* pod types */
+    a1 = g_array_new (FALSE, FALSE, sizeof (dbus_int32_t));
+    g_array_set_size (a1, 4);
+    g_array_index (a1, dbus_int32_t, 0) = 42;
+    g_array_index (a1, dbus_int32_t, 1) = 17;
+    g_array_index (a1, dbus_int32_t, 2) = 26;
+    g_array_index (a1, dbus_int32_t, 3) = 38;
+
+    msg = dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_CALL);
+    marshal (msg, "ai", &a1);
+    demarshal (msg, "ai", &a2);
+
+    g_assert (a2 != NULL);
+    g_assert (a2->len == 4);
+    g_assert (g_array_index (a2, dbus_int32_t, 0) == 42);
+    g_assert (g_array_index (a2, dbus_int32_t, 1) == 17);
+    g_assert (g_array_index (a2, dbus_int32_t, 2) == 26);
+    g_assert (g_array_index (a2, dbus_int32_t, 3) == 38);
+    g_array_free (a1, TRUE);
+
+    dbind_any_free ("ai", &a2);
+    dbus_message_unref (msg);
+
+    fprintf (stderr, "array ok\n");
+}
+
+/* this taught me that the struct type is a mis-nomer, 
+   it is generated by brackets */
+void test_struct_native ()
+{
+    DBusMessage *msg;
+    DBusMessageIter iter, arr, str;
+
+    /* manually create ar(ss) */
+    msg = dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_CALL);
+
+    dbus_message_iter_init_append (msg, &iter);
+
+    dbus_message_iter_open_container (&iter, DBUS_TYPE_ARRAY, "(ss)", &arr);
+    {
+        char *foo;
+        dbus_message_iter_open_container (&arr, DBUS_TYPE_STRUCT, NULL, &str);
+
+        foo = "foo";
+        dbus_message_iter_append_basic (&str, DBUS_TYPE_STRING, &foo);
+        foo = "baa";
+        dbus_message_iter_append_basic (&str, DBUS_TYPE_STRING, &foo);
+        
+        dbus_message_iter_close_container (&arr, &str);
+    }
+    dbus_message_iter_close_container (&iter, &arr);
+
+    fprintf (stderr, "native struct marshalling ok\n");
+    
+    dbus_message_unref (msg);
+}
+
+
+void test_struct_simple ()
+{
+    typedef struct {
+        char *foo;
+        char *baa;
+        char *baz;
+    } FooBaa;
+    GArray *a1 = NULL, *a2 = NULL;
+    DBusMessage *msg;
+
+    a1 = g_array_new (FALSE, FALSE, sizeof (FooBaa));
+    g_array_set_size (a1, 2);
+    g_array_index (a1, FooBaa, 0).foo = "foo";
+    g_array_index (a1, FooBaa, 0).baa = "baa";
+    g_array_index (a1, FooBaa, 0).baz = "baz";
+    g_array_index (a1, FooBaa, 1).foo = "Foo";
+    g_array_index (a1, FooBaa, 1).baa = "baA";
+    g_array_index (a1, FooBaa, 1).baz = "BaZ";
+
+    msg = dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_CALL);
+    marshal (msg, "a(sss)", &a1);
+    demarshal (msg, "a(sss)", &a2);
+
+    g_assert (a2 != NULL);
+    g_assert (a2 != a1);
+    g_assert (a2->len == 2);
+    g_assert (!strcmp (g_array_index (a2, FooBaa, 0).foo, "foo"));
+    g_assert (!strcmp (g_array_index (a2, FooBaa, 0).baa, "baa"));
+    g_assert (!strcmp (g_array_index (a2, FooBaa, 0).baz, "baz"));
+    g_assert (!strcmp (g_array_index (a2, FooBaa, 1).foo, "Foo"));
+    g_assert (!strcmp (g_array_index (a2, FooBaa, 1).baa, "baA"));
+    g_assert (!strcmp (g_array_index (a2, FooBaa, 1).baz, "BaZ"));
+    
+    fprintf (stderr, "simple struct ok\n");
+
+    dbind_any_free ("a(sss)", &a2);
+    dbus_message_unref (msg);
+}
+
+void test_struct_complex ()
+{
+    typedef struct {
+        dbus_int32_t x, y;
+    } Point;
+    typedef struct {
+        unsigned char pad1;
+        double        val;
+        Point         tl, br;
+        char          pad2;
+        char         *name;
+    } Complex;
+#define TYPEOF_POINT \
+    DBUS_STRUCT_BEGIN_CHAR_AS_STRING \
+        DBUS_TYPE_INT32_AS_STRING \
+        DBUS_TYPE_INT32_AS_STRING \
+    DBUS_STRUCT_END_CHAR_AS_STRING
+#define TYPEOF_COMPLEX \
+    DBUS_STRUCT_BEGIN_CHAR_AS_STRING \
+        DBUS_TYPE_BYTE_AS_STRING \
+        DBUS_TYPE_DOUBLE_AS_STRING \
+        TYPEOF_POINT \
+        TYPEOF_POINT \
+        DBUS_TYPE_BYTE_AS_STRING \
+        DBUS_TYPE_STRING_AS_STRING \
+    DBUS_STRUCT_END_CHAR_AS_STRING
+
+
+    DBusMessage *msg;
+    Complex c1, c2;
+
+    memset (&c1, 0, sizeof (c1));
+    memset (&c2, 0, sizeof (c2));
+
+    c1.pad1 = 2;
+    c1.val = 0.1327569;
+    c1.tl.x = 1;
+    c1.tl.y = 17;
+    c1.br.x = 2587;
+    c1.br.y = -1;
+    c1.pad2 = 1;
+    c1.name = "stroustrup";
+
+    msg = dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_CALL);
+    marshal (msg, TYPEOF_COMPLEX, &c1);
+    demarshal (msg, TYPEOF_COMPLEX, &c2);
+
+    g_assert (c2.pad1 == 2);
+    g_assert (c2.val == c1.val);
+    g_assert (c2.val != 0);
+    g_assert (c2.tl.x == 1);
+    g_assert (c2.tl.y == 17);
+    g_assert (c2.br.x == 2587);
+    g_assert (c2.br.y == -1);
+    g_assert (c2.pad2 == 1);
+    g_assert (!strcmp (c1.name, "stroustrup"));
+    
+    fprintf (stderr, "complex struct ok\n");
+
+    dbind_any_free (TYPEOF_COMPLEX, &c2);
+    dbus_message_unref (msg);
+}
+
+void test_struct_with_array ()
+{
+    typedef struct {
+        GArray *vals;
+        unsigned char pad1;
+    } ArrayStruct;
+#define TYPEOF_ARRAYSTRUCT \
+        DBUS_TYPE_ARRAY_AS_STRING \
+    DBUS_STRUCT_BEGIN_CHAR_AS_STRING \
+        DBUS_TYPE_ARRAY_AS_STRING \
+        DBUS_TYPE_UINT32_AS_STRING \
+        DBUS_TYPE_BYTE_AS_STRING \
+    DBUS_STRUCT_END_CHAR_AS_STRING
+
+
+    DBusMessage *msg;
+    GArray *a1, *a2;
+    ArrayStruct *p, *q;
+
+
+    a1 = g_array_new (FALSE, FALSE, sizeof (ArrayStruct));
+    g_array_set_size (a1, 2);
+    p = &g_array_index (a1, ArrayStruct, 0);
+    p[0].vals = g_array_new (FALSE, FALSE, sizeof (dbus_uint32_t));
+    g_array_set_size (p[0].vals, 2);
+    g_array_index (p[0].vals, dbus_uint32_t, 0) = 1;
+    g_array_index (p[0].vals, dbus_uint32_t, 1) = 1000;
+    p[0].pad1 = 2;
+    p[1].vals = g_array_new (FALSE, FALSE, sizeof (dbus_uint32_t));
+    g_array_set_size (p[1].vals, 2);
+    g_array_index (p[1].vals, dbus_uint32_t, 0) = 1000000;
+    g_array_index (p[1].vals, dbus_uint32_t, 1) = 1000000000;
+    p[1].pad1 = 8;
+
+    msg = dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_CALL);
+    marshal (msg, TYPEOF_ARRAYSTRUCT, &a1);
+    demarshal (msg, TYPEOF_ARRAYSTRUCT, &a2);
+
+    q = &g_array_index (a2, ArrayStruct, 0);
+    g_assert (p[0].pad1 == 2);
+    g_assert (g_array_index (p[1].vals, dbus_uint32_t, 1) == 1000000000);
+    
+    fprintf (stderr, "struct with array ok\n");
+
+    dbind_any_free (TYPEOF_ARRAYSTRUCT, &a2);
+    dbus_message_unref (msg);
+    g_array_free (p[0].vals, TRUE);
+    g_array_free (p[1].vals, TRUE);
+}
+
+void test_twovals ()
+{
+    typedef struct {
+        dbus_int32_t v1;
+        dbus_int32_t v2;
+    } TwoVal;
+#define TYPEOF_TWOVAL \
+        DBUS_TYPE_INT32_AS_STRING \
+        DBUS_TYPE_INT32_AS_STRING \
+
+    DBusMessage *msg;
+    DBusMessageIter iter;
+    TwoVal i, o;
+    char *type_twoval = TYPEOF_TWOVAL;
+    char *type;
+    void *ptr;
+
+    msg = dbus_message_new (DBUS_MESSAGE_TYPE_METHOD_CALL);
+    i.v1 = 42;
+  i.v2 = 1764;
+    dbus_message_iter_init_append (msg, &iter);
+    type = type_twoval;
+    ptr = &i;
+    dbind_any_marshal (&iter, &type, &ptr);
+    dbind_any_marshal (&iter, &type, &ptr);
+    dbus_message_iter_init (msg, &iter);
+    type = type_twoval;
+    ptr = &o;
+    dbind_any_demarshal (&iter, &type, &ptr);
+    dbind_any_demarshal (&iter, &type, &ptr);
+    g_assert (o.v1 == 42);
+    g_assert (o.v2 == 1764);
+    g_assert (i.v1 == o.v1);
+    g_assert (i.v2 == o.v2);
+
+    dbind_any_free ("ii", &o); /* nop */
+    dbus_message_unref (msg);
+
+    fprintf (stderr, "two-val ok\n");
+}
+
+void test_marshalling ()
+{
+    test_simple ();
+    test_array ();
+    test_struct_native ();
+    test_struct_simple ();
+    test_struct_complex ();
+    test_struct_with_array ();
+    test_twovals ();
+
+    fprintf (stderr, "Marshalling ok\n");
+}
+
+void test_teamspaces (DBusConnection *bus)
+{
+    GArray *spaces;
+    DBusError error;
+    int i;
+    typedef struct {
+        char *name;
+        char *id;
+        char *url;
+    } TeamSpace;
+
+    dbus_error_init (&error);
+    if (!dbind_method_call_reentrant (bus,
+                                      NULL,
+                                      DESKICE_PATH,
+                                      DESKICE_NAMESPACE,
+                                      "GetTeamList",
+                                      &error,
+                                      "=>a(sss)",
+                                      &spaces)) {
+        fprintf (stderr, "Error getting team spaces %s: %s\n",
+                 error.name, error.message);
+        dbus_error_free (&error);
+        return;
+    }
+
+    if (!spaces) {
+        fprintf (stderr, "no teamspaces\n");
+        return;
+    }
+    fprintf (stderr, "%d teamspace(s)\n", spaces->len);
+    for (i = 0; i < spaces->len; i++) {
+        TeamSpace *space = &g_array_index (spaces, TeamSpace, i);
+        fprintf (stderr, "\t%d: %s, %s, %s\n", i, space->name, space->id, space->url);
+    }
+
+    dbind_any_free_ptr ("a(sss)", spaces);
+}
+
+void test_helpers ()
+{
+    dbind_find_c_alignment ("(sss)");
+    dbind_find_c_alignment ("a(sss)");
+    dbind_find_c_alignment ("(s(s)yd(d)s)");
+    fprintf (stderr, "helpers passed\n");
+}
+
+int main (int argc, char **argv)
+{
+    DBusConnection *bus;
+    DBusError err;
+
+    dbus_error_init (&err);
+
+    bus = dbus_bus_get (DBUS_BUS_SESSION, &err);
+
+    test_helpers ();
+    test_marshalling ();
+    test_teamspaces (bus);
+
+    return 0;
+}
diff --git a/droute/Makefile.am b/droute/Makefile.am
new file mode 100644 (file)
index 0000000..1c4b8e3
--- /dev/null
@@ -0,0 +1,28 @@
+noinst_LTLIBRARIES = libdroute.la
+
+libdroute_la_CFLAGS = $(DBUS_GLIB_CFLAGS) \
+                      -DATSPI_INTROSPECTION_PATH=\"$(pkgdatadir)/$(DEFAULT_ATSPI_INTROSPECTION_PATH)\"\
+                      -I$(top_builddir)\
+                      -I$(top_srcdir)
+
+libdroute_la_SOURCES =\
+               droute.c\
+               droute.h\
+               droute-variant.c\
+               droute-variant.h\
+               droute-pairhash.c\
+               droute-pairhash.h
+libdroute_la_LIBADD = $(DBUS_GLIB_LIBS)
+
+TESTS = droute-test
+
+check_PROGRAMS = droute-test
+droute_test_SOURCES  = droute-test.c
+droute_test_CFLAGS = $(DBUS_GLIB_CFLAGS) \
+                    -I$(top_builddir)\
+                    -I$(top_srcdir)\
+                    -DTEST_INTROSPECTION_DIRECTORY=\"$(top_srcdir)/droute\"
+
+droute_test_LDFLAGS  = $(top_builddir)/dbind/libdbind.la\
+                      libdroute.la\
+                      $(DBUS_GLIB_LIBS)
diff --git a/droute/droute-pairhash.c b/droute/droute-pairhash.c
new file mode 100644 (file)
index 0000000..f3b8da2
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * AT-SPI - Assistive Technology Service Provider Interface
+ * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
+ *
+ * Copyright 2008 Codethink Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library 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.
+ */
+
+#include "droute-pairhash.h"
+
+/*---------------------------------------------------------------------------*/
+
+static guint
+str_hash (guint32 h, const char *p)
+{
+  for (p += 1; *p != '\0'; p++)
+      h = (h << 5) - h + *p;
+
+  return h;
+}
+
+/*---------------------------------------------------------------------------*/
+
+StrPair *
+str_pair_new (const gchar *one, const gchar *two)
+{
+    StrPair *pair;
+
+    pair = g_new (StrPair, 1);
+    pair->one = one;
+    pair->two = two;
+    return pair;
+}
+
+guint
+str_pair_hash (gconstpointer key)
+{
+    StrPair *pair = (StrPair *) key;
+    guint hash = 0;
+
+    /*g_return_val_if_fail (pair      != NULL, 0);
+      g_return_val_if_fail (pair->one != NULL, 0);
+      g_return_val_if_fail (pair->two != NULL, 0);
+    */
+
+    if (*(pair->two) != '\0')
+      {
+        hash = *(pair->two);
+        hash = str_hash (hash, ++(pair->two));
+        hash = str_hash (hash, pair->one);
+      }
+
+    return hash;
+}
+
+gboolean
+str_pair_equal (gconstpointer a, gconstpointer b)
+{
+    StrPair *ap = (StrPair *) a;
+    StrPair *bp = (StrPair *) b;
+
+    if (g_str_equal (ap->one, bp->one) &&
+        g_str_equal (ap->two, bp->two))
+      {
+        return TRUE;
+      }
+    else
+      {
+        return FALSE;
+      }
+}
+
+/*END------------------------------------------------------------------------*/
diff --git a/droute/droute-pairhash.h b/droute/droute-pairhash.h
new file mode 100644 (file)
index 0000000..1491c2d
--- /dev/null
@@ -0,0 +1,41 @@
+/*
+ * AT-SPI - Assistive Technology Service Provider Interface
+ * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
+ *
+ * Copyright 2008 Codethink Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library 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.
+ */
+#ifndef _DROUTE_PAIRHASH_H
+#define _DROUTE_PAIRHASH_H
+
+#include <glib.h>
+
+typedef struct _StrPair StrPair;
+struct _StrPair
+{
+    const gchar *one;
+    const gchar *two;
+};
+
+StrPair *str_pair_new     (const gchar *one,
+                           const gchar *two);
+
+guint    str_pair_hash    (gconstpointer key);
+gboolean str_pair_equal   (gconstpointer a,
+                           gconstpointer b);
+
+#endif /* _DROUTE_PAIRHASH_H */
diff --git a/droute/droute-test.c b/droute/droute-test.c
new file mode 100644 (file)
index 0000000..8b3fb7c
--- /dev/null
@@ -0,0 +1,243 @@
+#include <stdio.h>
+#include <glib.h>
+#include <string.h>
+#include <droute/droute.h>
+#include <dbind/dbind.h>
+
+#include "dbus/dbus-glib-lowlevel.h"
+
+#define TEST_OBJECT_PATH    "/test/object"
+#define TEST_INTERFACE_ONE  "test.interface.One"
+#define TEST_INTERFACE_TWO  "test.interface.Two"
+
+#define OBJECT_ONE "ObjectOne";
+#define OBJECT_TWO "ObjectTwo";
+
+#if !defined TEST_INTROSPECTION_DIRECTORY
+    #error "No introspection XML directory defined"
+#endif
+
+#define STRING_ONE "StringOne"
+#define STRING_TWO "StringTwo"
+
+#define INT_ONE 0
+#define INT_TWO 456
+
+#define NONE_REPLY_STRING "NoneMethod"
+
+typedef struct _AnObject
+{
+    gchar *astring;
+    guint *anint;
+} AnObject;
+
+static DBusConnection *bus;
+static GMainLoop      *main_loop;
+static gboolean       success = TRUE;
+
+static DBusMessage *
+impl_null (DBusConnection *bus, DBusMessage *message, void *user_data)
+{
+    DBusMessage *reply;
+
+    reply = dbus_message_new_method_return (message);
+    return reply;
+}
+
+static DBusMessage *
+impl_getInt (DBusConnection *bus, DBusMessage *message, void *user_data)
+{
+    AnObject    *object = (AnObject *) user_data;
+    DBusMessage *reply;
+    DBusError    error;
+
+    dbus_error_init (&error);
+
+    reply = dbus_message_new_method_return (message);
+    dbus_message_append_args (reply, DBUS_TYPE_INT32, &(object->anint), DBUS_TYPE_INVALID);
+    return reply;
+}
+
+static DBusMessage *
+impl_setInt (DBusConnection *bus, DBusMessage *message, void *user_data)
+{
+    AnObject    *object = (AnObject *) user_data;
+    DBusMessage *reply;
+    DBusError    error;
+
+    dbus_error_init (&error);
+
+    dbus_message_get_args (message, &error, DBUS_TYPE_INT32, &(object->anint), DBUS_TYPE_INVALID);
+
+    reply = dbus_message_new_method_return (message);
+    return reply;
+}
+
+static DBusMessage *
+impl_getString (DBusConnection *bus, DBusMessage *message, void *user_data)
+{
+    AnObject    *object = (AnObject *) user_data;
+    DBusMessage *reply;
+    DBusError    error;
+
+    dbus_error_init (&error);
+
+    reply = dbus_message_new_method_return (message);
+    dbus_message_append_args (reply, DBUS_TYPE_STRING, &(object->astring), DBUS_TYPE_INVALID);
+    return reply;
+}
+
+static DBusMessage *
+impl_setString (DBusConnection *bus, DBusMessage *message, void *user_data)
+{
+    AnObject    *object = (AnObject *) user_data;
+    DBusMessage *reply;
+    DBusError    error;
+
+    dbus_error_init (&error);
+
+    g_free (object->astring);
+    dbus_message_get_args (message, &error, DBUS_TYPE_STRING, &(object->astring), DBUS_TYPE_INVALID);
+
+    reply = dbus_message_new_method_return (message);
+    return reply;
+}
+
+static DBusMessage *
+impl_getInterfaceOne (DBusConnection *bus, DBusMessage *message, void *user_data)
+{
+    DBusMessage *reply;
+    DBusError    error;
+    gchar       *itf = TEST_INTERFACE_ONE;
+
+    dbus_error_init (&error);
+
+    reply = dbus_message_new_method_return (message);
+    dbus_message_append_args (reply, DBUS_TYPE_STRING, &itf, DBUS_TYPE_INVALID);
+    return reply;
+}
+
+static DBusMessage *
+impl_getInterfaceTwo (DBusConnection *bus, DBusMessage *message, void *user_data)
+{
+    DBusMessage *reply;
+    DBusError    error;
+    gchar       *itf = TEST_INTERFACE_TWO;
+
+    dbus_error_init (&error);
+
+    reply = dbus_message_new_method_return (message);
+    dbus_message_append_args (reply, DBUS_TYPE_STRING, &itf, DBUS_TYPE_INVALID);
+    return reply;
+}
+
+static DRouteMethod test_methods_one[] = {
+    {impl_null,            "null"},
+    {impl_getInt,          "getInt"},
+    {impl_setInt,          "setInt"},
+    {impl_getString,       "getString"},
+    {impl_setString,       "setString"},
+    {impl_getInterfaceOne, "getInterfaceOne"},
+    {NULL, NULL}
+};
+
+static DRouteMethod test_methods_two[] = {
+    {impl_null,            "null"},
+    {impl_getInt,          "getInt"},
+    {impl_setInt,          "setInt"},
+    {impl_getString,       "getString"},
+    {impl_setString,       "setString"},
+    {impl_getInterfaceTwo, "getInterfaceTwo"},
+    {NULL, NULL}
+};
+
+static DRouteProperty test_properties[] = {
+    {NULL, NULL, NULL}
+};
+
+gboolean
+do_tests_func (gpointer data)
+{
+    DBusError    error;
+    const gchar *bus_name;
+
+    gchar     *expected_string;
+    gchar     *result_string;
+
+    dbus_error_init (&error);
+    bus_name = dbus_bus_get_unique_name (bus);
+
+    /* --------------------------------------------------------*/
+
+    dbind_method_call_reentrant (bus,
+                                 bus_name,
+                                 TEST_OBJECT_PATH,
+                                 TEST_INTERFACE_ONE,
+                                 "null",
+                                 NULL,
+                                 "");
+
+    /* --------------------------------------------------------*/
+
+    expected_string = TEST_INTERFACE_ONE;
+    result_string = NULL;
+    dbind_method_call_reentrant (bus,
+                                 bus_name,
+                                 TEST_OBJECT_PATH,
+                                 TEST_INTERFACE_ONE,
+                                 "getInterfaceOne",
+                                 NULL,
+                                 "=>s",
+                                 &result_string);
+    if (g_strcmp0(expected_string, result_string))
+    {
+            g_print ("Failed: reply to getInterfaceOne not as expected\n");
+            goto out;
+    }
+
+    /* --------------------------------------------------------*/
+
+out:
+    g_main_loop_quit (main_loop);
+    return FALSE;
+}
+
+
+int main (int argc, char **argv)
+{
+    DRouteContext  *cnx;
+    DRoutePath     *path;
+    AnObject       *object;
+    DBusError       error;
+
+    /* Setup some server object */
+
+    object = g_new0(AnObject, 1);
+    object->astring = g_strdup (STRING_ONE);
+    object->anint = INT_ONE;
+
+    dbus_error_init (&error);
+    main_loop = g_main_loop_new(NULL, FALSE);
+    bus = dbus_bus_get (DBUS_BUS_SESSION, &error);
+    dbus_connection_setup_with_g_main(bus, g_main_context_default());
+
+    cnx = droute_new (bus, TEST_INTROSPECTION_DIRECTORY);
+    path = droute_add_one (cnx, TEST_OBJECT_PATH, object);
+
+    droute_path_add_interface (path,
+                               TEST_INTERFACE_ONE,
+                               test_methods_one,
+                               test_properties);
+
+    droute_path_add_interface (path,
+                               TEST_INTERFACE_TWO,
+                               test_methods_two,
+                               test_properties);
+
+    g_idle_add (do_tests_func, NULL);
+    g_main_run(main_loop);
+    if (success)
+            return 0;
+    else
+            return 1;
+}
diff --git a/droute/droute-variant.c b/droute/droute-variant.c
new file mode 100644 (file)
index 0000000..d4b5ca6
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * AT-SPI - Assistive Technology Service Provider Interface
+ * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
+ *
+ * Copyright 2008 Novell, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "droute-variant.h"
+
+/*---------------------------------------------------------------------------*/
+
+dbus_bool_t
+droute_return_v_int32 (DBusMessageIter *iter, dbus_int32_t val)
+{
+    DBusMessageIter sub;
+
+    if (!dbus_message_iter_open_container
+        (iter, DBUS_TYPE_VARIANT, DBUS_TYPE_INT32_AS_STRING, &sub))
+      {
+        return FALSE;
+      }
+    dbus_message_iter_append_basic (&sub, DBUS_TYPE_INT32, &val);
+    dbus_message_iter_close_container (iter, &sub);
+    return TRUE;
+}
+
+dbus_bool_t
+droute_return_v_double (DBusMessageIter *iter, double val)
+{
+    DBusMessageIter sub;
+
+    if (!dbus_message_iter_open_container
+        (iter, DBUS_TYPE_VARIANT, DBUS_TYPE_DOUBLE_AS_STRING, &sub))
+      {
+        return FALSE;
+      }
+    dbus_message_iter_append_basic (&sub, DBUS_TYPE_DOUBLE, &val);
+    dbus_message_iter_close_container (iter, &sub);
+    return TRUE;
+}
+
+dbus_bool_t
+droute_return_v_string (DBusMessageIter *iter, const char *val)
+{
+    DBusMessageIter sub;
+
+    if (!val)
+      val = "";
+    if (!dbus_message_iter_open_container
+        (iter, DBUS_TYPE_VARIANT, DBUS_TYPE_STRING_AS_STRING, &sub))
+      {
+        return FALSE;
+      }
+    dbus_message_iter_append_basic (&sub, DBUS_TYPE_STRING, &val);
+    dbus_message_iter_close_container (iter, &sub);
+    return TRUE;
+}
+
+dbus_bool_t
+droute_return_v_object (DBusMessageIter *iter, const char *path)
+{
+    DBusMessageIter sub;
+
+    if (!dbus_message_iter_open_container
+        (iter, DBUS_TYPE_VARIANT, DBUS_TYPE_OBJECT_PATH_AS_STRING, &sub))
+      {
+        return FALSE;
+      }
+    dbus_message_iter_append_basic (&sub, DBUS_TYPE_OBJECT_PATH, &path);
+    dbus_message_iter_close_container (iter, &sub);
+    return TRUE;
+}
+
+/*---------------------------------------------------------------------------*/
+
+dbus_int32_t
+droute_get_v_int32 (DBusMessageIter *iter)
+{
+    DBusMessageIter sub;
+    dbus_int32_t rv;
+
+    // TODO- ensure we have the correct type
+    dbus_message_iter_recurse (iter, &sub);
+    dbus_message_iter_get_basic (&sub, &rv);
+    return rv;
+}
+
+const char *
+droute_get_v_string (DBusMessageIter *iter)
+{
+    DBusMessageIter sub;
+    char *rv;
+
+    // TODO- ensure we have the correct type
+    dbus_message_iter_recurse (iter, &sub);
+    dbus_message_iter_get_basic (&sub, &rv);
+    return rv;
+}
+
+/*END------------------------------------------------------------------------*/
diff --git a/droute/droute-variant.h b/droute/droute-variant.h
new file mode 100644 (file)
index 0000000..47feb96
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * AT-SPI - Assistive Technology Service Provider Interface
+ * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
+ *
+ * Copyright 2008 Novell, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library 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.
+ */
+#ifndef _DROUTE_VARIANT_H
+#define _DROUTE_VARIANT_H
+
+#include <dbus/dbus.h>
+
+dbus_bool_t  droute_return_v_int32  (DBusMessageIter *iter, dbus_int32_t val);
+dbus_bool_t  droute_return_v_double (DBusMessageIter *iter, double val);
+dbus_bool_t  droute_return_v_string (DBusMessageIter *iter, const char *val);
+dbus_bool_t  droute_return_v_object (DBusMessageIter *iter, const char *path);
+
+dbus_int32_t droute_get_v_int32     (DBusMessageIter *iter);
+const char  *droute_get_v_string    (DBusMessageIter *iter);
+
+#endif /* _DROUTE_VARIANT_H */
diff --git a/droute/droute.c b/droute/droute.c
new file mode 100644 (file)
index 0000000..0cec8be
--- /dev/null
@@ -0,0 +1,598 @@
+/*
+ * AT-SPI - Assistive Technology Service Provider Interface
+ * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
+ *
+ * Copyright 2008 Novell, Inc.
+ * Copyright 2008 Codethink Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library 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.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "droute.h"
+#include "droute-pairhash.h"
+
+#define CHUNKS_DEFAULT (512)
+
+#define oom() g_error ("D-Bus out of memory, this message will fail anyway")
+
+#if defined DROUTE_DEBUG
+    #define _DROUTE_DEBUG(format, args...) g_print (format , ## args)
+#else
+    #define _DROUTE_DEBUG(format, args...)
+#endif
+
+struct _DRouteContext
+{
+    DBusConnection       *bus;
+    GPtrArray            *registered_paths;
+
+    gchar                *introspect_dir;
+};
+
+struct _DRoutePath
+{
+    DRouteContext        *cnx;
+    GStringChunk         *chunks;
+    GPtrArray            *interfaces;
+    GHashTable           *methods;
+    GHashTable           *properties;
+
+    void                   *user_data;
+    DRouteGetDatumFunction  get_datum;
+};
+
+/*---------------------------------------------------------------------------*/
+
+typedef struct PropertyPair
+{
+    DRoutePropertyFunction get;
+    DRoutePropertyFunction set;
+} PropertyPair;
+
+/*---------------------------------------------------------------------------*/
+
+static DBusHandlerResult
+handle_message (DBusConnection *bus, DBusMessage *message, void *user_data);
+
+/*---------------------------------------------------------------------------*/
+
+static DRoutePath *
+path_new (DRouteContext *cnx,
+          void    *user_data,
+          DRouteGetDatumFunction get_datum)
+{
+    DRoutePath *new_path;
+
+    new_path = g_new0 (DRoutePath, 1);
+    new_path->cnx = cnx;
+    new_path->chunks = g_string_chunk_new (CHUNKS_DEFAULT);
+    new_path->interfaces = g_ptr_array_new ();
+
+    new_path->methods = g_hash_table_new_full ((GHashFunc)str_pair_hash,
+                                               str_pair_equal,
+                                               g_free,
+                                               NULL);
+
+    new_path->properties = g_hash_table_new_full ((GHashFunc)str_pair_hash,
+                                                  str_pair_equal,
+                                                  g_free,
+                                                  NULL);
+
+    new_path->user_data = user_data;
+    new_path->get_datum = get_datum;
+
+    return new_path;
+}
+
+static void
+path_free (DRoutePath *path, gpointer user_data)
+{
+    g_string_chunk_free  (path->chunks);
+    g_ptr_array_free     (path->interfaces, TRUE);
+    g_hash_table_destroy (path->methods);
+    g_hash_table_destroy (path->properties);
+}
+
+static void *
+path_get_datum (DRoutePath *path, const gchar *pathstr)
+{
+    if (path->get_datum != NULL)
+        return (path->get_datum) (pathstr, path->user_data);
+    else
+        return path->user_data;
+}
+
+/*---------------------------------------------------------------------------*/
+
+DRouteContext *
+droute_new (DBusConnection *bus, const char *introspect_dir)
+{
+    DRouteContext *cnx;
+
+    cnx = g_new0 (DRouteContext, 1);
+    cnx->bus = bus;
+    cnx->registered_paths = g_ptr_array_new ();
+    cnx->introspect_dir = g_strdup(introspect_dir);
+
+    return cnx;
+}
+
+void
+droute_free (DRouteContext *cnx)
+{
+    g_ptr_array_foreach (cnx->registered_paths, (GFunc) path_free, NULL);
+    g_free (cnx->introspect_dir);
+    g_free (cnx);
+}
+
+/*---------------------------------------------------------------------------*/
+
+DBusConnection *
+droute_get_bus (DRouteContext *cnx)
+{
+    return cnx->bus;
+}
+
+/*---------------------------------------------------------------------------*/
+
+static DBusObjectPathVTable droute_vtable =
+{
+  NULL,
+  &handle_message,
+  NULL, NULL, NULL, NULL
+};
+
+DRoutePath *
+droute_add_one (DRouteContext *cnx,
+                const char    *path,
+                const void    *data)
+{
+    DRoutePath *new_path;
+    gboolean registered;
+
+    new_path = path_new (cnx, (void *) data, NULL);
+
+    registered = dbus_connection_register_object_path (cnx->bus, path, &droute_vtable, new_path);
+    if (!registered)
+        oom();
+
+    g_ptr_array_add (cnx->registered_paths, new_path);
+    return new_path;
+}
+
+DRoutePath *
+droute_add_many (DRouteContext *cnx,
+                 const char    *path,
+                 const void    *data,
+                 const DRouteGetDatumFunction get_datum)
+{
+    DRoutePath *new_path;
+
+    new_path = path_new (cnx, (void *) data, get_datum);
+
+    if (!dbus_connection_register_fallback (cnx->bus, path, &droute_vtable, new_path))
+        oom();
+
+    g_ptr_array_add (cnx->registered_paths, new_path);
+    return new_path;
+}
+
+/*---------------------------------------------------------------------------*/
+
+void
+droute_path_add_interface(DRoutePath *path,
+                          const char *name,
+                          const DRouteMethod   *methods,
+                          const DRouteProperty *properties)
+{
+    gchar *itf;
+
+    g_return_if_fail (name != NULL);
+
+    itf = g_string_chunk_insert (path->chunks, name);
+    g_ptr_array_add (path->interfaces, itf);
+
+    for (; methods != NULL && methods->name != NULL; methods++)
+      {
+        gchar *meth;
+
+        meth = g_string_chunk_insert (path->chunks, methods->name);
+        g_hash_table_insert (path->methods, str_pair_new (itf, meth), methods->func);
+      }
+
+    for (; properties != NULL && properties->name != NULL; properties++)
+      {
+        gchar *prop;
+        PropertyPair *pair;
+
+        prop = g_string_chunk_insert (path->chunks, properties->name);
+        pair = g_new (PropertyPair, 1);
+        pair->get = properties->get;
+        pair->set = properties->set;
+        g_hash_table_insert (path->properties, str_pair_new (itf, prop), pair);
+      }
+}
+
+/*---------------------------------------------------------------------------*/
+
+/* The data structures don't support an efficient implementation of GetAll
+ * and I don't really care.
+ */
+static DBusMessage *
+impl_prop_GetAll (DBusMessage *message,
+                  DRoutePath  *path,
+                  const char  *pathstr)
+{
+    DBusMessageIter iter, iter_dict, iter_dict_entry;
+    DBusMessage *reply;
+    DBusError error;
+    GHashTableIter prop_iter;
+
+    StrPair *key;
+    PropertyPair *value;
+    gchar *iface;
+
+    void  *datum = path_get_datum (path, pathstr);
+
+    dbus_error_init (&error);
+    if (!dbus_message_get_args
+                (message, &error, DBUS_TYPE_STRING, &iface, DBUS_TYPE_INVALID))
+        return dbus_message_new_error (message, DBUS_ERROR_FAILED, error.message);
+
+    reply = dbus_message_new_method_return (message);
+    if (!reply)
+        oom ();
+
+    dbus_message_iter_init_append (reply, &iter);
+    if (!dbus_message_iter_open_container
+                (&iter, DBUS_TYPE_ARRAY, "{sv}", &iter_dict))
+        oom ();
+
+    g_hash_table_iter_init (&prop_iter, path->properties);
+    while (g_hash_table_iter_next (&prop_iter, (gpointer*)&key, (gpointer*)&value))
+      {
+        if (!g_strcmp0 (key->one, iface))
+         {
+           if (!value->get)
+              continue;
+           if (!dbus_message_iter_open_container
+                        (&iter_dict, DBUS_TYPE_DICT_ENTRY, NULL, &iter_dict_entry))
+              oom ();
+           dbus_message_iter_append_basic (&iter_dict_entry, DBUS_TYPE_STRING,
+                                           key->two);
+           (value->get) (&iter_dict_entry, datum);
+           if (!dbus_message_iter_close_container (&iter_dict, &iter_dict_entry))
+               oom ();
+         }
+      }
+
+    if (!dbus_message_iter_close_container (&iter, &iter_dict))
+        oom ();
+    return reply;
+}
+
+static DBusMessage *
+impl_prop_GetSet (DBusMessage *message,
+                  DRoutePath  *path,
+                  const char  *pathstr,
+                  gboolean     get)
+{
+    DBusMessage *reply = NULL;
+    DBusError error;
+
+    StrPair pair;
+    PropertyPair *prop_funcs = NULL;
+
+
+    dbus_error_init (&error);
+    if (!dbus_message_get_args (message,
+                                &error,
+                                DBUS_TYPE_STRING,
+                                &(pair.one),
+                                DBUS_TYPE_STRING,
+                                &(pair.two),
+                                DBUS_TYPE_INVALID))
+        return dbus_message_new_error (message, DBUS_ERROR_FAILED, error.message);
+
+    _DROUTE_DEBUG ("DRoute (handle prop): %s|%s on %s\n", pair.one, pair.two, pathstr);
+
+    prop_funcs = (PropertyPair *) g_hash_table_lookup (path->properties, &pair);
+    if (!prop_funcs)
+        return dbus_message_new_error (message, DBUS_ERROR_FAILED, "Property unavailable");
+
+    if (get && prop_funcs->get)
+      {
+        void *datum = path_get_datum (path, pathstr);
+        DBusMessageIter iter;
+
+        _DROUTE_DEBUG ("DRoute (handle prop Get): %s|%s on %s\n", pair.one, pair.two, pathstr);
+
+        reply = dbus_message_new_method_return (message);
+        dbus_message_iter_init_append (reply, &iter);
+        if (!(prop_funcs->get) (&iter, datum))
+         {
+           dbus_message_unref (reply);
+            reply = dbus_message_new_error (message, DBUS_ERROR_FAILED, "Get failed");
+         }
+      }
+    else if (!get && prop_funcs->set)
+      {
+        void *datum = path_get_datum (path, pathstr);
+        DBusMessageIter iter;
+
+        _DROUTE_DEBUG ("DRoute (handle prop Get): %s|%s on %s\n", pair.one, pair.two, pathstr);
+
+        dbus_message_iter_init (message, &iter);
+        /* Skip the interface and property name */
+        dbus_message_iter_next(&iter);
+        dbus_message_iter_next(&iter);
+        (prop_funcs->set) (&iter, datum);
+
+        reply = dbus_message_new_method_return (message);
+      }
+    else
+      {
+        reply = dbus_message_new_error (message, DBUS_ERROR_FAILED, "Getter or setter unavailable");
+      }
+
+    return reply;
+}
+
+static DBusHandlerResult
+handle_properties (DBusConnection *bus,
+                   DBusMessage    *message,
+                   DRoutePath     *path,
+                   const gchar    *iface,
+                   const gchar    *member,
+                   const gchar    *pathstr)
+{
+    DBusMessage *reply;
+    DBusHandlerResult result = DBUS_HANDLER_RESULT_HANDLED;
+
+    if (!g_strcmp0(member, "GetAll"))
+       reply = impl_prop_GetAll (message, path, pathstr);
+    else if (!g_strcmp0 (member, "Get"))
+       reply = impl_prop_GetSet (message, path, pathstr, TRUE);
+    else if (!g_strcmp0 (member, "Set"))
+       reply = impl_prop_GetSet (message, path, pathstr, FALSE);
+    else
+       result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    if (reply)
+      {
+        dbus_connection_send (bus, reply, NULL);
+        dbus_message_unref (reply);
+      }
+
+    return result;
+}
+
+/*---------------------------------------------------------------------------*/
+
+static const char *introspection_header =
+"<?xml version=\"1.0\"?>\n";
+
+static const char *introspection_node_element =
+"<node name=\"%s\">\n";
+
+static const char *introspection_footer =
+"</node>";
+
+static void
+append_interface (GString     *str,
+                  const gchar *interface,
+                  const gchar *directory)
+{
+    gchar *filename;
+    gchar *contents;
+    gsize len;
+
+    GError *err = NULL;
+
+    filename = g_build_filename (directory, interface, NULL);
+
+    if (g_file_get_contents (filename, &contents, &len, &err))
+      {
+        g_string_append_len (str, contents, len);
+      }
+    else
+      {
+        g_warning ("AT-SPI: Cannot find introspection XML file %s - %s",
+                   filename, err->message);
+        g_error_free (err);
+      }
+
+    g_string_append (str, "\n");
+    g_free (filename);
+    g_free (contents);
+}
+
+static DBusHandlerResult
+handle_introspection (DBusConnection *bus,
+                      DBusMessage    *message,
+                      DRoutePath     *path,
+                      const gchar    *iface,
+                      const gchar    *member,
+                      const gchar    *pathstr)
+{
+    GString *output;
+    gchar *final;
+    gint i;
+
+    DBusMessage *reply;
+
+    _DROUTE_DEBUG ("DRoute (handle introspection): %s\n", pathstr);
+
+    if (g_strcmp0 (member, "Introspect"))
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    output = g_string_new(introspection_header);
+
+    g_string_append_printf(output, introspection_node_element, pathstr);
+
+    for (i=0; i < path->interfaces->len; i++)
+      {
+        gchar *interface = (gchar *) g_ptr_array_index (path->interfaces, i);
+        append_interface(output, interface, path->cnx->introspect_dir);
+      }
+
+    g_string_append(output, introspection_footer);
+    final = g_string_free(output, FALSE);
+
+    reply = dbus_message_new_method_return (message);
+    if (!reply)
+        oom ();
+    dbus_message_append_args(reply, DBUS_TYPE_STRING, &final,
+                             DBUS_TYPE_INVALID);
+    dbus_connection_send (bus, reply, NULL);
+
+    dbus_message_unref (reply);
+    g_free(final);
+    return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+/*---------------------------------------------------------------------------*/
+
+static DBusHandlerResult
+handle_other (DBusConnection *bus,
+              DBusMessage    *message,
+              DRoutePath     *path,
+              const gchar    *iface,
+              const gchar    *member,
+              const gchar    *pathstr)
+{
+    gint result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    StrPair pair;
+    DRouteFunction func;
+    DBusMessage *reply = NULL;
+
+    pair.one = iface;
+    pair.two = member;
+
+    _DROUTE_DEBUG ("DRoute (handle other): %s|%s on %s\n", member, iface, pathstr);
+
+    func = (DRouteFunction) g_hash_table_lookup (path->methods, &pair);
+    if (func != NULL)
+      {
+        void *datum = path_get_datum (path, pathstr);
+
+        reply = (func) (bus, message, datum);
+
+        if (!reply)
+          {
+            /* All D-Bus method calls must have a reply.
+             * If one is not provided presume that the call has a void
+             * return and no error has occured.
+             */
+            reply = dbus_message_new_method_return (message);
+          }
+        dbus_connection_send (bus, reply, NULL);
+        dbus_message_unref (reply);
+        result = DBUS_HANDLER_RESULT_HANDLED;
+      }
+
+    _DROUTE_DEBUG ("DRoute (handle other) (reply): type %d\n",
+                   dbus_message_get_type(reply));
+    return result;
+}
+
+/*---------------------------------------------------------------------------*/
+
+static DBusHandlerResult
+handle_message (DBusConnection *bus, DBusMessage *message, void *user_data)
+{
+    DRoutePath *path = (DRoutePath *) user_data;
+    const gchar *iface   = dbus_message_get_interface (message);
+    const gchar *member  = dbus_message_get_member (message);
+    const gint   type    = dbus_message_get_type (message);
+    const gchar *pathstr = dbus_message_get_path (message);
+
+    /* Check for basic reasons not to handle */
+    if (type   != DBUS_MESSAGE_TYPE_METHOD_CALL ||
+        member == NULL ||
+        iface  == NULL)
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+    if (!strcmp (iface, "org.freedesktop.DBus.Properties"))
+        return handle_properties (bus, message, path, iface, member, pathstr);
+
+    if (!strcmp (iface, "org.freedesktop.DBus.Introspectable"))
+        return handle_introspection (bus, message, path, iface, member, pathstr);
+
+    return handle_other (bus, message, path, iface, member, pathstr);
+}
+
+/*---------------------------------------------------------------------------*/
+
+DBusMessage *
+droute_not_yet_handled_error (DBusMessage *message)
+{
+    DBusMessage *reply;
+    gchar       *errmsg;
+
+    errmsg= g_strdup_printf (
+            "Method \"%s\" with signature \"%s\" on interface \"%s\" doesn't exist\n",
+            dbus_message_get_member (message),
+            dbus_message_get_signature (message),
+            dbus_message_get_interface (message));
+    reply = dbus_message_new_error (message,
+                                    DBUS_ERROR_UNKNOWN_METHOD,
+                                    errmsg);
+    g_free (errmsg);
+    return reply;
+}
+
+DBusMessage *
+droute_out_of_memory_error (DBusMessage *message)
+{
+    DBusMessage *reply;
+    gchar       *errmsg;
+
+    errmsg= g_strdup_printf (
+            "Method \"%s\" with signature \"%s\" on interface \"%s\" could not be processed due to lack of memory\n",
+            dbus_message_get_member (message),
+            dbus_message_get_signature (message),
+            dbus_message_get_interface (message));
+    reply = dbus_message_new_error (message,
+                                    DBUS_ERROR_NO_MEMORY,
+                                    errmsg);
+    g_free (errmsg);
+    return reply;
+}
+
+DBusMessage *
+droute_invalid_arguments_error (DBusMessage *message)
+{
+    DBusMessage *reply;
+    gchar       *errmsg;
+
+    errmsg= g_strdup_printf (
+            "Method \"%s\" with signature \"%s\" on interface \"%s\" was supplied with invalid arguments\n",
+            dbus_message_get_member (message),
+            dbus_message_get_signature (message),
+            dbus_message_get_interface (message));
+    reply = dbus_message_new_error (message,
+                                    DBUS_ERROR_INVALID_ARGS,
+                                    errmsg);
+    g_free (errmsg);
+    return reply;
+}
+
+/*END------------------------------------------------------------------------*/
diff --git a/droute/droute.h b/droute/droute.h
new file mode 100644 (file)
index 0000000..a892c62
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * AT-SPI - Assistive Technology Service Provider Interface
+ * (Gnome Accessibility Project; http://developer.gnome.org/projects/gap)
+ *
+ * Copyright 2008 Novell, Inc.
+ * Copyright 2008 Codethink Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library 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.
+ */
+#ifndef _DROUTE_H
+#define _DROUTE_H
+
+#include <dbus/dbus.h>
+#include <glib.h>
+
+#include <droute/droute-variant.h>
+
+
+typedef DBusMessage *(*DRouteFunction)         (DBusConnection *, DBusMessage *, void *);
+typedef dbus_bool_t  (*DRoutePropertyFunction) (DBusMessageIter *, void *);
+
+typedef void        *(*DRouteGetDatumFunction) (const char *, void *);
+
+typedef struct _DRouteMethod DRouteMethod;
+struct _DRouteMethod
+{
+    DRouteFunction func;
+    const char *name;
+};
+
+typedef struct _DRouteProperty DRouteProperty;
+struct _DRouteProperty
+{
+    DRoutePropertyFunction get;
+    DRoutePropertyFunction set;
+    const char *name;
+};
+
+/*---------------------------------------------------------------------------*/
+
+typedef struct _DRouteContext DRouteContext;
+
+typedef struct _DRoutePath    DRoutePath;
+
+/*---------------------------------------------------------------------------*/
+
+DRouteContext *
+droute_new      (DBusConnection *bus,
+                 const char *introspect_dir);
+void
+droute_free     (DRouteContext *cnx);
+
+DRoutePath *
+droute_add_one  (DRouteContext *cnx,
+                 const char    *path,
+                 const void    *data);
+
+DRoutePath *
+droute_add_many (DRouteContext *cnx,
+                 const char    *path,
+                 const void    *data,
+                 const DRouteGetDatumFunction get_datum);
+
+void
+droute_path_add_interface (DRoutePath *path,
+                           const char *name,
+                           const DRouteMethod   *methods,
+                           const DRouteProperty *properties);
+
+DBusMessage *
+droute_not_yet_handled_error   (DBusMessage *message);
+
+DBusMessage *
+droute_invalid_arguments_error (DBusMessage *message);
+
+DBusMessage *
+droute_out_of_memory_error     (DBusMessage *message);
+
+DBusConnection *
+droute_get_bus (DRouteContext *cnx);
+
+#endif /* _DROUTE_H */
diff --git a/droute/test.interface.One b/droute/test.interface.One
new file mode 100644 (file)
index 0000000..a8e2206
--- /dev/null
@@ -0,0 +1,15 @@
+<interface name="test.interface.One">
+  <method name="null"/>
+  <method name="getInt">
+    <arg direction="out" type="o"/>
+  </method>
+  <method name="setInt">
+    <arg direction="in" type="o"/>
+  </method>
+  <method name="getString">
+    <arg direction="out" type="s"/>
+  </method>
+  <method name="setString">
+    <arg direction="in" type="s"/>
+  </method>
+</interface>
diff --git a/droute/test.interface.Two b/droute/test.interface.Two
new file mode 100644 (file)
index 0000000..ca661ec
--- /dev/null
@@ -0,0 +1,15 @@
+<interface name="test.interface.Two">
+  <method name="null"/>
+  <method name="getInt">
+    <arg direction="out" type="o"/>
+  </method>
+  <method name="setInt">
+    <arg direction="in" type="o"/>
+  </method>
+  <method name="getString">
+    <arg direction="out" type="s"/>
+  </method>
+  <method name="setString">
+    <arg direction="in" type="s"/>
+  </method>
+</interface>