[glib/gdbus] Work some more on C object mapping



commit 909821bd1c5475ffcf5c9123f97fc7b88bdbb3aa
Author: David Zeuthen <davidz redhat com>
Date:   Sat Apr 25 23:16:05 2009 -0400

    Work some more on C object mapping
---
 docs/reference/gdbus/gdbus-docs.xml     |    2 +
 docs/reference/gdbus/gdbus-sections.txt |  122 ++
 docs/reference/gdbus/gdbus.types        |    2 +
 gdbus/Makefile.am                       |    4 +
 gdbus/gdbus.h                           |    2 +
 gdbus/gdbus.symbols                     |  108 ++-
 gdbus/gdbusprivate.c                    |  504 +++++++++
 gdbus/gdbusprivate.h                    |   59 +-
 gdbus/gdbusproxy.c                      | 1871 +++++++++++++++++++++++++++----
 gdbus/gdbusproxy.h                      |   81 +-
 gdbus/gdbusstructure.c                  |  457 ++++++++
 gdbus/gdbusstructure.h                  |  101 ++
 gdbus/gdbustypes.h                      |    2 +
 gdbus/gdbusvariant.c                    | 1575 ++++++++++++++++++++++++++
 gdbus/gdbusvariant.h                    |  201 ++++
 gdbus/tests/proxy.c                     |  784 +++++++++++++-
 gdbus/tests/sessionbus.c                |    7 +
 gdbus/tests/testserver.py               |  366 ++-----
 18 files changed, 5705 insertions(+), 543 deletions(-)

diff --git a/docs/reference/gdbus/gdbus-docs.xml b/docs/reference/gdbus/gdbus-docs.xml
index 0d31e06..860073f 100644
--- a/docs/reference/gdbus/gdbus-docs.xml
+++ b/docs/reference/gdbus/gdbus-docs.xml
@@ -32,6 +32,8 @@
     <chapter id="cmapping">
         <title>C Object Mapping</title>
         <xi:include href="xml/gdbusproxy.xml"/>
+        <xi:include href="xml/gdbusvariant.xml"/>
+        <xi:include href="xml/gdbusstructure.xml"/>
     </chapter>
   </part>
 
diff --git a/docs/reference/gdbus/gdbus-sections.txt b/docs/reference/gdbus/gdbus-sections.txt
index 18fbc4b..a4b9ead 100644
--- a/docs/reference/gdbus/gdbus-sections.txt
+++ b/docs/reference/gdbus/gdbus-sections.txt
@@ -118,13 +118,18 @@ g_bus_unown_name
 GDBusProxy
 GDBusProxyClass
 g_dbus_proxy_new
+g_dbus_proxy_new_finish
 g_dbus_proxy_get_connection
 g_dbus_proxy_get_interface_name
 g_dbus_proxy_get_name
 g_dbus_proxy_get_object_path
+g_dbus_proxy_get_cached_property
+g_dbus_proxy_set_cached_property
 g_dbus_proxy_invoke_method
 g_dbus_proxy_invoke_method_finish
 g_dbus_proxy_invoke_method_sync
+g_dbus_proxy_get_properties_loaded
+g_dbus_proxy_block_for_properties
 <SUBSECTION Standard>
 G_DBUS_PROXY
 G_IS_DBUS_PROXY
@@ -135,3 +140,120 @@ G_IS_DBUS_PROXY_CLASS
 G_DBUS_PROXY_GET_CLASS
 </SECTION>
 
+<SECTION>
+<FILE>gdbusstructure</FILE>
+<TITLE>GDBusStructure</TITLE>
+GDBusStructure
+GDBusStructureClass
+g_dbus_structure_new
+g_dbus_structure_equal
+g_dbus_structure_get_element
+g_dbus_structure_set_element
+g_dbus_structure_get_num_elements
+g_dbus_structure_get_signature
+g_dbus_structure_get_signature_for_element
+<SUBSECTION Standard>
+G_DBUS_STRUCTURE
+G_IS_DBUS_STRUCTURE
+G_TYPE_DBUS_STRUCTURE
+g_dbus_structure_get_type
+G_DBUS_STRUCTURE_CLASS
+G_IS_DBUS_STRUCTURE_CLASS
+G_DBUS_STRUCTURE_GET_CLASS
+</SECTION>
+
+<SECTION>
+<FILE>gdbusvariant</FILE>
+<TITLE>GDBusVariant</TITLE>
+GDBusVariant
+GDBusVariantClass
+g_dbus_variant_new
+g_dbus_variant_equal
+g_dbus_variant_get_variant_signature
+g_dbus_variant_new_for_string
+g_dbus_variant_new_for_byte
+g_dbus_variant_new_for_boolean
+g_dbus_variant_new_for_int16
+g_dbus_variant_new_for_uint16
+g_dbus_variant_new_for_int
+g_dbus_variant_new_for_uint
+g_dbus_variant_new_for_int64
+g_dbus_variant_new_for_uint64
+g_dbus_variant_new_for_double
+g_dbus_variant_new_for_array
+g_dbus_variant_new_for_ptr_array
+g_dbus_variant_new_for_hash_table
+g_dbus_variant_new_for_structure
+g_dbus_variant_new_for_string_array
+g_dbus_variant_new_for_object_path
+g_dbus_variant_new_for_object_path_array
+g_dbus_variant_new_for_signature
+g_dbus_variant_new_for_signature_array
+g_dbus_variant_get_string
+g_dbus_variant_get_byte
+g_dbus_variant_get_boolean
+g_dbus_variant_get_int16
+g_dbus_variant_get_int
+g_dbus_variant_get_uint
+g_dbus_variant_get_uint16
+g_dbus_variant_get_int64
+g_dbus_variant_get_uint64
+g_dbus_variant_get_double
+g_dbus_variant_get_array
+g_dbus_variant_get_ptr_array
+g_dbus_variant_get_hash_table
+g_dbus_variant_get_structure
+g_dbus_variant_get_string_array
+g_dbus_variant_get_object_path
+g_dbus_variant_get_object_path_array
+g_dbus_variant_get_signature
+g_dbus_variant_get_signature_array
+g_dbus_variant_set_string
+g_dbus_variant_set_byte
+g_dbus_variant_set_boolean
+g_dbus_variant_set_int16
+g_dbus_variant_set_uint16
+g_dbus_variant_set_int
+g_dbus_variant_set_uint
+g_dbus_variant_set_int64
+g_dbus_variant_set_uint64
+g_dbus_variant_set_double
+g_dbus_variant_set_array
+g_dbus_variant_set_ptr_array
+g_dbus_variant_set_hash_table
+g_dbus_variant_set_structure
+g_dbus_variant_set_string_array
+g_dbus_variant_set_object_path
+g_dbus_variant_set_object_path_array
+g_dbus_variant_set_signature
+g_dbus_variant_set_signature_array
+g_dbus_variant_is_unset
+g_dbus_variant_is_string
+g_dbus_variant_is_byte
+g_dbus_variant_is_boolean
+g_dbus_variant_is_int16
+g_dbus_variant_is_uint16
+g_dbus_variant_is_int
+g_dbus_variant_is_uint
+g_dbus_variant_is_int64
+g_dbus_variant_is_uint64
+g_dbus_variant_is_double
+g_dbus_variant_is_array
+g_dbus_variant_is_ptr_array
+g_dbus_variant_is_hash_table
+g_dbus_variant_is_structure
+g_dbus_variant_is_string_array
+g_dbus_variant_is_object_path
+g_dbus_variant_is_object_path_array
+g_dbus_variant_is_signature
+g_dbus_variant_is_signature_array
+<SUBSECTION Standard>
+G_DBUS_VARIANT
+G_IS_DBUS_VARIANT
+G_TYPE_DBUS_VARIANT
+g_dbus_variant_get_type
+G_DBUS_VARIANT_CLASS
+G_IS_DBUS_VARIANT_CLASS
+G_DBUS_VARIANT_GET_CLASS
+</SECTION>
+
diff --git a/docs/reference/gdbus/gdbus.types b/docs/reference/gdbus/gdbus.types
index d4f00a6..0545a3a 100644
--- a/docs/reference/gdbus/gdbus.types
+++ b/docs/reference/gdbus/gdbus.types
@@ -5,3 +5,5 @@ g_bus_type_get_type
 g_bus_name_owner_flags_get_type
 g_dbus_error_get_type
 g_dbus_proxy_get_type
+g_dbus_structure_get_type
+g_dbus_variant_get_type
diff --git a/gdbus/Makefile.am b/gdbus/Makefile.am
index bb246f1..5b5964c 100644
--- a/gdbus/Makefile.am
+++ b/gdbus/Makefile.am
@@ -84,6 +84,8 @@ gdbus_headers =				\
 	gdbusnamewatching.h		\
 	gdbusnameowning.h		\
 	gdbusproxy.h			\
+	gdbusstructure.h		\
+	gdbusvariant.h			\
 	$(NULL)
 
 libgdbus_2_0_la_SOURCES =						\
@@ -101,6 +103,8 @@ libgdbus_2_0_la_SOURCES =						\
 	gdbusnameowning.h		gdbusnameowning.c		\
 	gdbusprivate.h			gdbusprivate.c			\
 	gdbusproxy.h			gdbusproxy.c			\
+	gdbusstructure.h		gdbusstructure.c		\
+	gdbusvariant.h			gdbusvariant.c			\
 	gdbusalias.h 							\
 	gdbusaliasdef.c							\
 	$(NULL)
diff --git a/gdbus/gdbus.h b/gdbus/gdbus.h
index d4c2926..65595e4 100644
--- a/gdbus/gdbus.h
+++ b/gdbus/gdbus.h
@@ -35,6 +35,8 @@
 #include <gdbus/gdbusnamewatching.h>
 #include <gdbus/gdbusnameowning.h>
 #include <gdbus/gdbusproxy.h>
+#include <gdbus/gdbusstructure.h>
+#include <gdbus/gdbusvariant.h>
 
 #undef __G_DBUS_D_DBUS_H_INSIDE__
 
diff --git a/gdbus/gdbus.symbols b/gdbus/gdbus.symbols
index 90835c7..19c6d3b 100644
--- a/gdbus/gdbus.symbols
+++ b/gdbus/gdbus.symbols
@@ -37,6 +37,7 @@ g_dbus_connection_send_dbus_1_message_cancel
 
 #if IN_HEADER(__G_BUS_NAME_OWNER_H__)
 #if IN_FILE(__G_BUS_NAME_OWNER_C__)
+g_bus_name_owner_get_type G_GNUC_CONST
 g_bus_name_owner_new
 g_bus_name_owner_new_finish
 g_bus_name_owner_new_for_connection
@@ -45,12 +46,12 @@ g_bus_name_owner_get_connection
 g_bus_name_owner_get_flags
 g_bus_name_owner_get_name
 g_bus_name_owner_get_owns_name
-g_bus_name_owner_get_type
 #endif
 #endif
 
 #if IN_HEADER(__G_BUS_NAME_WATCHER_H__)
 #if IN_FILE(__G_BUS_NAME_WATCHER_C__)
+g_bus_name_watcher_get_type G_GNUC_CONST
 g_bus_name_watcher_new
 g_bus_name_watcher_new_finish
 g_bus_name_watcher_new_for_connection
@@ -58,7 +59,6 @@ g_bus_name_watcher_new_for_connection_finish
 g_bus_name_watcher_get_connection
 g_bus_name_watcher_get_name
 g_bus_name_watcher_get_name_owner
-g_bus_name_watcher_get_type
 #endif
 #endif
 
@@ -116,5 +116,109 @@ g_dbus_proxy_get_object_path
 g_dbus_proxy_invoke_method
 g_dbus_proxy_invoke_method_finish
 g_dbus_proxy_invoke_method_sync
+g_dbus_proxy_block_for_properties
+g_dbus_proxy_get_cached_property
+g_dbus_proxy_get_properties_loaded
+g_dbus_proxy_new_finish
+g_dbus_proxy_set_cached_property
+#endif
+#endif
+
+#if IN_HEADER(__G_DBUS_STRUCTURE_H__)
+#if IN_FILE(__G_DBUS_STRUCTURE_C__)
+g_dbus_structure_get_type G_GNUC_CONST
+g_dbus_structure_equal
+g_dbus_structure_get_element
+g_dbus_structure_get_num_elements
+g_dbus_structure_get_signature
+g_dbus_structure_get_signature_for_element
+g_dbus_structure_new
+g_dbus_structure_set_element
+#endif
+#endif
+
+#if IN_HEADER(__G_DBUS_VARIANT_H__)
+#if IN_FILE(__G_DBUS_VARIANT_C__)
+g_dbus_variant_get_type G_GNUC_CONST
+g_dbus_variant_equal
+g_dbus_variant_get_array
+g_dbus_variant_get_boolean
+g_dbus_variant_get_byte
+g_dbus_variant_get_double
+g_dbus_variant_get_hash_table
+g_dbus_variant_get_int
+g_dbus_variant_get_int16
+g_dbus_variant_get_int64
+g_dbus_variant_get_object_path
+g_dbus_variant_get_object_path_array
+g_dbus_variant_get_ptr_array
+g_dbus_variant_get_signature
+g_dbus_variant_get_signature_array
+g_dbus_variant_get_string
+g_dbus_variant_get_string_array
+g_dbus_variant_get_structure
+g_dbus_variant_get_uint
+g_dbus_variant_get_uint16
+g_dbus_variant_get_uint64
+g_dbus_variant_get_variant_signature
+g_dbus_variant_is_array
+g_dbus_variant_is_boolean
+g_dbus_variant_is_byte
+g_dbus_variant_is_double
+g_dbus_variant_is_hash_table
+g_dbus_variant_is_int
+g_dbus_variant_is_int16
+g_dbus_variant_is_int64
+g_dbus_variant_is_object_path
+g_dbus_variant_is_object_path_array
+g_dbus_variant_is_ptr_array
+g_dbus_variant_is_signature
+g_dbus_variant_is_signature_array
+g_dbus_variant_is_string
+g_dbus_variant_is_string_array
+g_dbus_variant_is_structure
+g_dbus_variant_is_uint
+g_dbus_variant_is_uint16
+g_dbus_variant_is_uint64
+g_dbus_variant_is_unset
+g_dbus_variant_new
+g_dbus_variant_new_for_array
+g_dbus_variant_new_for_boolean
+g_dbus_variant_new_for_byte
+g_dbus_variant_new_for_double
+g_dbus_variant_new_for_hash_table
+g_dbus_variant_new_for_int
+g_dbus_variant_new_for_int16
+g_dbus_variant_new_for_int64
+g_dbus_variant_new_for_object_path
+g_dbus_variant_new_for_object_path_array
+g_dbus_variant_new_for_ptr_array
+g_dbus_variant_new_for_signature
+g_dbus_variant_new_for_signature_array
+g_dbus_variant_new_for_string
+g_dbus_variant_new_for_string_array
+g_dbus_variant_new_for_structure
+g_dbus_variant_new_for_uint
+g_dbus_variant_new_for_uint16
+g_dbus_variant_new_for_uint64
+g_dbus_variant_set_array
+g_dbus_variant_set_boolean
+g_dbus_variant_set_byte
+g_dbus_variant_set_double
+g_dbus_variant_set_hash_table
+g_dbus_variant_set_int
+g_dbus_variant_set_int16
+g_dbus_variant_set_int64
+g_dbus_variant_set_object_path
+g_dbus_variant_set_object_path_array
+g_dbus_variant_set_ptr_array
+g_dbus_variant_set_signature
+g_dbus_variant_set_signature_array
+g_dbus_variant_set_string
+g_dbus_variant_set_string_array
+g_dbus_variant_set_structure
+g_dbus_variant_set_uint
+g_dbus_variant_set_uint16
+g_dbus_variant_set_uint64
 #endif
 #endif
diff --git a/gdbus/gdbusprivate.c b/gdbus/gdbusprivate.c
index f6e7d75..411e0fd 100644
--- a/gdbus/gdbusprivate.c
+++ b/gdbus/gdbusprivate.c
@@ -23,10 +23,14 @@
 #include "config.h"
 
 #include <stdlib.h>
+#include <string.h>
 
 #include <glib/gi18n.h>
 
 #include "gdbustypes.h"
+#include "gdbusprivate.h"
+#include "gdbusvariant.h"
+#include "gdbusstructure.h"
 
 void
 _g_dbus_oom (void)
@@ -35,3 +39,503 @@ _g_dbus_oom (void)
   g_error ("OOM from libdbus");
   abort ();
 }
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/**
+ * _gdbus_signature_vararg_foreach:
+ * @signature: A D-Bus signature of zero or more single complete types.
+ * @first_type: First #GType to match up.
+ * @va_args: The value of the first in argument, followed by zero or more (type, value) pairs, followed
+ * by #G_TYPE_INVALID.
+ * @callback: Callback function.
+ * @user_data: User data to pass to @callback.
+ *
+ * Splits up @signature into single complete types, matches each single complete type with
+ * the #GType<!-- -->s and values from @first_type, @va_args and invokes @callback for each
+ * matching pair.
+ *
+ * Returns: %TRUE if @callback short-circuited the matching, %FALSE otherwise.
+ */
+gboolean
+_gdbus_signature_vararg_foreach (const gchar                         *signature,
+                                 GType                                first_type,
+                                 va_list                              va_args,
+                                 _GDBusSignatureVarargForeachCallback callback,
+                                 gpointer                             user_data)
+{
+  DBusSignatureIter iter;
+  gchar *element_signature;
+  gboolean ret;
+  GType gtype;
+  guint arg_num;
+
+  g_assert (dbus_signature_validate (signature, NULL));
+
+  ret = FALSE;
+
+  if (strlen (signature) == 0)
+      goto out;
+
+  g_assert (first_type != G_TYPE_INVALID);
+
+  dbus_signature_iter_init (&iter, signature);
+
+  gtype = first_type;
+  arg_num = 0;
+  do
+    {
+      if (arg_num > 0)
+        gtype = va_arg (va_args, GType);
+
+      element_signature = dbus_signature_iter_get_signature (&iter);
+
+      ret = callback (arg_num,
+                      element_signature,
+                      gtype,
+                      va_args,
+                      user_data);
+      dbus_free (element_signature);
+
+      if (ret)
+        goto out;
+
+      arg_num++;
+    }
+  while (dbus_signature_iter_next (&iter));
+
+ out:
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+gboolean
+_g_strv_equal (gchar **strv1,
+               gchar **strv2)
+{
+  guint n;
+
+  if (strv1 == NULL && strv2 == NULL)
+    return TRUE;
+  if (strv1 == NULL || strv2 == NULL)
+    return FALSE;
+
+  for (n = 0; strv1[n] != NULL && strv2[n] != NULL; n++)
+    {
+      if (g_strcmp0 (strv1[n], strv2[n]) != 0)
+        return FALSE;
+    }
+  if (strv1[n] != NULL || strv2[n] != NULL)
+    return FALSE;
+
+  return TRUE;
+}
+
+gboolean
+_g_array_equal (GArray *array1,
+                GArray *array2)
+{
+  if (array1 == NULL && array2 == NULL)
+    return TRUE;
+  if (array1 == NULL || array2 == NULL)
+    return FALSE;
+
+  if (array1->len != array2->len)
+    return FALSE;
+
+  if (array1->len == 0)
+    return TRUE;
+
+  if (g_array_get_element_size (array1) != g_array_get_element_size (array2))
+    return FALSE;
+
+  return memcmp (array1->data, array2->data, g_array_get_element_size (array1) * array1->len) == 0;
+}
+
+static gboolean
+values_equal (gpointer     value1,
+              gpointer     value2,
+              const gchar *signature)
+{
+  gboolean ret;
+
+  ret = FALSE;
+  switch (signature[0])
+    {
+    case DBUS_TYPE_BYTE:
+    case DBUS_TYPE_BOOLEAN:
+    case DBUS_TYPE_INT16:
+    case DBUS_TYPE_UINT16:
+    case DBUS_TYPE_INT32:
+    case DBUS_TYPE_UINT32:
+      ret = (value1 == value2);
+      break;
+
+    case DBUS_TYPE_OBJECT_PATH:
+    case DBUS_TYPE_SIGNATURE:
+    case DBUS_TYPE_STRING:
+      ret = (strcmp (value1, value2) == 0);
+      break;
+
+    case DBUS_TYPE_INT64:
+    case DBUS_TYPE_UINT64:
+      ret = (*((const gint64*) value1) == *((const gint64*) value2));
+      break;
+
+    case DBUS_TYPE_DOUBLE:
+      ret = (*((const gdouble*) value1) == *((const gdouble*) value2));
+      break;
+
+    case DBUS_TYPE_VARIANT:
+      ret = g_dbus_variant_equal (value1, value2);
+      break;
+
+    case DBUS_STRUCT_BEGIN_CHAR:
+      ret = g_dbus_structure_equal (value1, value2);
+      break;
+
+    case DBUS_TYPE_ARRAY:
+      switch (signature[1])
+        {
+        case DBUS_TYPE_BYTE:
+        case DBUS_TYPE_BOOLEAN:
+        case DBUS_TYPE_INT16:
+        case DBUS_TYPE_UINT16:
+        case DBUS_TYPE_INT32:
+        case DBUS_TYPE_UINT32:
+        case DBUS_TYPE_INT64:
+        case DBUS_TYPE_UINT64:
+        case DBUS_TYPE_DOUBLE:
+          ret = _g_array_equal (value1, value2);
+          break;
+
+        case DBUS_TYPE_OBJECT_PATH:
+        case DBUS_TYPE_SIGNATURE:
+        case DBUS_TYPE_STRING:
+          ret = _g_strv_equal (value1, value2);
+          break;
+
+        case DBUS_STRUCT_BEGIN_CHAR:
+        case DBUS_TYPE_VARIANT:
+          ret = _g_ptr_array_equal (value1, value2, signature + 1);
+          break;
+
+        case DBUS_DICT_ENTRY_BEGIN_CHAR:
+          {
+            char key_sig[2];
+            char *val_sig;
+
+            /* keys are guaranteed by the D-Bus spec to be primitive types */
+            key_sig[0] = signature[2];
+            key_sig[1] = '\0';
+            val_sig = g_strdup (signature + 3);
+            val_sig[strlen (val_sig) - 1] = '\0';
+
+            ret = _g_hash_table_equal (value1, value2, key_sig, val_sig);
+            g_free (val_sig);
+          }
+          break;
+
+        default:
+          g_assert_not_reached ();
+          break;
+        }
+      break;
+
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+
+  return ret;
+}
+
+gboolean
+_g_ptr_array_equal (GPtrArray *array1,
+                    GPtrArray *array2,
+                    const gchar *element_signature)
+{
+  gboolean ret;
+  guint n;
+
+  if (array1 == NULL && array2 == NULL)
+    return TRUE;
+  if (array1 == NULL || array2 == NULL)
+    return FALSE;
+
+  if (array1->len != array2->len)
+    return FALSE;
+
+  if (array1->len == 0)
+    return TRUE;
+
+  ret = FALSE;
+
+  for (n = 0; n < array1->len; n++)
+    {
+      gpointer value1 = array1->pdata[n];
+      gpointer value2 = array2->pdata[n];
+
+      if (!values_equal (value1, value2, element_signature))
+        goto out;
+
+    }
+
+  ret = TRUE;
+
+ out:
+  return ret;
+}
+
+gboolean
+_g_hash_table_equal (GHashTable *hash1,
+                     GHashTable *hash2,
+                     const gchar *key_signature,
+                     const gchar *value_signature)
+{
+  gboolean ret;
+  GHashTableIter iter;
+  gpointer key1;
+  gpointer value1;
+  gpointer value2;
+
+  if (hash1 == NULL && hash2 == NULL)
+    return TRUE;
+  if (hash1 == NULL || hash2 == NULL)
+    return FALSE;
+
+  if (g_hash_table_size (hash1) != g_hash_table_size (hash2))
+    return FALSE;
+
+  if (g_hash_table_size (hash1) == 0)
+    return TRUE;
+
+  ret = FALSE;
+
+  g_hash_table_iter_init (&iter, hash1);
+  while (g_hash_table_iter_next (&iter, &key1, &value1))
+    {
+      /* this takes care of comparing the keys (relying on the hashes being set up correctly) */
+      if (!g_hash_table_lookup_extended (hash2, key1, NULL, &value2))
+        goto out;
+
+      if (!values_equal (value1, value2, value_signature))
+        goto out;
+    }
+
+  ret = TRUE;
+
+ out:
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/* this equal func only works for GValue instances used by GDBus */
+gboolean
+_g_dbus_gvalue_equal (const GValue *value1,
+                      const GValue *value2,
+                      const gchar  *signature)
+{
+  GType type;
+  gboolean ret;
+
+  if (value1 == NULL && value2 == NULL)
+    return TRUE;
+  if (value1 == NULL || value2 == NULL)
+    return FALSE;
+
+  if (G_VALUE_TYPE (value1) != G_VALUE_TYPE (value2))
+    return FALSE;
+
+  ret = FALSE;
+  type = G_VALUE_TYPE (value1);
+  switch (type)
+    {
+    case G_TYPE_UCHAR:
+      ret = (g_value_get_uchar (value1) == g_value_get_uchar (value2));
+      break;
+
+    case G_TYPE_BOOLEAN:
+      ret = (g_value_get_boolean (value1) == g_value_get_boolean (value2));
+      break;
+
+    case G_TYPE_INT: // TODO:16
+      ret = (g_value_get_int (value1) == g_value_get_int (value2));
+      break;
+
+    case G_TYPE_UINT:
+      ret = (g_value_get_uint (value1) == g_value_get_uint (value2));
+      break;
+
+    case G_TYPE_INT64:
+      ret = (g_value_get_int64 (value1) == g_value_get_int64 (value2));
+      break;
+
+    case G_TYPE_UINT64:
+      ret = (g_value_get_uint64 (value1) == g_value_get_uint64 (value2));
+      break;
+
+    case G_TYPE_DOUBLE:
+      ret = (g_value_get_double (value1) == g_value_get_double (value2));
+      break;
+
+    case G_TYPE_STRING: // TODO:o TODO:g
+      ret = (g_strcmp0 (g_value_get_string (value1), g_value_get_string (value2)) == 0);
+      break;
+
+    default:
+      if (type == G_TYPE_STRV) // TODO:ao TODO:ag
+        ret = _g_strv_equal (g_value_get_boxed (value1),
+                             g_value_get_boxed (value2));
+      else if (type == G_TYPE_ARRAY)
+        ret = _g_array_equal (g_value_get_boxed (value1),
+                              g_value_get_boxed (value2));
+      else if (type == G_TYPE_PTR_ARRAY)
+        ret = _g_ptr_array_equal (g_value_get_boxed (value1),
+                                  g_value_get_boxed (value2),
+                                  signature + 1);
+      else if (type == G_TYPE_HASH_TABLE)
+        {
+          char key_sig[2];
+          char *val_sig;
+
+          /* keys are guaranteed by the D-Bus spec to be primitive types */
+          key_sig[0] = signature[2];
+          key_sig[1] = '\0';
+          val_sig = g_strdup (signature + 3);
+          val_sig[strlen (val_sig) - 1] = '\0';
+
+          ret = _g_hash_table_equal (g_value_get_boxed (value1),
+                                     g_value_get_boxed (value2),
+                                     key_sig,
+                                     val_sig);
+          g_free (val_sig);
+        }
+      else if (type == G_TYPE_DBUS_STRUCTURE)
+        {
+          ret = g_dbus_structure_equal (g_value_get_object (value1),
+                                        g_value_get_object (value2));
+        }
+      else
+        g_assert_not_reached ();
+      break;
+    }
+
+  return ret;
+}
+
+/**
+ * _g_dbus_signature_get_gtype:
+ * @signature: A D-Bus signature
+ *
+ * Returns the #GType to use for @signature.
+ *
+ * Returns: A #GType.
+ **/
+GType
+_g_dbus_signature_get_gtype (const gchar *signature)
+{
+  GType ret;
+
+  ret = G_TYPE_INVALID;
+
+  switch (signature[0])
+    {
+    case DBUS_TYPE_BYTE:
+      ret = G_TYPE_UCHAR;
+      break;
+
+    case DBUS_TYPE_BOOLEAN:
+      ret = G_TYPE_BOOLEAN;
+      break;
+
+    case DBUS_TYPE_INT16:
+      ret = G_TYPE_INT; // TODO:16
+      break;
+
+    case DBUS_TYPE_UINT16:
+      ret = G_TYPE_UINT; // TODO:16
+      break;
+
+    case DBUS_TYPE_INT32:
+      ret = G_TYPE_INT;
+      break;
+
+    case DBUS_TYPE_UINT32:
+      ret = G_TYPE_UINT;
+      break;
+
+    case DBUS_TYPE_INT64:
+      ret = G_TYPE_INT64;
+      break;
+
+    case DBUS_TYPE_UINT64:
+      ret = G_TYPE_UINT64;
+      break;
+
+    case DBUS_TYPE_DOUBLE:
+      ret = G_TYPE_DOUBLE;
+      break;
+
+    case DBUS_TYPE_VARIANT:
+      ret = G_TYPE_DBUS_VARIANT;
+      break;
+
+    case DBUS_STRUCT_BEGIN_CHAR:
+      ret = G_TYPE_DBUS_STRUCTURE;
+      break;
+
+    case DBUS_TYPE_OBJECT_PATH: // TODO:o
+    case DBUS_TYPE_SIGNATURE: // TODO:g
+    case DBUS_TYPE_STRING:
+      ret = G_TYPE_STRING;
+      break;
+
+    case DBUS_TYPE_ARRAY:
+      switch (signature[1])
+        {
+        case DBUS_TYPE_BYTE:
+        case DBUS_TYPE_BOOLEAN:
+        case DBUS_TYPE_INT16:
+        case DBUS_TYPE_UINT16:
+        case DBUS_TYPE_INT32:
+        case DBUS_TYPE_UINT32:
+        case DBUS_TYPE_INT64:
+        case DBUS_TYPE_UINT64:
+        case DBUS_TYPE_DOUBLE:
+          ret = G_TYPE_ARRAY;
+          break;
+
+        case DBUS_TYPE_OBJECT_PATH: // TODO:ao
+        case DBUS_TYPE_SIGNATURE: // TODO:ag
+        case DBUS_TYPE_STRING:
+          ret = G_TYPE_STRV;
+          break;
+
+        case DBUS_STRUCT_BEGIN_CHAR:
+        case DBUS_TYPE_VARIANT:
+        case DBUS_TYPE_ARRAY:
+          ret = G_TYPE_PTR_ARRAY;
+          break;
+
+        case DBUS_DICT_ENTRY_BEGIN_CHAR:
+          ret = G_TYPE_HASH_TABLE;
+          break;
+
+        default:
+          g_warning ("Failed determining GType for D-Bus array with signature '%s'", signature);
+          g_assert_not_reached ();
+          break;
+        }
+      break;
+
+    default:
+      g_warning ("Failed determining GType for D-Bus signature '%s'", signature);
+      g_assert_not_reached ();
+      break;
+    }
+
+  return ret;
+}
diff --git a/gdbus/gdbusprivate.h b/gdbus/gdbusprivate.h
index 90d1278..e3bb462 100644
--- a/gdbus/gdbusprivate.h
+++ b/gdbus/gdbusprivate.h
@@ -28,11 +28,68 @@
 #define __G_DBUS_PRIVATE_H__
 
 #include <gdbus/gdbustypes.h>
+#include <stdarg.h>
 
 G_BEGIN_DECLS
 
 void _g_dbus_oom (void);
 
+/**
+ * _GDBusSignatureVarargForeachCallback:
+ * @arg_num: The argument number currently being processed.
+ * @signature: Signature for single complete D-Bus type.
+ * @type: The #GType corresponding to @signature.
+ * @va_args: A #va_list you can use va_arg() on to extract the value.
+ * @user_data: User data passed to _gdbus_signature_vararg_foreach().
+ *
+ * Callback function used in _gdbus_signature_vararg_foreach().
+ *
+ * Returns: %TRUE to short-circuit iteration, %FALSE to continue iterating.
+ */
+typedef gboolean (*_GDBusSignatureVarargForeachCallback) (guint        arg_num,
+                                                          const gchar *signature,
+                                                          GType        type,
+                                                          va_list      va_args,
+                                                          gpointer     user_data);
+
+gboolean _gdbus_signature_vararg_foreach (const gchar                         *signature,
+                                          GType                                first_type,
+                                          va_list                              va_args,
+                                          _GDBusSignatureVarargForeachCallback callback,
+                                          gpointer                             user_data);
+
+GDBusStructure *_g_dbus_structure_new_for_values (const gchar *signature,
+                                                  guint        num_elements,
+                                                  GValue      *elements);
+
+const GValue   *_g_dbus_structure_get_gvalue_for_element     (GDBusStructure  *structure,
+                                                              guint            element);
+
+const GValue *_g_dbus_variant_get_gvalue (GDBusVariant *variant);
+
+GDBusVariant *_g_dbus_variant_new_for_gvalue (const GValue *value, const gchar *signature);
+
+gboolean _g_strv_equal (gchar **strv1,
+                        gchar **strv2);
+
+gboolean _g_array_equal (GArray *array1,
+                         GArray *array2);
+
+gboolean _g_ptr_array_equal (GPtrArray *array1,
+                             GPtrArray *array2,
+                             const gchar *element_signature);
+
+gboolean _g_hash_table_equal (GHashTable *hash1,
+                              GHashTable *hash2,
+                              const gchar *key_signature,
+                              const gchar *value_signature);
+
+gboolean _g_dbus_gvalue_equal (const GValue *value1,
+                               const GValue *value2,
+                               const gchar  *signature);
+
+GType _g_dbus_signature_get_gtype (const gchar *signature);
+
 G_END_DECLS
 
-#endif /* __G_DBUS_TYPES_H__ */
+#endif /* __G_DBUS_PRIVATE_H__ */
diff --git a/gdbus/gdbusproxy.c b/gdbus/gdbusproxy.c
index fd1d092..4f043c9 100644
--- a/gdbus/gdbusproxy.c
+++ b/gdbus/gdbusproxy.c
@@ -23,23 +23,26 @@
 #include "config.h"
 
 #include <stdlib.h>
-
 #include <glib/gi18n.h>
+#include <gobject/gvaluecollector.h>
 
 #include "gdbusproxy.h"
 #include "gdbusenumtypes.h"
 #include "gdbusconnection.h"
 #include "gdbuserror.h"
+#include "gdbusstructure.h"
+#include "gdbusvariant.h"
 #include "gdbusprivate.h"
 
 #include "gdbusalias.h"
 
 /**
  * SECTION:gdbusproxy
- * @short_description: Proxy Class
+ * @short_description: Base class for proxies
  * @include: gdbus/gdbus.h
  *
- * #GDBusProxy is a...
+ * #GDBusProxy is a base class used for proxies to access a D-Bus
+ * interface on a remote object.
  */
 
 struct _GDBusProxyPrivate
@@ -48,6 +51,9 @@ struct _GDBusProxyPrivate
   gchar *name;
   gchar *object_path;
   gchar *interface_name;
+  gboolean properties_loaded;
+  guint property_loading_pending_call_id;
+  GHashTable *properties;
 };
 
 enum
@@ -57,6 +63,7 @@ enum
   PROP_G_DBUS_PROXY_NAME,
   PROP_G_DBUS_PROXY_OBJECT_PATH,
   PROP_G_DBUS_PROXY_INTERFACE_NAME,
+  PROP_G_DBUS_PROXY_PROPERTIES_LOADED,
 };
 
 G_DEFINE_TYPE (GDBusProxy, g_dbus_proxy, G_TYPE_OBJECT);
@@ -70,6 +77,8 @@ g_dbus_proxy_finalize (GObject *object)
   g_free (proxy->priv->name);
   g_free (proxy->priv->object_path);
   g_free (proxy->priv->interface_name);
+  if (proxy->priv->properties != NULL)
+    g_hash_table_unref (proxy->priv->properties);
 
   if (G_OBJECT_CLASS (g_dbus_proxy_parent_class)->finalize != NULL)
     G_OBJECT_CLASS (g_dbus_proxy_parent_class)->finalize (object);
@@ -101,6 +110,10 @@ g_dbus_proxy_get_property (GObject    *object,
       g_value_set_string (value, proxy->priv->interface_name);
       break;
 
+    case PROP_G_DBUS_PROXY_PROPERTIES_LOADED:
+      g_value_set_boolean (value, proxy->priv->properties_loaded);
+      break;
+
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -234,6 +247,22 @@ g_dbus_proxy_class_init (GDBusProxyClass *klass)
                                                         G_PARAM_STATIC_BLURB |
                                                         G_PARAM_STATIC_NICK));
 
+  /**
+   * GDBusProxy:g-dbus-proxy-properties-loaded:
+   *
+   * Whether D-Bus properties has been acquired.
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_G_DBUS_PROXY_PROPERTIES_LOADED,
+                                   g_param_spec_boolean ("g-dbus-proxy-properties-loaded",
+                                                         _("g-dbus-proxy-properties-loaded"),
+                                                         _("Whether D-Bus properties has been acquired"),
+                                                         FALSE,
+                                                         G_PARAM_READABLE |
+                                                         G_PARAM_STATIC_NAME |
+                                                         G_PARAM_STATIC_BLURB |
+                                                         G_PARAM_STATIC_NICK));
+
   g_type_class_add_private (klass, sizeof (GDBusProxyPrivate));
 }
 
@@ -246,14 +275,193 @@ g_dbus_proxy_init (GDBusProxy *proxy)
 /* ---------------------------------------------------------------------------------------------------- */
 
 /**
+ * g_dbus_proxy_get_cached_property:
+ * @proxy: A #GDBusProxy.
+ * @property_name: Property name.
+ * @error: Return location for error.
+ *
+ * Looks up the value for a property from the cache. This call does no
+ * blocking IO.
+ *
+ * Returns: A #GDBusVariant that holds the value for @property_name or
+ * %NULL if @error is set. Free with g_object_unref().
+ **/
+GDBusVariant *
+g_dbus_proxy_get_cached_property (GDBusProxy          *proxy,
+                                  const gchar         *property_name,
+                                  GError             **error)
+{
+  GDBusVariant *variant;
+
+  g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL);
+  g_return_val_if_fail (property_name != NULL, NULL);
+
+  variant = NULL;
+
+  if (!proxy->priv->properties_loaded)
+    {
+      g_set_error (error,
+                   G_DBUS_ERROR,
+                   G_DBUS_ERROR_FAILED,
+                   _("Properties are not loaded"));
+      goto out;
+    }
+
+  if (proxy->priv->properties == NULL)
+    {
+      g_set_error (error,
+                   G_DBUS_ERROR,
+                   G_DBUS_ERROR_FAILED,
+                   _("Failed loading properties"));
+      goto out;
+    }
+
+  variant = g_hash_table_lookup (proxy->priv->properties, property_name);
+  if (variant == NULL)
+    {
+      g_set_error (error,
+                   G_DBUS_ERROR,
+                   G_DBUS_ERROR_FAILED,
+                   _("No property with name %s"),
+                   property_name);
+      goto out;
+    }
+
+  g_object_ref (variant);
+
+ out:
+
+  return variant;
+}
+
+/**
+ * g_dbus_proxy_set_cached_property:
+ * @proxy: A #GDBusProxy.
+ * @property_name: Property name.
+ * @variant: The value to set.
+ * @error: Return location for error.
+ *
+ * Updates the local cache with the value for @property_name. This
+ * call does no blocking IO.
+ *
+ * Returns: %TRUE if the property was updated, %FALSE if @error is set.
+ **/
+gboolean
+g_dbus_proxy_set_cached_property (GDBusProxy          *proxy,
+                                  const gchar         *property_name,
+                                  GDBusVariant        *variant,
+                                  GError             **error)
+{
+  GDBusVariant *existing_variant;
+  gboolean ret;
+
+  g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), FALSE);
+  g_return_val_if_fail (property_name != NULL, FALSE);
+  g_return_val_if_fail (variant != NULL, FALSE);
+
+  ret = FALSE;
+
+  if (!proxy->priv->properties_loaded)
+    {
+      g_set_error (error,
+                   G_DBUS_ERROR,
+                   G_DBUS_ERROR_FAILED,
+                   _("Cannot set property %s (properties are not loaded)"),
+                   property_name);
+      goto out;
+    }
+
+  if (proxy->priv->properties == NULL)
+    {
+      g_set_error (error,
+                   G_DBUS_ERROR,
+                   G_DBUS_ERROR_FAILED,
+                   _("Cannot set property %s (properties are not loaded and the attempt to load them failed)"),
+                   property_name);
+      goto out;
+    }
+
+  existing_variant = g_hash_table_lookup (proxy->priv->properties, property_name);
+  if (existing_variant == NULL)
+    {
+      g_set_error (error,
+                   G_DBUS_ERROR,
+                   G_DBUS_ERROR_FAILED,
+                   _("There is no existing property with name %s"),
+                   property_name);
+      goto out;
+    }
+
+  if (g_strcmp0 (g_dbus_variant_get_variant_signature (existing_variant),
+                 g_dbus_variant_get_variant_signature (variant)) != 0)
+    {
+      g_set_error (error,
+                   G_DBUS_ERROR,
+                   G_DBUS_ERROR_FAILED,
+                   _("Existing value for property %s has signature %s and value to set has signature %s"),
+                   property_name,
+                   g_dbus_variant_get_variant_signature (existing_variant),
+                   g_dbus_variant_get_variant_signature (variant));
+      goto out;
+    }
+
+  g_hash_table_insert (proxy->priv->properties,
+                       g_strdup (property_name),
+                       g_object_ref (variant));
+
+  ret = TRUE;
+
+ out:
+
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+get_all_cb (GDBusProxy   *proxy,
+            GAsyncResult *res,
+            gpointer      user_data)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
+  GError *error;
+
+  error = NULL;
+  if (!g_dbus_proxy_invoke_method_finish (proxy,
+                                          "a{sv}",
+                                          res,
+                                          &error,
+                                          G_TYPE_HASH_TABLE, &proxy->priv->properties,
+                                          G_TYPE_INVALID))
+    {
+      g_simple_async_result_set_from_error (simple, error);
+      g_error_free (error);
+      proxy->priv->properties = NULL;
+    }
+
+  proxy->priv->properties_loaded = TRUE;
+  proxy->priv->property_loading_pending_call_id = 0;
+  g_object_notify (G_OBJECT (proxy), "g-dbus-proxy-properties-loaded");
+
+  g_simple_async_result_complete (simple);
+}
+
+/**
  * g_dbus_proxy_new:
  * @connection: A #GDBusConnection.
  * @name: A bus name.
  * @object_path: An object path.
  * @interface_name: A D-Bus interface name.
+ * @cancellable: A #GCancellable or %NULL.
+ * @callback: The callback function to invoke when finished attempting to load properties or %NULL.
+ * @user_data: User data to pass to @callback.
  *
  * Creates a proxy for accessing @interface_name on the remote at
- * @object_path owned by @name at @connection.
+ * @object_path owned by @name at @connection and starts loading
+ * the D-Bus properties for the remote object.
+ *
+ * Once the D-Bus properties are loaded, @callback will be invoked
+ * and you can call g_dbus_proxy_new_finish() to check the result.
  *
  * Returns: A #GDBusProxy. Free with g_object_unref().
  **/
@@ -261,11 +469,13 @@ GDBusProxy *
 g_dbus_proxy_new (GDBusConnection     *connection,
                   const gchar         *name,
                   const gchar         *object_path,
-                  const gchar         *interface_name)
+                  const gchar         *interface_name,
+                  GCancellable        *cancellable,
+                  GAsyncReadyCallback  callback,
+                  gpointer             user_data)
 {
   GDBusProxy *proxy;
-
-  /* TODO: need to take a GAsyncReadyCallback for when properties are loaded */
+  GSimpleAsyncResult *simple;
 
   g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
   g_return_val_if_fail (name != NULL, NULL);
@@ -279,8 +489,49 @@ g_dbus_proxy_new (GDBusConnection     *connection,
                                       "g-dbus-proxy-interface-name", interface_name,
                                       NULL));
 
+  simple = g_simple_async_result_new (G_OBJECT (connection),
+                                      callback,
+                                      user_data,
+                                      g_dbus_proxy_new_finish);
+
+  proxy->priv->property_loading_pending_call_id = g_dbus_proxy_invoke_method (proxy,
+                                                                              "org.freedesktop.DBus.Properties.GetAll",
+                                                                              "s",
+                                                                              -1,
+                                                                              cancellable,
+                                                                              (GAsyncReadyCallback) get_all_cb,
+                                                                              simple,
+                                                                              G_TYPE_STRING,
+                                                                              proxy->priv->interface_name,
+                                                                              G_TYPE_INVALID);
+
   return proxy;
 }
+
+/**
+ * g_dbus_proxy_new_finish:
+ * @proxy: A #GDBusProxy.
+ * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback function passed to g_dbus_proxy_new().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes loading properties for @proxy.
+ *
+ * Returns: %TRUE if properties was successfully loaded, otherwise %FALSE with @error set.
+ **/
+gboolean
+g_dbus_proxy_new_finish (GDBusProxy         *proxy,
+                         GAsyncResult       *res,
+                         GError            **error)
+{
+  GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
+  g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), FALSE);
+  g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_dbus_proxy_new_finish);
+  if (g_simple_async_result_propagate_error (simple, error))
+    return FALSE;
+  else
+    return proxy->priv->properties_loaded;
+}
+
 /* ---------------------------------------------------------------------------------------------------- */
 
 /**
@@ -343,289 +594,1448 @@ g_dbus_proxy_get_interface_name (GDBusProxy *proxy)
   return proxy->priv->interface_name;
 }
 
-/* ---------------------------------------------------------------------------------------------------- */
-
 /**
- * ForeachCallback:
- * @signature: Signature for single complete D-Bus type.
- * @type: The #GType corresponding to @signature.
- * @va_args: A #va_list you can use va_arg() on to extract the value.
+ * g_dbus_proxy_get_properties_loaded:
+ * @proxy: A #GDBusProxy.
  *
- * Callback function used in foreach_signature_and_gtype().
+ * Gets whether properties has been loaded.
  *
- * Returns: %TRUE to short-circuit iteration, %FALSE to continue iterating.
- */
-typedef gboolean (*ForeachCallback) (guint        arg_num,
-                                     const gchar *signature,
-                                     GType        type,
-                                     va_list      va_args,
-                                     gpointer     user_data);
+ * To track changes, listen for notifications on the
+ * #GDBusProxy:g-dbus-proxy-properties-loaded property.
+ *
+ * Returns: %TRUE if properties are available, %FALSE otherwise.
+ **/
+gboolean
+g_dbus_proxy_get_properties_loaded (GDBusProxy *proxy)
+{
+  g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), FALSE);
+  return proxy->priv->properties_loaded;
+}
 
 /**
- * foreach_signature_and_gtype:
- * @signature: A D-Bus signature of zero or more single complete types.
- * @first_type: First #GType to match up.
- * @va_args: The value of the first in argument, followed by zero or more (type, value) pairs, followed
- * by #G_TYPE_INVALID.
- * @callback: Callback function.
- * @user_data: User data to pass to @callback.
- *
- * Splits up @signature into single complete types, matches each single complete type with
- * the #GType<!-- -->s and values from @first_type, @va_args and invokes @callback for each
- * matching pair.
+ * g_dbus_proxy_block_for_properties:
+ * @proxy: A #GDBusProxy.
  *
- * Returns: %TRUE if @callback short-circuited the matching, %FALSE otherwise.
- */
-static gboolean
-foreach_signature_and_gtype (const gchar    *signature,
-                             GType           first_type,
-                             va_list         va_args,
-                             ForeachCallback callback,
-                             gpointer        user_data)
+ * If the attempt to load properties is done, does nothing. Otherwise blocks (not
+ * in the mainloop), until the attempt to load properties is done.
+ **/
+void
+g_dbus_proxy_block_for_properties (GDBusProxy *proxy)
 {
-  DBusSignatureIter iter;
-  gchar *element_signature;
-  gboolean ret;
-  GType gtype;
-  guint arg_num;
-
-  g_assert (dbus_signature_validate (signature, NULL));
+  g_return_if_fail (G_IS_DBUS_PROXY (proxy));
 
-  ret = FALSE;
+  if (G_LIKELY (proxy->priv->properties_loaded))
+    return;
 
-  if (strlen (signature) == 0)
-      goto out;
-
-  g_assert (first_type != G_TYPE_INVALID);
-
-  dbus_signature_iter_init (&iter, signature);
-
-  gtype = first_type;
-  arg_num = 0;
-  do
+  if (proxy->priv->property_loading_pending_call_id != 0)
     {
-      if (arg_num > 0)
-        gtype = va_arg (va_args, GType);
-
-      element_signature = dbus_signature_iter_get_signature (&iter);
+      g_dbus_connection_send_dbus_1_message_block (proxy->priv->connection,
+                                                   proxy->priv->property_loading_pending_call_id);
+    }
+}
 
-      ret = callback (arg_num,
-                      element_signature,
-                      gtype,
-                      va_args,
-                      user_data);
-      dbus_free (element_signature);
+/* ---------------------------------------------------------------------------------------------------- */
 
-      if (ret)
-        goto out;
+static guint
+get_element_size (gchar dbus_type_code)
+{
+  guint ret;
 
-      arg_num++;
+  switch (dbus_type_code)
+    {
+    case DBUS_TYPE_BYTE:
+      ret = 1;
+      break;
+    case DBUS_TYPE_BOOLEAN:
+      ret = sizeof (gboolean);
+      break;
+    case DBUS_TYPE_INT16:
+    case DBUS_TYPE_UINT16:
+      ret = sizeof (gint16);
+      break;
+    case DBUS_TYPE_INT32:
+    case DBUS_TYPE_UINT32:
+      ret = sizeof (gint);
+      break;
+    case DBUS_TYPE_INT64:
+    case DBUS_TYPE_UINT64:
+      ret = sizeof (gint64);
+      break;
+    case DBUS_TYPE_DOUBLE:
+      ret = sizeof (gdouble);
+      break;
+    default:
+      g_assert_not_reached ();
     }
-  while (dbus_signature_iter_next (&iter));
 
- out:
   return ret;
 }
 
-/* ---------------------------------------------------------------------------------------------------- */
-
 static gboolean
-append_values_cb (guint        arg_num,
-                  const gchar *signature,
-                  GType        type,
-                  va_list      va_args,
-                  gpointer     user_data)
+get_value_from_iter (DBusMessageIter *iter,
+                     GValue          *out_value,
+                     GError         **error)
 {
-  DBusMessageIter *iter = user_data;
-  guchar           v_byte;
-  dbus_uint16_t    v_uint16;
-  dbus_uint32_t    v_uint32;
-  dbus_uint64_t    v_uint64;
-  gdouble          v_double;
-  dbus_bool_t      v_boolean;
-  const gchar     *v_string;
+  int arg_type;
+  int array_arg_type;
+  dbus_bool_t bool_val;
+  const char *str_val;
+  guchar uint8_val;
+  dbus_int16_t int16_val;
+  dbus_uint16_t uint16_val;
+  dbus_int32_t int32_val;
+  dbus_uint32_t uint32_val;
+  dbus_int64_t int64_val;
+  dbus_uint64_t uint64_val;
+  double double_val;
+  gboolean ret;
 
-  switch (signature[0])
+  /* TODO: - fill in @error instead of using g_assert_not_reached() */
+
+  g_return_val_if_fail (iter != NULL, FALSE);
+  g_return_val_if_fail (out_value != NULL, FALSE);
+
+  ret = FALSE;
+
+  arg_type = dbus_message_iter_get_arg_type (iter);
+
+  switch (arg_type)
     {
-    case DBUS_TYPE_BYTE:
-      if (type == G_TYPE_UCHAR || type == G_TYPE_CHAR)
-        v_byte = va_arg (va_args, guint);
-      else
-        goto mismatch;
-      dbus_message_iter_append_basic (iter, signature[0], &v_byte);
+    case DBUS_TYPE_STRING:
+      g_value_init (out_value, G_TYPE_STRING);
+      dbus_message_iter_get_basic (iter, &str_val);
+      g_value_set_string (out_value, str_val);
+      break;
+
+    case DBUS_TYPE_OBJECT_PATH:
+      g_value_init (out_value, G_TYPE_STRING); // TODO:o
+      dbus_message_iter_get_basic (iter, &str_val);
+      g_value_set_string (out_value, str_val);
+      break;
+
+    case DBUS_TYPE_SIGNATURE:
+      g_value_init (out_value, G_TYPE_STRING); // TODO:g
+      dbus_message_iter_get_basic (iter, &str_val);
+      g_value_set_string (out_value, str_val);
       break;
 
     case DBUS_TYPE_BOOLEAN:
-      if (type == G_TYPE_BOOLEAN)
-        v_boolean = va_arg (va_args, gboolean);
-      else
-        goto mismatch;
-      dbus_message_iter_append_basic (iter, signature[0], &v_boolean);
+      g_value_init (out_value, G_TYPE_BOOLEAN);
+      dbus_message_iter_get_basic (iter, &bool_val);
+      g_value_set_boolean (out_value, bool_val);
+      break;
+
+    case DBUS_TYPE_BYTE:
+      g_value_init (out_value, G_TYPE_UCHAR);
+      dbus_message_iter_get_basic (iter, &uint8_val);
+      g_value_set_uchar (out_value, uint8_val);
       break;
 
     case DBUS_TYPE_INT16:
+      g_value_init (out_value, G_TYPE_INT); // TODO:16
+      dbus_message_iter_get_basic (iter, &int16_val);
+      g_value_set_int (out_value, int16_val);
+      break;
+
     case DBUS_TYPE_UINT16:
-      if (type == G_TYPE_INT || type == G_TYPE_UINT)
-        v_uint16 = va_arg (va_args, guint);
-      else
-        goto mismatch;
-      dbus_message_iter_append_basic (iter, signature[0], &v_uint16);
+      g_value_init (out_value, G_TYPE_UINT); // TODO:16
+      dbus_message_iter_get_basic (iter, &uint16_val);
+      g_value_set_uint (out_value, uint16_val);
       break;
 
     case DBUS_TYPE_INT32:
+      g_value_init (out_value, G_TYPE_INT);
+      dbus_message_iter_get_basic (iter, &int32_val);
+      g_value_set_int (out_value, int32_val);
+      break;
+
     case DBUS_TYPE_UINT32:
-      if (type == G_TYPE_INT || type == G_TYPE_UINT)
-        v_uint32 = va_arg (va_args, guint);
-      else
-        goto mismatch;
-      dbus_message_iter_append_basic (iter, signature[0], &v_uint32);
+      g_value_init (out_value, G_TYPE_UINT);
+      dbus_message_iter_get_basic (iter, &uint32_val);
+      g_value_set_uint (out_value, uint32_val);
       break;
 
     case DBUS_TYPE_INT64:
+      g_value_init (out_value, G_TYPE_INT64);
+      dbus_message_iter_get_basic (iter, &int64_val);
+      g_value_set_int64 (out_value, int64_val);
+      break;
+
     case DBUS_TYPE_UINT64:
-      if (type == G_TYPE_INT64 || type == G_TYPE_UINT64)
-        v_uint64 = va_arg (va_args, guint64);
-      else
-        goto mismatch;
-      dbus_message_iter_append_basic (iter, signature[0], &v_uint64);
+      g_value_init (out_value, G_TYPE_UINT64);
+      dbus_message_iter_get_basic (iter, &uint64_val);
+      g_value_set_uint64 (out_value, uint64_val);
       break;
 
     case DBUS_TYPE_DOUBLE:
-      if (type == G_TYPE_DOUBLE)
-        v_double = va_arg (va_args, gdouble);
-      else
-        goto mismatch;
-      dbus_message_iter_append_basic (iter, signature[0], &v_double);
+      g_value_init (out_value, G_TYPE_DOUBLE);
+      dbus_message_iter_get_basic (iter, &double_val);
+      g_value_set_double (out_value, double_val);
       break;
 
-    case DBUS_TYPE_STRING:
-    case DBUS_TYPE_OBJECT_PATH:
-    case DBUS_TYPE_SIGNATURE:
-      if (type == G_TYPE_STRING)
-        v_string = (gchar *) va_arg (va_args, gchar*);
+    case DBUS_TYPE_STRUCT:
+      {
+        DBusMessageIter struct_iter;
+        char *struct_signature;
+        GDBusStructure *structure;
+        GArray *a;
+
+        struct_signature = dbus_message_iter_get_signature (iter);
+
+        a = g_array_new (FALSE,
+                         TRUE,
+                         sizeof (GValue));
+
+        dbus_message_iter_recurse (iter, &struct_iter);
+
+        /* now collect all the elements in the structure as GValue objects */
+        while (dbus_message_iter_get_arg_type (&struct_iter) != DBUS_TYPE_INVALID)
+          {
+            GValue *value;
+
+            g_array_set_size (a, a->len + 1);
+            value = &g_array_index (a, GValue, a->len - 1);
+
+            /* doing the recursive dance! */
+            if (!get_value_from_iter (&struct_iter, value, error))
+              goto out;
+
+            dbus_message_iter_next (&struct_iter);
+          }
+
+        /* takes ownership of elems */
+        structure = _g_dbus_structure_new_for_values (struct_signature,
+                                                      a->len,
+                                                      (GValue *) a->data);
+        g_assert (structure != NULL);
+
+        g_array_free (a, FALSE);
+
+        g_value_init (out_value, G_TYPE_DBUS_STRUCTURE);
+        g_value_take_object (out_value, structure);
+
+        dbus_free (struct_signature);
+      }
+      break;
+
+    case DBUS_TYPE_ARRAY:
+      array_arg_type = dbus_message_iter_get_element_type (iter);
+      if (array_arg_type == DBUS_TYPE_STRING ||
+          array_arg_type == DBUS_TYPE_OBJECT_PATH ||
+          array_arg_type == DBUS_TYPE_SIGNATURE)
+        {
+          GPtrArray *p;
+          DBusMessageIter array_iter;
+          GType boxed_type;
+
+          if (array_arg_type == DBUS_TYPE_STRING)
+            boxed_type = G_TYPE_STRV;
+          else if (array_arg_type == DBUS_TYPE_OBJECT_PATH)
+            boxed_type = G_TYPE_STRV; // TODO:o EGG_DBUS_TYPE_OBJECT_PATH_ARRAY;
+          else if (array_arg_type == DBUS_TYPE_SIGNATURE)
+            boxed_type = G_TYPE_STRV; // TODO:g EGG_DBUS_TYPE_SIGNATURE_ARRAY;
+          else
+            g_assert_not_reached ();
+
+          dbus_message_iter_recurse (iter, &array_iter);
+          p = g_ptr_array_new ();
+          while (dbus_message_iter_get_arg_type (&array_iter) != DBUS_TYPE_INVALID)
+            {
+              dbus_message_iter_get_basic (&array_iter, &str_val);
+              g_ptr_array_add (p, (void *) g_strdup (str_val));
+              dbus_message_iter_next (&array_iter);
+            }
+          g_ptr_array_add (p, NULL);
+          g_value_init (out_value, boxed_type);
+          g_value_take_boxed (out_value, p->pdata);
+          g_ptr_array_free (p, FALSE);
+        }
+      else if (array_arg_type == DBUS_TYPE_BYTE ||
+               array_arg_type == DBUS_TYPE_INT16 ||
+               array_arg_type == DBUS_TYPE_UINT16 ||
+               array_arg_type == DBUS_TYPE_INT32 ||
+               array_arg_type == DBUS_TYPE_UINT32 ||
+               array_arg_type == DBUS_TYPE_INT64 ||
+               array_arg_type == DBUS_TYPE_UINT64 ||
+               array_arg_type == DBUS_TYPE_BOOLEAN ||
+               array_arg_type == DBUS_TYPE_DOUBLE)
+        {
+          GArray *a;
+          DBusMessageIter array_iter;
+          gconstpointer data;
+          gint num_items;
+
+          dbus_message_iter_recurse (iter, &array_iter);
+          dbus_message_iter_get_fixed_array (&array_iter,
+                                             &data,
+                                             (int*) &num_items);
+          a = g_array_sized_new (FALSE, FALSE, get_element_size (array_arg_type), num_items);
+          g_array_append_vals (a, data, num_items);
+          g_value_init (out_value, G_TYPE_ARRAY);
+          g_value_take_boxed (out_value, a);
+        }
+      else if (array_arg_type == DBUS_TYPE_STRUCT)
+        {
+          DBusMessageIter array_iter;
+          char *struct_signature;
+          GPtrArray *p;
+
+          p = g_ptr_array_new_with_free_func (g_object_unref);
+
+          dbus_message_iter_recurse (iter, &array_iter);
+
+          struct_signature = dbus_message_iter_get_signature (&array_iter);
+
+          /* now collect all the elements in the structure.
+           */
+          while (dbus_message_iter_get_arg_type (&array_iter) != DBUS_TYPE_INVALID)
+            {
+              GValue elem_value = {0};
+
+              /* recurse */
+              if (!get_value_from_iter (&array_iter, &elem_value, error))
+                {
+                  dbus_free (struct_signature);
+                  g_ptr_array_unref (p);
+                  goto out;
+                }
+
+              g_ptr_array_add (p, g_value_get_object (&elem_value));
+
+              dbus_message_iter_next (&array_iter);
+            }
+
+          g_value_init (out_value, G_TYPE_PTR_ARRAY);
+          g_value_take_boxed (out_value, p);
+
+          dbus_free (struct_signature);
+        }
+      else if (array_arg_type == DBUS_TYPE_DICT_ENTRY)
+        {
+          DBusMessageIter array_iter;
+          GHashTable *hash;
+          char key_sig[2];
+          char *val_sig;
+          char *array_sig;
+          GHashFunc hash_func;
+          GEqualFunc equal_func;
+          GDestroyNotify key_free_func;
+          GDestroyNotify val_free_func;
+
+          dbus_message_iter_recurse (iter, &array_iter);
+
+          array_sig = dbus_message_iter_get_signature (&array_iter);
+
+          /* keys are guaranteed by the D-Bus spec to be primitive types */
+          key_sig[0] = array_sig[1];
+          key_sig[1] = '\0';
+          val_sig = g_strdup (array_sig + 2);
+          val_sig[strlen (val_sig) - 1] = '\0';
+
+          /* set up the hash table */
+
+          switch (key_sig[0])
+            {
+            case DBUS_TYPE_BOOLEAN:
+            case DBUS_TYPE_BYTE:
+            case DBUS_TYPE_INT16:
+            case DBUS_TYPE_UINT16:
+            case DBUS_TYPE_INT32:
+            case DBUS_TYPE_UINT32:
+              key_free_func = NULL;
+              hash_func = g_direct_hash;
+              equal_func = g_direct_equal;
+              break;
+
+            case DBUS_TYPE_INT64:
+            case DBUS_TYPE_UINT64:
+              hash_func = g_int64_hash;
+              equal_func = g_int64_equal;
+              key_free_func = g_free;
+              break;
+
+            case DBUS_TYPE_DOUBLE:
+              hash_func = g_double_hash;
+              equal_func = g_double_equal;
+              key_free_func = g_free;
+              break;
+
+            case DBUS_TYPE_STRING:
+            case DBUS_TYPE_OBJECT_PATH:
+            case DBUS_TYPE_SIGNATURE:
+              hash_func = g_str_hash;
+              equal_func = g_str_equal;
+              key_free_func = g_free;
+              break;
+
+            default:
+              g_assert_not_reached ();
+              break;
+            }
+
+          switch (val_sig[0])
+            {
+            case DBUS_TYPE_BOOLEAN:
+            case DBUS_TYPE_BYTE:
+            case DBUS_TYPE_INT16:
+            case DBUS_TYPE_UINT16:
+            case DBUS_TYPE_INT32:
+            case DBUS_TYPE_UINT32:
+              val_free_func = NULL;
+              break;
+
+            case DBUS_TYPE_INT64:
+            case DBUS_TYPE_UINT64:
+            case DBUS_TYPE_DOUBLE:
+            case DBUS_TYPE_STRING:
+            case DBUS_TYPE_OBJECT_PATH:
+            case DBUS_TYPE_SIGNATURE:
+              val_free_func = g_free;
+              break;
+
+            case DBUS_TYPE_ARRAY:
+              switch (val_sig[1])
+                {
+                case DBUS_TYPE_STRING:
+                case DBUS_TYPE_OBJECT_PATH:
+                case DBUS_TYPE_SIGNATURE:
+                  val_free_func = (GDestroyNotify) g_strfreev;
+                  break;
+
+                case DBUS_TYPE_BOOLEAN:
+                case DBUS_TYPE_BYTE:
+                case DBUS_TYPE_INT16:
+                case DBUS_TYPE_UINT16:
+                case DBUS_TYPE_INT32:
+                case DBUS_TYPE_UINT32:
+                case DBUS_TYPE_INT64:
+                case DBUS_TYPE_UINT64:
+                case DBUS_TYPE_DOUBLE:
+                  val_free_func = (GDestroyNotify) g_array_unref;
+                  break;
+
+                case DBUS_STRUCT_BEGIN_CHAR:
+                case DBUS_DICT_ENTRY_BEGIN_CHAR:
+                  val_free_func = (GDestroyNotify) g_ptr_array_unref;
+                  break;
+
+                case DBUS_TYPE_VARIANT:
+                  val_free_func = (GDestroyNotify) g_ptr_array_unref;
+                  break;
+
+                case DBUS_TYPE_ARRAY:
+                  val_free_func = (GDestroyNotify) g_ptr_array_unref;
+                  break;
+
+                default:
+                  g_assert_not_reached ();
+                  break;
+                }
+              break;
+
+            case DBUS_STRUCT_BEGIN_CHAR:
+              val_free_func = g_object_unref;
+              break;
+
+            case DBUS_TYPE_VARIANT:
+              val_free_func = g_object_unref;
+              break;
+
+            default:
+              g_assert_not_reached ();
+              break;
+            }
+
+          hash = g_hash_table_new_full (hash_func,
+                                        equal_func,
+                                        key_free_func,
+                                        val_free_func);
+
+          while (dbus_message_iter_get_arg_type (&array_iter) != DBUS_TYPE_INVALID)
+            {
+              DBusMessageIter hash_iter;
+              gpointer key, value;
+              const char *str_val;
+
+              dbus_message_iter_recurse (&array_iter, &hash_iter);
+
+              switch (key_sig[0])
+                {
+                case DBUS_TYPE_BOOLEAN:
+                  dbus_message_iter_get_basic (&hash_iter, &bool_val);
+                  key = GINT_TO_POINTER (bool_val);
+                  break;
+
+                case DBUS_TYPE_BYTE:
+                  dbus_message_iter_get_basic (&hash_iter, &uint8_val);
+                  key = GINT_TO_POINTER (uint8_val);
+                  break;
+
+                case DBUS_TYPE_INT16:
+                  dbus_message_iter_get_basic (&hash_iter, &int16_val);
+                  key = GINT_TO_POINTER (int16_val);
+                  break;
+
+                case DBUS_TYPE_UINT16:
+                  dbus_message_iter_get_basic (&hash_iter, &uint16_val);
+                  key = GINT_TO_POINTER (uint16_val);
+                  break;
+
+                case DBUS_TYPE_INT32:
+                  dbus_message_iter_get_basic (&hash_iter, &int32_val);
+                  key = GINT_TO_POINTER (int32_val);
+                  break;
+
+                case DBUS_TYPE_UINT32:
+                  dbus_message_iter_get_basic (&hash_iter, &uint32_val);
+                  key = GINT_TO_POINTER (uint32_val);
+                  break;
+
+                case DBUS_TYPE_INT64:
+                  dbus_message_iter_get_basic (&hash_iter, &int64_val);
+                  key = g_memdup (&int64_val, sizeof (gint64));
+                  break;
+
+                case DBUS_TYPE_UINT64:
+                  dbus_message_iter_get_basic (&hash_iter, &uint64_val);
+                  key = g_memdup (&uint64_val, sizeof (guint64));
+                  break;
+
+                case DBUS_TYPE_DOUBLE:
+                  dbus_message_iter_get_basic (&hash_iter, &double_val);
+                  key = g_memdup (&double_val, sizeof (gdouble));
+                  break;
+
+                case DBUS_TYPE_STRING:
+                  dbus_message_iter_get_basic (&hash_iter, &str_val);
+                  key = g_strdup (str_val);
+                  break;
+
+                case DBUS_TYPE_OBJECT_PATH:
+                  dbus_message_iter_get_basic (&hash_iter, &str_val);
+                  key = g_strdup (str_val);
+                  break;
+
+                case DBUS_TYPE_SIGNATURE:
+                  dbus_message_iter_get_basic (&hash_iter, &str_val);
+                  key = g_strdup (str_val);
+                  break;
+
+                default:
+                  g_assert_not_reached ();
+                  break;
+                }
+
+              dbus_message_iter_next (&hash_iter);
+
+              switch (val_sig[0])
+                {
+                case DBUS_TYPE_BOOLEAN:
+                  dbus_message_iter_get_basic (&hash_iter, &bool_val);
+                  value = GINT_TO_POINTER (bool_val);
+                  break;
+
+                case DBUS_TYPE_BYTE:
+                  dbus_message_iter_get_basic (&hash_iter, &uint8_val);
+                  value = GINT_TO_POINTER (uint8_val);
+                  break;
+
+                case DBUS_TYPE_INT16:
+                  dbus_message_iter_get_basic (&hash_iter, &int16_val);
+                  value = GINT_TO_POINTER (int16_val);
+                  break;
+
+                case DBUS_TYPE_UINT16:
+                  dbus_message_iter_get_basic (&hash_iter, &uint16_val);
+                  value = GINT_TO_POINTER (uint16_val);
+                  break;
+
+                case DBUS_TYPE_INT32:
+                  dbus_message_iter_get_basic (&hash_iter, &int32_val);
+                  value = GINT_TO_POINTER (int32_val);
+                  break;
+
+                case DBUS_TYPE_INT64:
+                  dbus_message_iter_get_basic (&hash_iter, &int64_val);
+                  value = g_memdup (&int64_val, sizeof (gint64));
+                  break;
+
+                case DBUS_TYPE_UINT64:
+                  dbus_message_iter_get_basic (&hash_iter, &uint64_val);
+                  value = g_memdup (&uint64_val, sizeof (guint64));
+                  break;
+
+                case DBUS_TYPE_DOUBLE:
+                  dbus_message_iter_get_basic (&hash_iter, &double_val);
+                  value = g_memdup (&double_val, sizeof (gdouble));
+                  break;
+
+                case DBUS_TYPE_UINT32:
+                  dbus_message_iter_get_basic (&hash_iter, &uint32_val);
+                  value = GINT_TO_POINTER (uint32_val);
+                  break;
+
+                case DBUS_TYPE_STRING:
+                  dbus_message_iter_get_basic (&hash_iter, &str_val);
+                  value = g_strdup (str_val);
+                  break;
+
+                case DBUS_TYPE_OBJECT_PATH:
+                  dbus_message_iter_get_basic (&hash_iter, &str_val);
+                  value = g_strdup (str_val);
+                  break;
+
+                case DBUS_TYPE_SIGNATURE:
+                  dbus_message_iter_get_basic (&hash_iter, &str_val);
+                  value = g_strdup (str_val);
+                  break;
+
+                case DBUS_TYPE_ARRAY:
+                  {
+                    GValue array_val = {0};
+                    /* recurse */
+                    if (!get_value_from_iter (&hash_iter, &array_val, error))
+                      goto out;
+                    switch (val_sig[1])
+                      {
+                      case DBUS_TYPE_BOOLEAN:
+                      case DBUS_TYPE_BYTE:
+                      case DBUS_TYPE_INT16:
+                      case DBUS_TYPE_UINT16:
+                      case DBUS_TYPE_INT32:
+                      case DBUS_TYPE_UINT32:
+                      case DBUS_TYPE_INT64:
+                      case DBUS_TYPE_UINT64:
+                      case DBUS_TYPE_DOUBLE:
+                      case DBUS_STRUCT_BEGIN_CHAR:
+                      case DBUS_DICT_ENTRY_BEGIN_CHAR:
+                      case DBUS_TYPE_STRING:
+                      case DBUS_TYPE_OBJECT_PATH:
+                      case DBUS_TYPE_SIGNATURE:
+                      case DBUS_TYPE_VARIANT:
+                      case DBUS_TYPE_ARRAY:
+                        value = g_value_get_boxed (&array_val);
+                        break;
+
+                      default:
+                        g_assert_not_reached ();
+                        break;
+                      }
+                  }
+                  break;
+
+                case DBUS_STRUCT_BEGIN_CHAR:
+                  {
+                    GValue object_val = {0};
+                    /* recurse */
+                    if (!get_value_from_iter (&hash_iter, &object_val, error))
+                      goto out;
+                    value = g_value_get_object (&object_val);
+                  }
+                  break;
+
+                case DBUS_TYPE_VARIANT:
+                  {
+                    GValue object_val = {0};
+                    /* recurse */
+                    if (!get_value_from_iter (&hash_iter, &object_val, error))
+                      goto out;
+                    value = g_value_get_object (&object_val);
+                  }
+                  break;
+
+                default:
+                  g_assert_not_reached ();
+                  break;
+                }
+
+              g_hash_table_insert (hash, key, value);
+              dbus_message_iter_next (&array_iter);
+            }
+
+          g_value_init (out_value, G_TYPE_HASH_TABLE);
+          g_value_take_boxed (out_value, hash);
+
+          dbus_free (array_sig);
+          g_free (val_sig);
+
+        }
+      else if (array_arg_type == DBUS_TYPE_ARRAY)
+        {
+          GPtrArray *p;
+          DBusMessageIter array_iter;
+          GDestroyNotify elem_free_func;
+
+          /* handling array of arrays, e.g.
+           *
+           * - aas:    GPtrArray of GStrv (gchar **)
+           * - aao:    GPtrArray of GStrv (gchar **) TODO:o
+           * - aao:    GPtrArray of GStrv (gchar **) TODO:g
+           * - aa{ss}: GPtrArray of GHashTable
+           * - aai:    GPtrArray of GArray
+           * - aa(ii): GPtrArray of GPtrArray containg GObject-derived type
+           * - aaas:   GPtrArray of GPtrArray of GStrv (gchar **)
+           *
+           */
+
+          dbus_message_iter_recurse (iter, &array_iter);
+
+          elem_free_func = NULL;
+
+          p = g_ptr_array_new ();
+          while (dbus_message_iter_get_arg_type (&array_iter) != DBUS_TYPE_INVALID)
+            {
+              GValue elem_val = {0};
+
+              /* recurse */
+              if (!get_value_from_iter (&array_iter, &elem_val, error))
+                goto out;
+
+              if (G_VALUE_TYPE (&elem_val) == G_TYPE_STRV)
+                {
+                  g_ptr_array_add (p, g_value_get_boxed (&elem_val));
+                  elem_free_func = (GDestroyNotify) g_strfreev;
+                }
+              else if (G_VALUE_TYPE (&elem_val) == G_TYPE_ARRAY)
+                {
+                  g_ptr_array_add (p, g_value_get_boxed (&elem_val));
+                  elem_free_func = (GDestroyNotify) g_array_unref;
+                }
+              else if (G_VALUE_TYPE (&elem_val) == G_TYPE_PTR_ARRAY)
+                {
+                  g_ptr_array_add (p, g_value_get_boxed (&elem_val));
+                  elem_free_func = (GDestroyNotify) g_ptr_array_unref;
+                }
+              else if (G_VALUE_TYPE (&elem_val) == G_TYPE_HASH_TABLE)
+                {
+                  g_ptr_array_add (p, g_value_get_boxed (&elem_val));
+                  elem_free_func = (GDestroyNotify) g_hash_table_unref;
+                }
+              else
+                {
+                  g_assert_not_reached ();
+                }
+
+              dbus_message_iter_next (&array_iter);
+            } /* for all elements in array */
+          g_ptr_array_set_free_func (p, elem_free_func);
+          g_value_init (out_value, G_TYPE_PTR_ARRAY);
+          g_value_take_boxed (out_value, p);
+        }
+      else if (array_arg_type == DBUS_TYPE_VARIANT)
+        {
+          GPtrArray *p;
+          DBusMessageIter array_iter;
+          char *elem_signature;
+
+          /* array of variants */
+
+          dbus_message_iter_recurse (iter, &array_iter);
+
+          elem_signature = dbus_message_iter_get_signature (&array_iter);
+
+          p = g_ptr_array_new_with_free_func (g_object_unref);
+          while (dbus_message_iter_get_arg_type (&array_iter) != DBUS_TYPE_INVALID)
+            {
+              GValue elem_val = {0};
+
+              /* recurse */
+              if (!get_value_from_iter (&array_iter, &elem_val, error))
+                goto out;
+
+              g_ptr_array_add (p, g_value_get_object (&elem_val));
+
+              dbus_message_iter_next (&array_iter);
+            } /* for all elements in array */
+
+          g_value_init (out_value, G_TYPE_PTR_ARRAY);
+          g_value_take_boxed (out_value, p);
+
+          dbus_free (elem_signature);
+        }
       else
-        goto mismatch;
-      dbus_message_iter_append_basic (iter, signature[0], &v_string);
+        {
+          char *signature;
+          signature = dbus_message_iter_get_signature (iter);
+          g_set_error (error,
+                       G_DBUS_ERROR,
+                       G_DBUS_ERROR_FAILED,
+                       "Cannot decode message with array of signature %s", signature);
+          g_free (signature);
+          goto out;
+        }
+      break;
+
+    case DBUS_TYPE_VARIANT:
+      {
+        GValue variant_val = {0};
+        GDBusVariant *variant;
+        DBusMessageIter variant_iter;
+        gchar *variant_signature;
+
+        dbus_message_iter_recurse (iter, &variant_iter);
+
+        variant_signature = dbus_message_iter_get_signature (&variant_iter);
+
+        /* recurse */
+        if (!get_value_from_iter (&variant_iter, &variant_val, error))
+          goto out;
+
+        variant = _g_dbus_variant_new_for_gvalue (&variant_val, variant_signature);
+
+        g_value_init (out_value, G_TYPE_DBUS_VARIANT);
+        g_value_take_object (out_value, variant);
+
+        dbus_free (variant_signature);
+      }
       break;
 
     default:
-      g_warning ("Don't know how to append D-Bus type-code %c while processing in-arg %d",
-                 signature[0],
-                 arg_num);
-      goto error;
+      {
+        char *signature;
+        signature = dbus_message_iter_get_signature (iter);
+        g_set_error (error,
+                     G_DBUS_ERROR,
+                     G_DBUS_ERROR_FAILED,
+                     "Cannot decode message with signature %s", signature);
+        dbus_free (signature);
+        goto out;
+      }
+      break;
     }
 
-  return FALSE;
+  ret = TRUE;
+
+ out:
 
- mismatch:
-  g_warning ("D-Bus signature %s and GType %s are not compatible for in-arg %d",
-             signature,
-             g_type_name (type),
-             arg_num);
- error:
-  return TRUE;
+  return ret;
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
 
 static gboolean
-extract_values_cb (guint        arg_num,
-                   const gchar *signature,
-                   GType        type,
-                   va_list      va_args,
-                   gpointer     user_data)
+append_value_to_iter (DBusMessageIter              *iter,
+                      const gchar                  *signature,
+                      const GValue                 *value,
+                      GError                      **error)
 {
-  DBusMessageIter *iter = user_data;
-  guchar           v_byte;
-  dbus_uint16_t    v_uint16;
-  dbus_uint32_t    v_uint32;
-  dbus_uint64_t    v_uint64;
-  gdouble          v_double;
-  dbus_bool_t      v_boolean;
-  const gchar     *v_string;
-  gpointer         p;
+  gboolean ret;
+
+  /* TODO: - fill in @error instead of using g_assert_not_reached()
+   *       - validate object paths and signatures (to shield app developers
+   *         from getting disconnected from the bus)
+   *       - perhaps access GValue directly (trading off safety for performance)
+   */
+
+  //g_debug ("_append_value_to_iter: signature=%s -> value=%s", signature, g_strdup_value_contents (value));
 
+  ret = FALSE;
+
+  /* TODO: we could probably speed this up by accessing the GValue directly */
   switch (signature[0])
     {
+    case DBUS_TYPE_STRING:
+      {
+        const char *val = g_value_get_string (value);
+        dbus_message_iter_append_basic (iter, DBUS_TYPE_STRING, &val);
+      }
+      break;
+
+    case DBUS_TYPE_OBJECT_PATH:
+      {
+        const char *val = g_value_get_string (value); // TODO:o
+        dbus_message_iter_append_basic (iter, DBUS_TYPE_OBJECT_PATH, &val);
+      }
+      break;
+
+    case DBUS_TYPE_SIGNATURE:
+      {
+        const char *val = g_value_get_string (value); // TODO:g
+        dbus_message_iter_append_basic (iter, DBUS_TYPE_SIGNATURE, &val);
+      }
+      break;
+
     case DBUS_TYPE_BYTE:
-      if (type != G_TYPE_UCHAR && type != G_TYPE_CHAR)
-        goto mismatch;
-      dbus_message_iter_get_basic (iter, &v_byte);
-      p = va_arg (va_args, gpointer);
-      *((guchar *) p) = v_byte;
+      {
+        guchar val = g_value_get_uchar (value);
+        dbus_message_iter_append_basic (iter, DBUS_TYPE_BYTE, &val);
+      }
       break;
 
     case DBUS_TYPE_BOOLEAN:
-      if (type != G_TYPE_BOOLEAN)
-        goto mismatch;
-      dbus_message_iter_get_basic (iter, &v_boolean);
-      p = va_arg (va_args, gpointer);
-      *((gboolean *) p) = v_boolean;
+      {
+        dbus_bool_t val = g_value_get_boolean (value);
+        dbus_message_iter_append_basic (iter, DBUS_TYPE_BOOLEAN, &val);
+      }
       break;
 
     case DBUS_TYPE_INT16:
+      {
+        dbus_int16_t val = g_value_get_int (value); // TODO:16
+        dbus_message_iter_append_basic (iter, DBUS_TYPE_INT16, &val);
+      }
+      break;
+
     case DBUS_TYPE_UINT16:
-      if (type != G_TYPE_INT && type != G_TYPE_UINT)
-        goto mismatch;
-      dbus_message_iter_get_basic (iter, &v_uint16);
-      p = va_arg (va_args, gpointer);
-      *((guint *) p) = v_uint16;
+      {
+        dbus_uint16_t val = g_value_get_uint (value); // TODO:16
+        dbus_message_iter_append_basic (iter, DBUS_TYPE_UINT16, &val);
+      }
       break;
 
     case DBUS_TYPE_INT32:
+      {
+        dbus_int32_t val = g_value_get_int (value);
+        dbus_message_iter_append_basic (iter, DBUS_TYPE_INT32, &val);
+      }
+      break;
+
     case DBUS_TYPE_UINT32:
-      if (type != G_TYPE_INT && type != G_TYPE_UINT)
-        goto mismatch;
-      dbus_message_iter_get_basic (iter, &v_uint32);
-      p = va_arg (va_args, gpointer);
-      *((guint *) p) = v_uint32;
+      {
+        dbus_uint32_t val;
+        if (value->g_type == G_TYPE_UINT)
+          {
+            val = g_value_get_uint (value);
+          }
+        else if (G_TYPE_IS_ENUM (value->g_type))
+          {
+            val = g_value_get_enum (value);
+          }
+        else if (G_TYPE_IS_FLAGS (value->g_type))
+          {
+            val = g_value_get_flags (value);
+          }
+        dbus_message_iter_append_basic (iter, DBUS_TYPE_UINT32, &val);
+      }
       break;
 
     case DBUS_TYPE_INT64:
+      {
+        dbus_int64_t val = g_value_get_int64 (value);
+        dbus_message_iter_append_basic (iter, DBUS_TYPE_INT64, &val);
+      }
+      break;
+
     case DBUS_TYPE_UINT64:
-      if (type != G_TYPE_INT64 && type != G_TYPE_UINT64)
-        goto mismatch;
-      dbus_message_iter_get_basic (iter, &v_uint64);
-      p = va_arg (va_args, gpointer);
-      *((guint64 *) p) = v_uint64;
+      {
+        dbus_uint64_t val = g_value_get_uint64 (value);
+        dbus_message_iter_append_basic (iter, DBUS_TYPE_UINT64, &val);
+      }
       break;
 
     case DBUS_TYPE_DOUBLE:
-      if (type != G_TYPE_DOUBLE)
-        goto mismatch;
-      dbus_message_iter_get_basic (iter, &v_double);
-      p = va_arg (va_args, gpointer);
-      *((gdouble *) p) = v_double;
+      {
+        double val = g_value_get_double (value);
+        dbus_message_iter_append_basic (iter, DBUS_TYPE_DOUBLE, &val);
+      }
       break;
 
-    case DBUS_TYPE_STRING:
-    case DBUS_TYPE_OBJECT_PATH:
-    case DBUS_TYPE_SIGNATURE:
-      if (type != G_TYPE_STRING)
-        goto mismatch;
-      dbus_message_iter_get_basic (iter, &v_string);
-      p = va_arg (va_args, gpointer);
-      *((gchar **) p) = g_strdup (v_string);
+    /* ---------------------------------------------------------------------------------------------------- */
+    case DBUS_TYPE_ARRAY:
+      if (signature[1] == DBUS_TYPE_BYTE   ||
+          signature[1] == DBUS_TYPE_INT16  ||
+          signature[1] == DBUS_TYPE_UINT16 ||
+          signature[1] == DBUS_TYPE_INT32  ||
+          signature[1] == DBUS_TYPE_UINT32 ||
+          signature[1] == DBUS_TYPE_INT64  ||
+          signature[1] == DBUS_TYPE_UINT64 ||
+          signature[1] == DBUS_TYPE_DOUBLE ||
+          signature[1] == DBUS_TYPE_BOOLEAN)
+        {
+          DBusMessageIter array_iter;
+          GArray *a;
+
+          a = (GArray *) g_value_get_boxed (value);
+
+          if (a != NULL && a->len > 0 && get_element_size (signature[1]) != g_array_get_element_size (a))
+            {
+              g_set_error (error,
+                           G_DBUS_ERROR,
+                           G_DBUS_ERROR_FAILED,
+                           "GArray has element size %d but element size %d was expected",
+                           get_element_size (signature[1]),
+                           g_array_get_element_size (a));
+              goto out;
+            }
+
+          dbus_message_iter_open_container (iter, DBUS_TYPE_ARRAY, signature + 1, &array_iter);
+          dbus_message_iter_append_fixed_array (&array_iter, signature[1], &(a->data), a->len);
+          dbus_message_iter_close_container (iter, &array_iter);
+        }
+      else if (signature[1] == DBUS_TYPE_STRING ||
+               signature[1] == DBUS_TYPE_OBJECT_PATH ||
+               signature[1] == DBUS_TYPE_SIGNATURE)
+        {
+          DBusMessageIter array_iter;
+          gchar **strv;
+          guint n;
+
+          strv = (gchar **) g_value_get_boxed (value);
+          dbus_message_iter_open_container (iter, DBUS_TYPE_ARRAY, signature + 1, &array_iter);
+          for (n = 0; strv[n] != NULL; n++)
+            dbus_message_iter_append_basic (&array_iter, signature[1], &(strv[n]));
+          dbus_message_iter_close_container (iter, &array_iter);
+        }
+      else if (signature[1] == DBUS_STRUCT_BEGIN_CHAR)
+        {
+          DBusMessageIter array_iter;
+          GPtrArray *p;
+          guint n;
+
+          p = g_value_get_boxed (value);
+          dbus_message_iter_open_container (iter, DBUS_TYPE_ARRAY, signature + 1, &array_iter);
+          for (n = 0; n < p->len; n++)
+            {
+              GValue val = {0};
+              g_value_init (&val, G_TYPE_DBUS_STRUCTURE);
+              g_value_take_object (&val, G_DBUS_STRUCTURE (p->pdata[n]));
+              /* recursive dance */
+              if (!append_value_to_iter (&array_iter, signature + 1, &val, error))
+                goto out;
+            }
+          dbus_message_iter_close_container (iter, &array_iter);
+        }
+      else if (signature[1] == DBUS_DICT_ENTRY_BEGIN_CHAR)
+        {
+          DBusMessageIter array_iter;
+          GHashTable *hash_table;
+          GHashTableIter hash_iter;
+          gpointer hash_key;
+          gpointer hash_value;
+          char *value_signature;
+
+          value_signature = g_strdup (signature + 3);
+          value_signature[strlen (value_signature) - 1] = '\0';
+
+          hash_table = g_value_get_boxed (value);
+
+          //g_debug ("signature='%s' value_signature='%s'", signature, value_signature);
+
+          dbus_message_iter_open_container (iter, DBUS_TYPE_ARRAY, signature + 1, &array_iter);
+          g_hash_table_iter_init (&hash_iter, hash_table);
+          while (g_hash_table_iter_next (&hash_iter, &hash_key, &hash_value))
+            {
+              GValue val = {0};
+              DBusMessageIter dict_iter;
+
+              dbus_message_iter_open_container (&array_iter,
+                                                DBUS_TYPE_DICT_ENTRY,
+                                                NULL,
+                                                &dict_iter);
+              switch (signature[2])
+                {
+                case DBUS_TYPE_STRING:
+                case DBUS_TYPE_OBJECT_PATH:
+                case DBUS_TYPE_SIGNATURE:
+                case DBUS_TYPE_BYTE:
+                case DBUS_TYPE_BOOLEAN:
+                case DBUS_TYPE_INT16:
+                case DBUS_TYPE_UINT16:
+                case DBUS_TYPE_INT32:
+                case DBUS_TYPE_UINT32:
+                  dbus_message_iter_append_basic (&dict_iter, signature[2], &hash_key);
+                  break;
+
+                case DBUS_TYPE_INT64:
+                case DBUS_TYPE_UINT64:
+                case DBUS_TYPE_DOUBLE:
+                  dbus_message_iter_append_basic (&dict_iter, signature[2], hash_key);
+                  break;
+
+                default:
+                  /* the D-Bus spec says the hash_key must be a basic type */
+                  g_assert_not_reached ();
+                  break;
+                }
+
+              //g_debug ("value_signature='%s'", value_signature);
+              switch (value_signature[0])
+                {
+                case DBUS_TYPE_STRING:
+                case DBUS_TYPE_OBJECT_PATH:
+                case DBUS_TYPE_SIGNATURE:
+                case DBUS_TYPE_BYTE:
+                case DBUS_TYPE_BOOLEAN:
+                case DBUS_TYPE_INT16:
+                case DBUS_TYPE_UINT16:
+                case DBUS_TYPE_INT32:
+                case DBUS_TYPE_UINT32:
+                  dbus_message_iter_append_basic (&dict_iter, signature[2], &hash_value);
+                  break;
+
+                case DBUS_TYPE_INT64:
+                case DBUS_TYPE_UINT64:
+                case DBUS_TYPE_DOUBLE:
+                  dbus_message_iter_append_basic (&dict_iter, signature[2], hash_value);
+                  break;
+
+                case DBUS_STRUCT_BEGIN_CHAR:
+                  g_value_init (&val, G_TYPE_DBUS_STRUCTURE);
+                  g_value_take_object (&val, G_DBUS_STRUCTURE (hash_value));
+                  /* recursive dance */
+                  if (!append_value_to_iter (&dict_iter, value_signature, &val, error))
+                    goto out;
+                  break;
+
+                case DBUS_TYPE_ARRAY:
+                  if (value_signature[1] == DBUS_TYPE_BYTE ||
+                      value_signature[1] == DBUS_TYPE_INT16 ||
+                      value_signature[1] == DBUS_TYPE_UINT16 ||
+                      value_signature[1] == DBUS_TYPE_INT32 ||
+                      value_signature[1] == DBUS_TYPE_UINT32 ||
+                      value_signature[1] == DBUS_TYPE_INT64 ||
+                      value_signature[1] == DBUS_TYPE_UINT64 ||
+                      value_signature[1] == DBUS_TYPE_BOOLEAN ||
+                      value_signature[1] == DBUS_TYPE_DOUBLE)
+                    {
+                      g_value_init (&val, G_TYPE_ARRAY);
+                      g_value_take_boxed (&val, hash_value);
+                      /* recurse */
+                      if (!append_value_to_iter (&dict_iter, value_signature, &val, error))
+                        goto out;
+                    }
+                  else if (value_signature[1] == DBUS_TYPE_STRING ||
+                           value_signature[1] == DBUS_TYPE_OBJECT_PATH ||
+                           value_signature[1] == DBUS_TYPE_SIGNATURE)
+                    {
+                      GType boxed_type;
+
+                      if (value_signature[1] == DBUS_TYPE_STRING)
+                        boxed_type = G_TYPE_STRV;
+                      else if (value_signature[1] == DBUS_TYPE_OBJECT_PATH)
+                        boxed_type = G_TYPE_STRV; // TODO:o EGG_DBUS_TYPE_OBJECT_PATH_ARRAY;
+                      else if (value_signature[1] == DBUS_TYPE_SIGNATURE)
+                        boxed_type = G_TYPE_STRV; // TODO:o EGG_DBUS_TYPE_SIGNATURE_ARRAY;
+                      else
+                        g_assert_not_reached ();
+
+                      g_value_init (&val, boxed_type);
+                      g_value_set_static_boxed (&val, hash_value);
+                      /* recurse */
+                      if (!append_value_to_iter (&dict_iter, value_signature, &val, error))
+                        goto out;
+                    }
+                  else if (value_signature[1] == DBUS_STRUCT_BEGIN_CHAR)
+                    {
+                      g_value_init (&val, G_TYPE_PTR_ARRAY);
+                      g_value_take_boxed (&val, hash_value);
+                      /* recurse */
+                      if (!append_value_to_iter (&dict_iter, value_signature, &val, error))
+                        goto out;
+                    }
+                  else if (value_signature[1] == DBUS_DICT_ENTRY_BEGIN_CHAR)
+                    {
+                      g_value_init (&val, G_TYPE_HASH_TABLE);
+                      g_value_take_boxed (&val, hash_value);
+                      /* recurse */
+                      if (!append_value_to_iter (&dict_iter, value_signature, &val, error))
+                        goto out;
+                    }
+                  else if (value_signature[1] == DBUS_TYPE_VARIANT)
+                    {
+                      g_value_init (&val, G_TYPE_PTR_ARRAY);
+                      g_value_take_boxed (&val, hash_value);
+                      /* recurse */
+                      if (!append_value_to_iter (&dict_iter, value_signature, &val, error))
+                        goto out;
+                    }
+                  else if (value_signature[1] == DBUS_TYPE_ARRAY)
+                    {
+                      g_value_init (&val, G_TYPE_PTR_ARRAY);
+                      g_value_take_boxed (&val, hash_value);
+                      /* recurse */
+                      if (!append_value_to_iter (&dict_iter, value_signature, &val, error))
+                        goto out;
+                    }
+                  else
+                    {
+                      g_assert_not_reached ();
+                    }
+                  break;
+
+                case DBUS_TYPE_VARIANT:
+                  g_value_init (&val, G_TYPE_DBUS_VARIANT);
+                  g_value_take_object (&val, hash_value);
+                  /* recurse */
+                  if (!append_value_to_iter (&dict_iter, value_signature, &val, error))
+                    goto out;
+                  break;
+
+                default:
+                  g_assert_not_reached ();
+                  break;
+                }
+
+              dbus_message_iter_close_container (&array_iter, &dict_iter);
+            }
+          dbus_message_iter_close_container (iter, &array_iter);
+
+          g_free (value_signature);
+        }
+      else if (signature[1] == DBUS_TYPE_ARRAY) /* array of an array */
+        {
+          DBusMessageIter array_iter;
+          GPtrArray *p;
+          guint n;
+
+          p = g_value_get_boxed (value);
+          dbus_message_iter_open_container (iter, DBUS_TYPE_ARRAY, signature + 1, &array_iter);
+          for (n = 0; n < p->len; n++)
+            {
+              GValue val = {0};
+
+              g_value_init (&val, _g_dbus_signature_get_gtype (signature + 1));
+
+              /* this can either be boxed (for GStrv, GArray) or an object (for e.g. structs, variants) */
+              if (g_type_is_a (G_VALUE_TYPE (&val), G_TYPE_BOXED))
+                g_value_take_boxed (&val, p->pdata[n]);
+              else if (g_type_is_a (G_VALUE_TYPE (&val), G_TYPE_OBJECT))
+                g_value_take_object (&val, p->pdata[n]);
+              else
+                g_assert_not_reached ();
+
+              /* recursive dance */
+              if (!append_value_to_iter (&array_iter, signature + 1, &val, error))
+                goto out;
+            }
+
+          dbus_message_iter_close_container (iter, &array_iter);
+        }
+      else if (signature[1] == DBUS_TYPE_VARIANT ) /* array of variants */
+        {
+          DBusMessageIter array_iter;
+          GPtrArray *p;
+          guint n;
+
+          p = g_value_get_boxed (value);
+          dbus_message_iter_open_container (iter, DBUS_TYPE_ARRAY, signature + 1, &array_iter);
+          for (n = 0; n < p->len; n++)
+            {
+              GValue val = {0};
+
+              g_value_init (&val, G_TYPE_DBUS_VARIANT);
+              g_value_take_object (&val, p->pdata[n]);
+
+              /* recursive dance */
+              if (!append_value_to_iter (&array_iter, signature + 1, &val, error))
+                goto out;
+            }
+
+          dbus_message_iter_close_container (iter, &array_iter);
+        }
+      else
+        {
+          g_warning ("Don't know append an array with elements with with signature %s", signature + 1);
+          g_assert_not_reached ();
+        }
+      break;
+
+    /* ---------------------------------------------------------------------------------------------------- */
+    case DBUS_STRUCT_BEGIN_CHAR:
+      {
+        DBusMessageIter struct_iter;
+        GDBusStructure *structure;
+        guint n;
+        guint num_elems;
+
+        structure = G_DBUS_STRUCTURE (g_value_get_object (value));
+
+        num_elems = g_dbus_structure_get_num_elements (structure);
+
+        dbus_message_iter_open_container (iter, DBUS_TYPE_STRUCT, NULL, &struct_iter);
+        for (n = 0; n < num_elems; n++)
+          {
+            const GValue *val;
+            const gchar *sig_for_elem;
+
+            val = _g_dbus_structure_get_gvalue_for_element (structure, n);
+            sig_for_elem = g_dbus_structure_get_signature_for_element (structure, n);
+            /* recurse */
+            if (!append_value_to_iter (&struct_iter, sig_for_elem, val, error))
+              goto out;
+          }
+        dbus_message_iter_close_container (iter, &struct_iter);
+      }
+      break;
+
+    /* ---------------------------------------------------------------------------------------------------- */
+    case DBUS_TYPE_VARIANT:
+      {
+        GDBusVariant *variant;
+        DBusMessageIter variant_iter;
+        const gchar *variant_signature;
+        GValue val = {0};
+
+        g_value_init (&val, G_TYPE_DBUS_VARIANT);
+        variant = g_value_get_object (value);
+
+        variant_signature = g_dbus_variant_get_variant_signature (variant);
+
+        dbus_message_iter_open_container (iter, DBUS_TYPE_VARIANT, variant_signature, &variant_iter);
+        /* recurse */
+        if (!append_value_to_iter (&variant_iter,
+                                   variant_signature,
+                                   _g_dbus_variant_get_gvalue (variant),
+                                   error))
+          goto out;
+        dbus_message_iter_close_container (iter, &variant_iter);
+      }
       break;
 
     default:
-      g_warning ("Don't know how to extract D-Bus type-code %c for out-arg %d",
-                 signature[0],
-                 arg_num);
-      goto error;
+      g_warning ("Don't know append a value with signature %s", signature);
+      g_assert_not_reached ();
+      break;
+    }
+
+  ret = TRUE;
+
+ out:
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static gboolean
+extract_values_cb (guint        arg_num,
+                   const gchar *signature,
+                   GType        type,
+                   va_list      va_args,
+                   gpointer     user_data)
+{
+  DBusMessageIter *iter = user_data;
+  GValue value = {0};
+  GValue value2 = {0};
+  GValue *value_to_extract;
+  GError *error;
+  gboolean ret;
+  gchar *error_str;
+
+  ret = TRUE;
+
+  //g_debug ("arg %d: %s", arg_num, g_type_name (type));
+
+  error = NULL;
+  if (!get_value_from_iter (iter, &value, &error))
+    {
+      g_warning ("Error extracting value for arg %d: %s",
+                 arg_num,
+                 error->message);
+      g_error_free (error);
+      goto out;
+    }
+  value_to_extract = &value;
+
+  /* coerce if needed */
+  if (!g_type_is_a (type, G_VALUE_TYPE (&value)))
+    {
+      /* try and see if a transformation routine is available */
+      g_value_init (&value2, type);
+      if (!g_value_transform (&value, &value2))
+        {
+          g_warning ("GType '%s' is incompatible with D-Bus signature '%s' (expected GType '%s') "
+                     "and no transformation is available",
+                     g_type_name (type),
+                     signature,
+                     g_type_name (G_VALUE_TYPE (&value)));
+          g_value_unset (&value);
+          goto out;
+        }
+      //g_debug ("Transformed from %s to %s", g_type_name (G_VALUE_TYPE (&value)), g_type_name (type));
+      value_to_extract = &value2;
+      g_value_unset (&value);
+    }
+
+  error_str = NULL;
+  G_VALUE_LCOPY (value_to_extract, va_args, G_VALUE_NOCOPY_CONTENTS, &error_str);
+  if (error != NULL)
+    {
+      g_warning ("Error copying value: %s", error_str);
+      g_free (error_str);
+      g_value_unset (value_to_extract);
+      goto out;
     }
 
   dbus_message_iter_next (iter);
 
-  return FALSE;
+  ret = FALSE;
+
+ out:
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
 
- mismatch:
-  g_warning ("D-Bus signature %s and GType %s are not compatible for out-arg %d",
-             signature,
-             g_type_name (type),
-             arg_num);
- error:
-  return TRUE;
+static gboolean
+append_values_cb (guint        arg_num,
+                  const gchar *signature,
+                  GType        type,
+                  va_list      va_args,
+                  gpointer     user_data)
+{
+  DBusMessageIter *iter = user_data;
+  GValue value = {0};
+  GValue value2 = {0};
+  GValue *value_to_append;
+  GError *error;
+  gboolean ret;
+  gchar *error_str;
+  GType expected_type;
+
+  ret = TRUE;
+
+  //g_debug ("arg %d: %s", arg_num, g_type_name (type));
+
+  g_value_init (&value, type);
+  error_str = NULL;
+  G_VALUE_COLLECT (&value, va_args, G_VALUE_NOCOPY_CONTENTS, &error_str);
+  if (error_str != NULL)
+    {
+      g_warning ("Error collecting value: %s", error_str);
+      g_free (error_str);
+      g_value_unset (&value);
+      goto out;
+    }
+  value_to_append = &value;
+
+  /* coerce if needed */
+  expected_type = _g_dbus_signature_get_gtype (signature);
+  if (!g_type_is_a (type, expected_type))
+    {
+      /* try and see if a transformation routine is available */
+      g_value_init (&value2, expected_type);
+      if (!g_value_transform (&value, &value2))
+        {
+          g_warning ("GType '%s' is incompatible with D-Bus signature '%s' (expected GType '%s') "
+                     "and no transformation is available",
+                     g_type_name (type),
+                     signature,
+                     g_type_name (expected_type));
+          g_value_unset (&value);
+          goto out;
+        }
+      //g_debug ("Transformed from %s to %s", g_type_name (type), g_type_name (expected_type));
+      value_to_append = &value2;
+      g_value_unset (&value);
+    }
+
+  error = NULL;
+  if (!append_value_to_iter (iter, signature, value_to_append, &error))
+    {
+      g_warning ("Error appending value for arg %d: %s",
+                 arg_num,
+                 error->message);
+      g_error_free (error);
+      g_value_unset (value_to_append);
+      goto out;
+    }
+
+  g_value_unset (value_to_append);
+
+  ret = FALSE;
+
+ out:
+  return ret;
 }
 
 /* ---------------------------------------------------------------------------------------------------- */
@@ -683,19 +2093,36 @@ g_dbus_proxy_invoke_method_valist (GDBusProxy          *proxy,
                                       user_data,
                                       g_dbus_proxy_invoke_method);
 
-  if ((message = dbus_message_new_method_call (proxy->priv->name,
-                                               proxy->priv->object_path,
-                                               proxy->priv->interface_name,
-                                               name)) == NULL)
-    _g_dbus_oom ();
+  if (strchr (name, '.'))
+    {
+      gchar *buf;
+      gchar *p;
+      buf = g_strdup (name);
+      p = strrchr (buf, '.');
+      *p = '\0';
+      if ((message = dbus_message_new_method_call (proxy->priv->name,
+                                                   proxy->priv->object_path,
+                                                   buf,
+                                                   p + 1)) == NULL)
+        _g_dbus_oom ();
+      g_free (buf);
+    }
+  else
+    {
+      if ((message = dbus_message_new_method_call (proxy->priv->name,
+                                                   proxy->priv->object_path,
+                                                   proxy->priv->interface_name,
+                                                   name)) == NULL)
+        _g_dbus_oom ();
+    }
 
   /* append args to message */
   dbus_message_iter_init_append (message, &iter);
-  if (foreach_signature_and_gtype (signature,
-                                   first_in_arg_type,
-                                   va_args,
-                                   append_values_cb,
-                                   &iter))
+  if (_gdbus_signature_vararg_foreach (signature,
+                                       first_in_arg_type,
+                                       va_args,
+                                       append_values_cb,
+                                       &iter))
     goto out;
 
   pending_call_id = g_dbus_connection_send_dbus_1_message_with_reply (proxy->priv->connection,
@@ -747,11 +2174,11 @@ g_dbus_proxy_invoke_method_finish_valist (GDBusProxy          *proxy,
 
   /* extract values from message */
   dbus_message_iter_init (reply, &reply_iter);
-  if (foreach_signature_and_gtype (signature,
-                                   first_out_arg_type,
-                                   va_args,
-                                   extract_values_cb,
-                                   &reply_iter))
+  if (_gdbus_signature_vararg_foreach (signature,
+                                       first_out_arg_type,
+                                       va_args,
+                                       extract_values_cb,
+                                       &reply_iter))
     {
       g_set_error (error,
                    G_DBUS_ERROR,
@@ -783,6 +2210,10 @@ g_dbus_proxy_invoke_method_finish_valist (GDBusProxy          *proxy,
  * is an asynchronous operation and when the reply is ready @callback will be invoked (on
  * the main thread) and you can get the result by calling g_dbus_proxy_invoke_method_finish().
  *
+ * If @name contains any dots, then @name is split into interface and
+ * method name parts. This allows using @proxy for invoking methods on
+ * other interfaces.
+ *
  * Note that @signature and and the supplied (type, value) pairs must match as described in
  * chapter TODO_SECTION_EXPLAINING_DBUS_TO_GTYPE_OBJECT_MAPPING.
  *
@@ -885,6 +2316,10 @@ invoke_method_sync_cb (GDBusConnection *connection,
  * Synchronously invokes the method @name on the interface and remote
  * object represented by @proxy.
  *
+ * If @name contains any dots, then @name is split into interface and
+ * method name parts. This allows using @proxy for invoking methods on
+ * other interfaces.
+ *
  * Returns: %TRUE if the call succeeded, %FALSE if @error is set.
  **/
 gboolean
diff --git a/gdbus/gdbusproxy.h b/gdbus/gdbusproxy.h
index 7a56db4..ebe2630 100644
--- a/gdbus/gdbusproxy.h
+++ b/gdbus/gdbusproxy.h
@@ -79,39 +79,54 @@ struct _GDBusProxyClass
   void (*_g_reserved8) (void);
 };
 
-GType            g_dbus_proxy_get_type             (void) G_GNUC_CONST;
-GDBusProxy      *g_dbus_proxy_new                  (GDBusConnection     *connection,
-                                                    const gchar         *name,
-                                                    const gchar         *object_path,
-                                                    const gchar         *interface_name);
-GDBusConnection *g_dbus_proxy_get_connection       (GDBusProxy          *proxy);
-const gchar     *g_dbus_proxy_get_name             (GDBusProxy          *proxy);
-const gchar     *g_dbus_proxy_get_object_path      (GDBusProxy          *proxy);
-const gchar     *g_dbus_proxy_get_interface_name   (GDBusProxy          *proxy);
-guint            g_dbus_proxy_invoke_method        (GDBusProxy          *proxy,
-                                                    const gchar         *name,
-                                                    const gchar         *signature,
-                                                    guint                timeout_msec,
-                                                    GCancellable        *cancellable,
-                                                    GAsyncReadyCallback  callback,
-                                                    gpointer             user_data,
-                                                    GType                first_in_arg_type,
-                                                    ...);
-gboolean         g_dbus_proxy_invoke_method_finish (GDBusProxy          *proxy,
-                                                    const gchar         *signature,
-                                                    GAsyncResult        *res,
-                                                    GError             **error,
-                                                    GType                first_out_arg_type,
-                                                    ...);
-gboolean         g_dbus_proxy_invoke_method_sync   (GDBusProxy          *proxy,
-                                                    const gchar         *name,
-                                                    const gchar         *in_signature,
-                                                    const gchar         *out_signature,
-                                                    guint                timeout_msec,
-                                                    GCancellable        *cancellable,
-                                                    GError             **error,
-                                                    GType                first_in_arg_type,
-                                                    ...);
+GType            g_dbus_proxy_get_type              (void) G_GNUC_CONST;
+GDBusProxy      *g_dbus_proxy_new                   (GDBusConnection     *connection,
+                                                     const gchar         *name,
+                                                     const gchar         *object_path,
+                                                     const gchar         *interface_name,
+                                                     GCancellable        *cancellable,
+                                                     GAsyncReadyCallback  callback,
+                                                     gpointer             user_data);
+gboolean         g_dbus_proxy_new_finish             (GDBusProxy         *proxy,
+                                                      GAsyncResult       *res,
+                                                      GError            **error);
+GDBusConnection *g_dbus_proxy_get_connection        (GDBusProxy          *proxy);
+const gchar     *g_dbus_proxy_get_name              (GDBusProxy          *proxy);
+const gchar     *g_dbus_proxy_get_object_path       (GDBusProxy          *proxy);
+const gchar     *g_dbus_proxy_get_interface_name    (GDBusProxy          *proxy);
+gboolean         g_dbus_proxy_get_properties_loaded (GDBusProxy          *proxy);
+void             g_dbus_proxy_block_for_properties  (GDBusProxy          *proxy);
+guint            g_dbus_proxy_invoke_method         (GDBusProxy          *proxy,
+                                                     const gchar         *name,
+                                                     const gchar         *signature,
+                                                     guint                timeout_msec,
+                                                     GCancellable        *cancellable,
+                                                     GAsyncReadyCallback  callback,
+                                                     gpointer             user_data,
+                                                     GType                first_in_arg_type,
+                                                     ...);
+gboolean         g_dbus_proxy_invoke_method_finish  (GDBusProxy          *proxy,
+                                                     const gchar         *signature,
+                                                     GAsyncResult        *res,
+                                                     GError             **error,
+                                                     GType                first_out_arg_type,
+                                                     ...);
+gboolean         g_dbus_proxy_invoke_method_sync    (GDBusProxy          *proxy,
+                                                     const gchar         *name,
+                                                     const gchar         *in_signature,
+                                                     const gchar         *out_signature,
+                                                     guint                timeout_msec,
+                                                     GCancellable        *cancellable,
+                                                     GError             **error,
+                                                     GType                first_in_arg_type,
+                                                     ...);
+GDBusVariant    *g_dbus_proxy_get_cached_property   (GDBusProxy          *proxy,
+                                                     const gchar         *property_name,
+                                                     GError             **error);
+gboolean         g_dbus_proxy_set_cached_property   (GDBusProxy          *proxy,
+                                                     const gchar         *property_name,
+                                                     GDBusVariant        *variant,
+                                                     GError             **error);
 
 G_END_DECLS
 
diff --git a/gdbus/gdbusstructure.c b/gdbus/gdbusstructure.c
new file mode 100644
index 0000000..02c7dcb
--- /dev/null
+++ b/gdbus/gdbusstructure.c
@@ -0,0 +1,457 @@
+/* GDBus - GLib D-Bus Library
+ *
+ * Copyright (C) 2008-2009 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <glib/gi18n.h>
+#include <gobject/gvaluecollector.h>
+
+#include "gdbusstructure.h"
+#include "gdbusenumtypes.h"
+#include "gdbusconnection.h"
+#include "gdbuserror.h"
+#include "gdbusprivate.h"
+
+#include "gdbusalias.h"
+
+/**
+ * SECTION:gdbusstructure
+ * @short_description: Holds a set of values
+ * @include: gdbus/gdbus.h
+ *
+ * #GDBusStructure is a type that holds a D-Bus structure.
+ */
+
+struct _GDBusStructurePrivate
+{
+  gchar  *signature;
+  guint   num_elements;
+  GValue *elements;
+  gchar **element_signatures;
+};
+
+static gboolean construct_cb (guint        arg_num,
+                              const gchar *signature,
+                              GType        type,
+                              va_list      va_args,
+                              gpointer     user_data);
+
+G_DEFINE_TYPE (GDBusStructure, g_dbus_structure, G_TYPE_OBJECT);
+
+static void
+g_dbus_structure_finalize (GObject *object)
+{
+  GDBusStructure *structure = G_DBUS_STRUCTURE (object);
+  guint n;
+
+  g_free (structure->priv->signature);
+  for (n = 0; n < structure->priv->num_elements; n++)
+    {
+      g_value_unset (&structure->priv->elements[n]);
+      dbus_free (structure->priv->element_signatures[n]); /* allocated by libdbus */
+    }
+  g_free (structure->priv->element_signatures);
+
+  if (G_OBJECT_CLASS (g_dbus_structure_parent_class)->finalize != NULL)
+    G_OBJECT_CLASS (g_dbus_structure_parent_class)->finalize (object);
+}
+
+static void
+g_dbus_structure_class_init (GDBusStructureClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->finalize     = g_dbus_structure_finalize;
+
+  /* We avoid using GObject properties since
+   *
+   *  - performance is a concern
+   *  - this is for the C object mapping only
+   */
+
+  g_type_class_add_private (klass, sizeof (GDBusStructurePrivate));
+}
+
+static void
+g_dbus_structure_init (GDBusStructure *structure)
+{
+  structure->priv = G_TYPE_INSTANCE_GET_PRIVATE (structure, G_TYPE_DBUS_STRUCTURE, GDBusStructurePrivate);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/**
+ * g_dbus_structure_get_num_elements:
+ * @structure: A #GDBusStructure.
+ *
+ * Gets number of elements in @structure.
+ *
+ * Returns: Number of elements in @structure.
+ **/
+guint
+g_dbus_structure_get_num_elements (GDBusStructure *structure)
+{
+  g_return_val_if_fail (G_IS_DBUS_STRUCTURE (structure), 0);
+  return structure->priv->num_elements;
+}
+
+/**
+ * g_dbus_structure_get_signature:
+ * @structure: A #GDBusStructure.
+ *
+ * Gets the signature for @structure.
+ *
+ * Returns: A D-Bus signature. Do not free this string, it
+ * is owned by @structure.
+ **/
+const gchar *
+g_dbus_structure_get_signature (GDBusStructure *structure)
+{
+  g_return_val_if_fail (G_IS_DBUS_STRUCTURE (structure), NULL);
+  return structure->priv->signature;
+}
+
+/**
+ * g_dbus_structure_get_signature_for_element:
+ * @structure: A #GDBusStructure.
+ * @element: Element number.
+ *
+ * Gets the signature for an @element in @structure.
+ *
+ * Returns: A D-Bus signature. Do not free this string, it
+ * is owned by @structure.
+ **/
+const gchar *
+g_dbus_structure_get_signature_for_element (GDBusStructure *structure,
+                                            guint           element)
+{
+  g_return_val_if_fail (G_IS_DBUS_STRUCTURE (structure), NULL);
+  g_return_val_if_fail (element < structure->priv->num_elements, NULL);
+  return structure->priv->element_signatures[element];
+}
+
+const GValue *
+_g_dbus_structure_get_gvalue_for_element (GDBusStructure *structure,
+                                          guint           element)
+{
+  g_return_val_if_fail (G_IS_DBUS_STRUCTURE (structure), NULL);
+  g_return_val_if_fail (element < structure->priv->num_elements, NULL);
+  return &structure->priv->elements[element];
+}
+
+/**
+ * g_dbus_structure_equal:
+ * @structure1: The first #GDBusStructure.
+ * @structure2: The second #GDBusStructure.
+ *
+ * Checks if @structure1 holds the same types and values as @structure2.
+ *
+ * Returns: %TRUE if @structure1 and @structure2 are equal, %FALSE otherwise.
+ **/
+gboolean
+g_dbus_structure_equal (GDBusStructure  *structure1,
+                        GDBusStructure  *structure2)
+{
+  gboolean ret;
+  guint n;
+
+  g_return_val_if_fail (G_IS_DBUS_STRUCTURE (structure1), FALSE);
+  g_return_val_if_fail (G_IS_DBUS_STRUCTURE (structure2), FALSE);
+
+  if (g_strcmp0 (structure1->priv->signature, structure2->priv->signature) != 0)
+    return FALSE;
+
+  ret = FALSE;
+  for (n = 0; n < structure1->priv->num_elements; n++)
+    {
+      if (!_g_dbus_gvalue_equal (&structure1->priv->elements[n],
+                                 &structure2->priv->elements[n],
+                                 structure1->priv->element_signatures[n]))
+        goto out;
+    }
+
+  ret = TRUE;
+
+ out:
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/**
+ * g_dbus_structure_new:
+ * @signature: The signature of the structure.
+ * @first_element_type: The #GType of the element.
+ * @...: The value of the first element, followed by zero or more (type, value) pairs, followed by #G_TYPE_INVALID.
+ *
+ * Creates a new D-Bus structure with @signature.
+ *
+ * Note that @signature and and the supplied (type, value) pairs must match as described in
+ * chapter TODO_SECTION_EXPLAINING_DBUS_TO_GTYPE_OBJECT_MAPPING.
+ *
+ * Returns: A new #GDBusStructure, free with g_object_unref().
+ **/
+GDBusStructure *
+g_dbus_structure_new (const gchar *signature,
+                      GType        first_element_type,
+                      ...)
+{
+  GDBusStructure *s;
+  va_list va_args;
+  GArray *a;
+  guint n;
+  gchar *signature_with_parenthesis;
+
+  s = NULL;
+  a = g_array_new (FALSE,
+                   TRUE,
+                   sizeof (GValue));
+
+  signature_with_parenthesis = g_strdup (signature + 1);
+  signature_with_parenthesis[strlen (signature_with_parenthesis) - 1] = '\0';
+
+  va_start (va_args, first_element_type);
+  if (_gdbus_signature_vararg_foreach (signature_with_parenthesis,
+                                       first_element_type,
+                                       va_args,
+                                       construct_cb,
+                                       a))
+    {
+      for (n = 0; n < a->len; n++)
+        g_value_unset (&(g_array_index (a, GValue, n)));
+      g_array_free (a, TRUE);
+      goto out;
+    }
+
+  s = _g_dbus_structure_new_for_values (signature,
+                                        a->len,
+                                        (GValue *) a->data);
+
+  g_array_free (a, FALSE);
+
+ out:
+  va_end (va_args);
+  g_free (signature_with_parenthesis);
+  return s;
+}
+
+static gboolean
+construct_cb (guint        arg_num,
+              const gchar *signature,
+              GType        type,
+              va_list      va_args,
+              gpointer     user_data)
+{
+  GArray *a = user_data;
+  GValue *value;
+  gboolean ret;
+  gchar *error_str;
+
+  ret = TRUE;
+
+  g_array_set_size (a, a->len + 1);
+  value = &g_array_index (a, GValue, a->len - 1);
+
+  g_value_init (value, type);
+
+  error_str = NULL;
+  G_VALUE_COLLECT (value, va_args, 0, &error_str);
+  if (error_str != NULL)
+    {
+      g_warning ("Error collecting value: %s", error_str);
+      g_free (error_str);
+      goto error;
+    }
+
+  /* TODO: check if value is compatible with signature */
+
+  //g_debug ("arg_num %d: %s", arg_num, g_strdup_value_contents (value));
+
+  ret = FALSE;
+
+ error:
+  return ret;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+GDBusStructure *
+_g_dbus_structure_new_for_values (const gchar *signature,
+                                  guint        num_elements,
+                                  GValue      *elements)
+{
+  GDBusStructure *s;
+  DBusSignatureIter iter;
+  GPtrArray *p;
+  gchar *signature_with_parenthesis;
+
+  s = G_DBUS_STRUCTURE (g_object_new (G_TYPE_DBUS_STRUCTURE, NULL));
+
+  signature_with_parenthesis = g_strdup (signature + 1);
+  signature_with_parenthesis[strlen (signature_with_parenthesis) - 1] = '\0';
+
+  s->priv->signature = g_strdup (signature);
+  s->priv->num_elements = num_elements;
+  s->priv->elements = elements; /* we steal the elements */
+
+  /* TODO: when constructing structures we already parse the signature so maybe use
+   *       that to avoid the overhead of parsing it again
+   */
+  p = g_ptr_array_new ();
+  dbus_signature_iter_init (&iter, signature_with_parenthesis);
+  do
+    {
+      g_ptr_array_add (p, dbus_signature_iter_get_signature (&iter));
+    }
+  while (dbus_signature_iter_next (&iter));
+
+  g_assert (num_elements == p->len);
+
+  s->priv->element_signatures = (gchar **) p->pdata;
+
+  g_ptr_array_free (p, FALSE);
+
+  g_free (signature_with_parenthesis);
+
+  return s;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+g_dbus_structure_set_element_valist (GDBusStructure *structure,
+                                     guint           first_element_number,
+                                     va_list         var_args)
+{
+  guint elem_number;
+
+  g_return_if_fail (G_IS_DBUS_STRUCTURE (structure));
+
+  elem_number = first_element_number;
+  while (elem_number != (guint) -1)
+    {
+      gchar *error;
+
+      if (elem_number >= structure->priv->num_elements)
+        {
+          g_warning ("%s: elem number %u is out of bounds", G_STRFUNC, elem_number);
+          break;
+        }
+
+      G_VALUE_COLLECT (&(structure->priv->elements[elem_number]), var_args, 0, &error);
+      if (error != NULL)
+        {
+          g_warning ("%s: %s", G_STRFUNC, error);
+          g_free (error);
+          break;
+        }
+
+      elem_number = va_arg (var_args, guint);
+    }
+}
+
+/**
+ * g_dbus_structure_set_element:
+ * @structure: A #GDBusStructure.
+ * @first_element_number: Element number to set.
+ * @...: First element to set, followed optionally by
+ * more element number / return location pairs, followed by -1.
+ *
+ * Sets element values in a #GDBusStructure. Similar to g_object_set().
+ **/
+void
+g_dbus_structure_set_element (GDBusStructure *structure,
+                              guint           first_element_number,
+                              ...)
+{
+  va_list var_args;
+
+  g_return_if_fail (G_IS_DBUS_STRUCTURE (structure));
+
+  va_start (var_args, first_element_number);
+  g_dbus_structure_set_element_valist (structure, first_element_number, var_args);
+  va_end (var_args);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+g_dbus_structure_get_element_valist (GDBusStructure *structure,
+                                     guint           first_element_number,
+                                     va_list         var_args)
+{
+  guint elem_number;
+
+  g_return_if_fail (G_IS_DBUS_STRUCTURE (structure));
+
+  elem_number = first_element_number;
+  while (elem_number != (guint) -1)
+    {
+      gchar *error;
+
+      if (elem_number >= structure->priv->num_elements)
+        {
+          g_warning ("%s: elem number %u is out of bounds", G_STRFUNC, elem_number);
+          break;
+        }
+
+      G_VALUE_LCOPY (&(structure->priv->elements[elem_number]), var_args, G_VALUE_NOCOPY_CONTENTS, &error);
+      if (error != NULL)
+        {
+          g_warning ("%s: %s", G_STRFUNC, error);
+          g_free (error);
+          break;
+        }
+
+      elem_number = va_arg (var_args, guint);
+    }
+}
+
+/**
+ * g_dbus_structure_get_element:
+ * @structure: A #GDBusStructure.
+ * @first_element_number: Element number to get.
+ * @...: Return location for the first element, followed optionally by
+ * more element number / return location pairs, followed by -1.
+ *
+ * Gets element values in a #GDBusStructure, similar to g_object_get(). The returned
+ * values should not be freed; @structure owns the reference.
+ **/
+void
+g_dbus_structure_get_element (GDBusStructure *structure,
+                              guint           first_element_number,
+                              ...)
+{
+  va_list var_args;
+
+  g_return_if_fail (G_IS_DBUS_STRUCTURE (structure));
+
+  va_start (var_args, first_element_number);
+  g_dbus_structure_get_element_valist (structure, first_element_number, var_args);
+  va_end (var_args);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+#define __G_DBUS_STRUCTURE_C__
+#include "gdbusaliasdef.c"
diff --git a/gdbus/gdbusstructure.h b/gdbus/gdbusstructure.h
new file mode 100644
index 0000000..2978e52
--- /dev/null
+++ b/gdbus/gdbusstructure.h
@@ -0,0 +1,101 @@
+/* GDBus - GLib D-Bus Library
+ *
+ * Copyright (C) 2008-2009 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#if !defined (__G_DBUS_G_DBUS_H_INSIDE__) && !defined (G_DBUS_COMPILATION)
+#error "Only <gdbus/gdbus.h> can be included directly."
+#endif
+
+#ifndef __G_DBUS_STRUCTURE_H__
+#define __G_DBUS_STRUCTURE_H__
+
+#include <gdbus/gdbustypes.h>
+#include <dbus/dbus.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_DBUS_STRUCTURE         (g_dbus_structure_get_type ())
+#define G_DBUS_STRUCTURE(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_STRUCTURE, GDBusStructure))
+#define G_DBUS_STRUCTURE_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_STRUCTURE, GDBusStructureClass))
+#define G_DBUS_STRUCTURE_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_STRUCTURE, GDBusStructureClass))
+#define G_IS_DBUS_STRUCTURE(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_STRUCTURE))
+#define G_IS_DBUS_STRUCTURE_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_STRUCTURE))
+
+typedef struct _GDBusStructureClass   GDBusStructureClass;
+typedef struct _GDBusStructurePrivate GDBusStructurePrivate;
+
+/**
+ * GDBusStructure:
+ *
+ * The #GDBusStructure structure contains only private data and
+ * should only be accessed using the provided API.
+ */
+struct _GDBusStructure
+{
+  /*< private >*/
+  GObject parent_instance;
+  GDBusStructurePrivate *priv;
+};
+
+/**
+ * GDBusStructureClass:
+ *
+ * Class structure for #GDBusStructure.
+ */
+struct _GDBusStructureClass
+{
+  /*< private >*/
+  GObjectClass parent_class;
+
+  /*< public >*/
+
+  /*< private >*/
+  /* Padding for future expansion */
+  void (*_g_reserved1) (void);
+  void (*_g_reserved2) (void);
+  void (*_g_reserved3) (void);
+  void (*_g_reserved4) (void);
+  void (*_g_reserved5) (void);
+  void (*_g_reserved6) (void);
+  void (*_g_reserved7) (void);
+  void (*_g_reserved8) (void);
+};
+
+GType           g_dbus_structure_get_type                   (void) G_GNUC_CONST;
+gboolean        g_dbus_structure_equal                      (GDBusStructure  *structure1,
+                                                             GDBusStructure  *structure2);
+GDBusStructure *g_dbus_structure_new                        (const gchar     *signature,
+                                                             GType            first_element_type,
+                                                             ...);
+const gchar    *g_dbus_structure_get_signature              (GDBusStructure  *structure);
+guint           g_dbus_structure_get_num_elements           (GDBusStructure  *structure);
+const gchar    *g_dbus_structure_get_signature_for_element  (GDBusStructure  *structure,
+                                                             guint            element);
+void            g_dbus_structure_get_element                (GDBusStructure  *structure,
+                                                             guint            first_element_number,
+                                                             ...);
+void            g_dbus_structure_set_element                (GDBusStructure  *structure,
+                                                             guint            first_element_number,
+                                                             ...);
+
+G_END_DECLS
+
+#endif /* __G_DBUS_STRUCTURE_H__ */
diff --git a/gdbus/gdbustypes.h b/gdbus/gdbustypes.h
index f1a4451..8a6d31b 100644
--- a/gdbus/gdbustypes.h
+++ b/gdbus/gdbustypes.h
@@ -35,6 +35,8 @@ typedef struct _GDBusConnection       GDBusConnection;
 typedef struct _GBusNameOwner         GBusNameOwner;
 typedef struct _GBusNameWatcher       GBusNameWatcher;
 typedef struct _GDBusProxy            GDBusProxy;
+typedef struct _GDBusStructure        GDBusStructure;
+typedef struct _GDBusVariant          GDBusVariant;
 
 G_END_DECLS
 
diff --git a/gdbus/gdbusvariant.c b/gdbus/gdbusvariant.c
new file mode 100644
index 0000000..278a63e
--- /dev/null
+++ b/gdbus/gdbusvariant.c
@@ -0,0 +1,1575 @@
+/* GDBus - GLib D-Bus Library
+ *
+ * Copyright (C) 2008-2009 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <glib/gi18n.h>
+#include <gobject/gvaluecollector.h>
+
+#include "gdbusvariant.h"
+#include "gdbusenumtypes.h"
+#include "gdbusconnection.h"
+#include "gdbuserror.h"
+#include "gdbusstructure.h"
+#include "gdbusprivate.h"
+
+#include "gdbusalias.h"
+
+/**
+ * SECTION:gdbusvariant
+ * @short_description: Holds a D-Bus signature and a value
+ * @include: gdbus/gdbus.h
+ *
+ * #GDBusVariant is a polymorphic type that holds a D-Bus signature and a value.
+ */
+
+struct _GDBusVariantPrivate
+{
+  gchar *signature;
+  GValue value;
+};
+
+G_DEFINE_TYPE (GDBusVariant, g_dbus_variant, G_TYPE_OBJECT);
+
+static void
+g_dbus_variant_finalize (GObject *object)
+{
+  GDBusVariant *variant = G_DBUS_VARIANT (object);
+
+  if (variant->priv->signature != NULL)
+    g_value_unset (&variant->priv->value);
+  g_free (variant->priv->signature);
+
+  if (G_OBJECT_CLASS (g_dbus_variant_parent_class)->finalize != NULL)
+    G_OBJECT_CLASS (g_dbus_variant_parent_class)->finalize (object);
+}
+
+static void
+g_dbus_variant_class_init (GDBusVariantClass *klass)
+{
+  GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+  gobject_class->finalize     = g_dbus_variant_finalize;
+
+  /* We avoid using GObject properties since
+   *
+   *  - performance is a concern
+   *  - this is for the C object mapping only
+   */
+
+  g_type_class_add_private (klass, sizeof (GDBusVariantPrivate));
+}
+
+static void
+g_dbus_variant_init (GDBusVariant *variant)
+{
+  variant->priv = G_TYPE_INSTANCE_GET_PRIVATE (variant, G_TYPE_DBUS_VARIANT, GDBusVariantPrivate);
+}
+
+const GValue *
+_g_dbus_variant_get_gvalue (GDBusVariant *variant)
+{
+  return &variant->priv->value;
+}
+
+/**
+ * g_dbus_variant_get_variant_signature:
+ * @variant: A #GDBusVariant
+ *
+ * Gets the signature for @variant.
+ *
+ * Returns: The signature for @variant or %NULL if @variant is
+ * unset. Do not free this string, it is owned by @variant.
+ **/
+const gchar *
+g_dbus_variant_get_variant_signature (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant), NULL);
+  return variant->priv->signature;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/**
+ * g_dbus_variant_equal:
+ * @variant1: The first #GDBusVariant.
+ * @variant2: The second #GDBusVariant.
+ *
+ * Checks if @variant1 holds the same type and value as @variant2.
+ *
+ * Returns: %TRUE if @variant1 and @variant2 are equal, %FALSE otherwise.
+ **/
+gboolean
+g_dbus_variant_equal (GDBusVariant    *variant1,
+                      GDBusVariant    *variant2)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant1), FALSE);
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant2), FALSE);
+
+  if (g_strcmp0 (variant1->priv->signature, variant2->priv->signature) != 0)
+    return FALSE;
+
+  if (variant1->priv->signature == NULL)
+    return TRUE;
+
+  return _g_dbus_gvalue_equal (&variant1->priv->value,
+                               &variant2->priv->value,
+                               variant1->priv->signature);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+set_signature (GDBusVariant *variant,
+               const gchar  *signature)
+{
+  if (signature == NULL)
+    {
+      if (variant->priv->signature != NULL)
+        g_value_unset (&variant->priv->value);
+      variant->priv->signature = NULL;
+      g_free (variant->priv->signature);
+    }
+  else
+    {
+      g_free (variant->priv->signature);
+      variant->priv->signature = g_strdup (signature);
+    }
+}
+
+static void
+set_signature_for_array (GDBusVariant *variant,
+                         const gchar  *elem_signature)
+{
+  if (elem_signature == NULL)
+    {
+      if (variant->priv->signature != NULL)
+        g_value_unset (&variant->priv->value);
+      variant->priv->signature = NULL;
+      g_free (variant->priv->signature);
+    }
+  else
+    {
+      g_free (variant->priv->signature);
+      variant->priv->signature = g_strdup_printf ("a%s", elem_signature);
+    }
+}
+
+static void
+set_signature_for_hash_table (GDBusVariant *variant,
+                              const gchar  *key_signature,
+                              const gchar  *value_signature)
+{
+  if (key_signature == NULL || value_signature == NULL)
+    {
+      if (variant->priv->signature != NULL)
+        g_value_unset (&variant->priv->value);
+      variant->priv->signature = NULL;
+      g_free (variant->priv->signature);
+    }
+  else
+    {
+      g_free (variant->priv->signature);
+      variant->priv->signature = g_strdup_printf ("a{%s%s}", key_signature, value_signature);
+    }
+}
+
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/**
+ * g_dbus_variant_new:
+ *
+ * Creates a new unset variant.
+ *
+ * Returns: A new #GDBusVariant. Free with g_object_unref().
+ **/
+GDBusVariant *
+g_dbus_variant_new (void)
+{
+  GDBusVariant *variant;
+  variant = G_DBUS_VARIANT (g_object_new (G_TYPE_DBUS_VARIANT, NULL));
+  return variant;
+}
+
+GDBusVariant *
+_g_dbus_variant_new_for_gvalue (const GValue *value, const gchar *signature)
+{
+  GDBusVariant *variant;
+  variant = g_dbus_variant_new ();
+  g_value_init (&variant->priv->value, G_VALUE_TYPE (value));
+  g_value_copy (value, &variant->priv->value);
+  set_signature (variant, signature);
+  return variant;
+}
+
+/**
+ * g_dbus_variant_new_for_string:
+ * @value: A string.
+ *
+ * Creates a new variant that holds a copy of @value.
+ *
+ * Returns: A new #GDBusVariant. Free with g_object_unref().
+ **/
+GDBusVariant *
+g_dbus_variant_new_for_string (const gchar *value)
+{
+  GDBusVariant *variant;
+  variant = g_dbus_variant_new ();
+  g_dbus_variant_set_string (variant, value);
+  return variant;
+}
+
+/**
+ * g_dbus_variant_new_for_object_path:
+ * @value: An object path.
+ *
+ * Creates a new variant that holds a copy of @value.
+ *
+ * Returns: A new #GDBusVariant. Free with g_object_unref().
+ **/
+GDBusVariant *
+g_dbus_variant_new_for_object_path (const gchar *value)
+{
+  GDBusVariant *variant;
+  variant = g_dbus_variant_new ();
+  g_dbus_variant_set_object_path (variant, value);
+  return variant;
+}
+
+/**
+ * g_dbus_variant_new_for_signature:
+ * @value: A D-Bus signature.
+ *
+ * Creates a new variant that holds a copy of @value.
+ *
+ * Returns: A new #GDBusVariant. Free with g_object_unref().
+ **/
+GDBusVariant *
+g_dbus_variant_new_for_signature (const gchar *value)
+{
+  GDBusVariant *variant;
+  variant = g_dbus_variant_new ();
+  g_dbus_variant_set_signature (variant, value);
+  return variant;
+}
+
+/**
+ * g_dbus_variant_new_for_string_array:
+ * @value: A string array.
+ *
+ * Creates a new variant that holds a copy of @value.
+ *
+ * Returns: A new #GDBusVariant. Free with g_object_unref().
+ **/
+GDBusVariant *
+g_dbus_variant_new_for_string_array (gchar **value)
+{
+  GDBusVariant *variant;
+  variant = g_dbus_variant_new ();
+  g_dbus_variant_set_string_array (variant, value);
+  return variant;
+}
+
+/**
+ * g_dbus_variant_new_for_object_path_array:
+ * @value: An object path array.
+ *
+ * Creates a new variant that holds a copy of @value.
+ *
+ * Returns: A new #GDBusVariant. Free with g_object_unref().
+ **/
+GDBusVariant *
+g_dbus_variant_new_for_object_path_array (gchar **value)
+{
+  GDBusVariant *variant;
+  variant = g_dbus_variant_new ();
+  g_dbus_variant_set_object_path_array (variant, value);
+  return variant;
+}
+
+/**
+ * g_dbus_variant_new_for_signature_array:
+ * @value: A signature array.
+ *
+ * Creates a new variant that holds a copy of @value.
+ *
+ * Returns: A new #GDBusVariant. Free with g_object_unref().
+ **/
+GDBusVariant *
+g_dbus_variant_new_for_signature_array (gchar **value)
+{
+  GDBusVariant *variant;
+  variant = g_dbus_variant_new ();
+  g_dbus_variant_set_signature_array (variant, value);
+  return variant;
+}
+
+/**
+ * g_dbus_variant_new_for_byte:
+ * @value: A #guchar.
+ *
+ * Creates a new variant that holds a #guchar equal to @value.
+ *
+ * Returns: A new #GDBusVariant. Free with g_object_unref().
+ **/
+GDBusVariant *
+g_dbus_variant_new_for_byte (guchar value)
+{
+  GDBusVariant *variant;
+  variant = g_dbus_variant_new ();
+  g_dbus_variant_set_byte (variant, value);
+  return variant;
+}
+
+/**
+ * g_dbus_variant_new_for_int16:
+ * @value: A #gint16.
+ *
+ * Creates a new variant that holds a #gint16 equal to @value.
+ *
+ * Returns: A new #GDBusVariant. Free with g_object_unref().
+ **/
+GDBusVariant *
+g_dbus_variant_new_for_int16 (gint16 value)
+{
+  GDBusVariant *variant;
+  variant = g_dbus_variant_new ();
+  g_dbus_variant_set_int16 (variant, value);
+  return variant;
+}
+
+/**
+ * g_dbus_variant_new_for_uint16:
+ * @value: A #guint16.
+ *
+ * Creates a new variant that holds a #guint16 equal to @value.
+ *
+ * Returns: A new #GDBusVariant. Free with g_object_unref().
+ **/
+GDBusVariant *
+g_dbus_variant_new_for_uint16 (guint16 value)
+{
+  GDBusVariant *variant;
+  variant = g_dbus_variant_new ();
+  g_dbus_variant_set_uint16 (variant, value);
+  return variant;
+}
+
+/**
+ * g_dbus_variant_new_for_int:
+ * @value: A #gint.
+ *
+ * Creates a new variant that holds a #gint equal to @value.
+ *
+ * Returns: A new #GDBusVariant. Free with g_object_unref().
+ **/
+GDBusVariant *
+g_dbus_variant_new_for_int (gint value)
+{
+  GDBusVariant *variant;
+  variant = g_dbus_variant_new ();
+  g_dbus_variant_set_int (variant, value);
+  return variant;
+}
+
+/**
+ * g_dbus_variant_new_for_uint:
+ * @value: A #guint.
+ *
+ * Creates a new variant that holds a #guint equal to @value.
+ *
+ * Returns: A new #GDBusVariant. Free with g_object_unref().
+ **/
+GDBusVariant *
+g_dbus_variant_new_for_uint (guint value)
+{
+  GDBusVariant *variant;
+  variant = g_dbus_variant_new ();
+  g_dbus_variant_set_uint (variant, value);
+  return variant;
+}
+
+/**
+ * g_dbus_variant_new_for_int64:
+ * @value: A #gint64.
+ *
+ * Creates a new variant that holds a #gint64 equal to @value.
+ *
+ * Returns: A new #GDBusVariant. Free with g_object_unref().
+ **/
+GDBusVariant *
+g_dbus_variant_new_for_int64 (gint64 value)
+{
+  GDBusVariant *variant;
+  variant = g_dbus_variant_new ();
+  g_dbus_variant_set_int64 (variant, value);
+  return variant;
+}
+
+/**
+ * g_dbus_variant_new_for_uint64:
+ * @value: A #guint64.
+ *
+ * Creates a new variant that holds a #guint64 equal to @value.
+ *
+ * Returns: A new #GDBusVariant. Free with g_object_unref().
+ **/
+GDBusVariant *
+g_dbus_variant_new_for_uint64 (guint64 value)
+{
+  GDBusVariant *variant;
+  variant = g_dbus_variant_new ();
+  g_dbus_variant_set_uint64 (variant, value);
+  return variant;
+}
+
+/**
+ * g_dbus_variant_new_for_boolean:
+ * @value: A #gboolean.
+ *
+ * Creates a new variant that holds a #gboolean equal to @value.
+ *
+ * Returns: A new #GDBusVariant. Free with g_object_unref().
+ **/
+GDBusVariant *
+g_dbus_variant_new_for_boolean (gboolean value)
+{
+  GDBusVariant *variant;
+  variant = g_dbus_variant_new ();
+  g_dbus_variant_set_boolean (variant, value);
+  return variant;
+}
+
+/**
+ * g_dbus_variant_new_for_double:
+ * @value: A #gdouble.
+ *
+ * Creates a new variant that holds a #gdouble equal to @value.
+ *
+ * Returns: A new #GDBusVariant. Free with g_object_unref().
+ **/
+GDBusVariant *
+g_dbus_variant_new_for_double (gdouble value)
+{
+  GDBusVariant *variant;
+  variant = g_dbus_variant_new ();
+  g_dbus_variant_set_double (variant, value);
+  return variant;
+}
+
+/**
+ * g_dbus_variant_new_for_array:
+ * @array: A #GArray.
+ * @element_signature: D-Bus signature of the elements stored in @array.
+ *
+ * Creates a new variant that holds a reference to @array.
+ *
+ * Returns: A new #GDBusVariant. Free with g_object_unref().
+ **/
+GDBusVariant *
+g_dbus_variant_new_for_array (GArray      *array,
+                              const gchar *element_signature)
+{
+  GDBusVariant *variant;
+  variant = g_dbus_variant_new ();
+  g_dbus_variant_set_array (variant, array, element_signature);
+  return variant;
+}
+
+/**
+ * g_dbus_variant_new_for_ptr_array:
+ * @array: A #GPtrArray.
+ * @element_signature: D-Bus signature of the elements stored in @array.
+ *
+ * Creates a new variant that holds a reference to @array.
+ *
+ * Returns: A new #GDBusVariant. Free with g_object_unref().
+ **/
+GDBusVariant *
+g_dbus_variant_new_for_ptr_array (GPtrArray   *array,
+                                  const gchar *element_signature)
+{
+  GDBusVariant *variant;
+  variant = g_dbus_variant_new ();
+  g_dbus_variant_set_ptr_array (variant, array, element_signature);
+  return variant;
+}
+
+/**
+ * g_dbus_variant_new_for_hash_table:
+ * @hash_table: A #GHashTable.
+ * @key_signature: D-Bus signature of the keys stored in @hash_table.
+ * @value_signature: D-Bus signature of the values stored in @hash_table.
+ *
+ * Creates a new variant that holds a reference to @hash_table.
+ *
+ * Returns: A new #GDBusVariant. Free with g_object_unref().
+ **/
+GDBusVariant *
+g_dbus_variant_new_for_hash_table (GHashTable   *hash_table,
+                                   const gchar  *key_signature,
+                                   const gchar  *value_signature)
+{
+  GDBusVariant *variant;
+  variant = g_dbus_variant_new ();
+  g_dbus_variant_set_hash_table (variant, hash_table, key_signature, value_signature);
+  return variant;
+}
+
+/**
+ * g_dbus_variant_new_for_structure:
+ * @structure: A #GDBusStructure.
+ *
+ * Creates a new variant that holds a reference to @structure.
+ *
+ * Returns: A new #GDBusVariant. Free with g_object_unref().
+ **/
+GDBusVariant *
+g_dbus_variant_new_for_structure (GDBusStructure *structure)
+{
+  GDBusVariant *variant;
+  variant = g_dbus_variant_new ();
+  g_dbus_variant_set_structure (variant, structure);
+  return variant;
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/**
+ * g_dbus_variant_set_string:
+ * @variant: A #GDBusVariant.
+ * @value: A string.
+ *
+ * Makes @variant hold a copy of @value.
+ **/
+void
+g_dbus_variant_set_string (GDBusVariant *variant,
+                           const gchar  *value)
+{
+  g_return_if_fail (G_IS_DBUS_VARIANT (variant));
+  if (variant->priv->signature != NULL)
+    g_value_unset (&variant->priv->value);
+  g_value_init (&variant->priv->value, G_TYPE_STRING);
+  g_value_set_string (&variant->priv->value, value);
+  set_signature (variant, "s");
+}
+
+/**
+ * g_dbus_variant_set_object_path:
+ * @variant: A #GDBusVariant.
+ * @value: An object path.
+ *
+  * Makes @variant hold a copy of @value.
+ **/
+void
+g_dbus_variant_set_object_path (GDBusVariant *variant,
+                                const gchar  *value)
+{
+  g_return_if_fail (G_IS_DBUS_VARIANT (variant));
+  if (variant->priv->signature != NULL)
+    g_value_unset (&variant->priv->value);
+  g_value_init (&variant->priv->value, G_TYPE_STRING); //TODO:o G_DBUS_TYPE_OBJECT_PATH);
+  g_value_set_string (&variant->priv->value, value);
+  set_signature (variant, "o");
+}
+
+/**
+ * g_dbus_variant_set_signature:
+ * @variant: A #GDBusVariant.
+ * @value: A signature.
+ *
+  * Makes @variant hold a copy of @value.
+ **/
+void
+g_dbus_variant_set_signature (GDBusVariant *variant,
+                              const gchar  *value)
+{
+  g_return_if_fail (G_IS_DBUS_VARIANT (variant));
+  if (variant->priv->signature != NULL)
+    g_value_unset (&variant->priv->value);
+  g_value_init (&variant->priv->value, G_TYPE_STRING); //TODO:g G_DBUS_TYPE_OBJECT_PATH);
+  g_value_set_string (&variant->priv->value, value);
+  set_signature (variant, "g");
+}
+
+/**
+ * g_dbus_variant_set_string_array:
+ * @variant: A #GDBusVariant.
+ * @value: A %NULL-terminated string array.
+ *
+  * Makes @variant hold a copy of @value.
+ **/
+void
+g_dbus_variant_set_string_array (GDBusVariant  *variant,
+                                 gchar        **value)
+{
+  g_return_if_fail (G_IS_DBUS_VARIANT (variant));
+  if (variant->priv->signature != NULL)
+    g_value_unset (&variant->priv->value);
+  g_value_init (&variant->priv->value, G_TYPE_STRV);
+  g_value_set_boxed (&variant->priv->value, value);
+  set_signature (variant, "as");
+}
+
+/**
+ * g_dbus_variant_set_object_path_array:
+ * @variant: A #GDBusVariant.
+ * @value: A %NULL-terminated object path array.
+ *
+  * Makes @variant hold a copy of @value.
+ **/
+void
+g_dbus_variant_set_object_path_array (GDBusVariant  *variant,
+                                      gchar        **value)
+{
+  g_return_if_fail (G_IS_DBUS_VARIANT (variant));
+  if (variant->priv->signature != NULL)
+    g_value_unset (&variant->priv->value);
+  g_value_init (&variant->priv->value, G_TYPE_STRV); //TODO:o G_DBUS_TYPE_OBJECT_PATH);
+  g_value_set_boxed (&variant->priv->value, value);
+  set_signature (variant, "ao");
+}
+
+/**
+ * g_dbus_variant_set_signature_array:
+ * @variant: A #GDBusVariant.
+ * @value: A %NULL-terminated signature array.
+ *
+  * Makes @variant hold a copy of @value.
+ **/
+void
+g_dbus_variant_set_signature_array (GDBusVariant  *variant,
+                                    gchar        **value)
+{
+  g_return_if_fail (G_IS_DBUS_VARIANT (variant));
+  if (variant->priv->signature != NULL)
+    g_value_unset (&variant->priv->value);
+  g_value_init (&variant->priv->value, G_TYPE_STRV); //TODO:g G_DBUS_TYPE_SIGNATURE);
+  g_value_set_boxed (&variant->priv->value, value);
+  set_signature (variant, "ag");
+}
+
+/**
+ * g_dbus_variant_set_byte:
+ * @variant: A #GDBusVariant.
+ * @value: A #guchar.
+ *
+  * Makes @variant hold a #guchar equal to @value.
+ **/
+void
+g_dbus_variant_set_byte (GDBusVariant *variant,
+                         guchar        value)
+{
+  g_return_if_fail (G_IS_DBUS_VARIANT (variant));
+  if (variant->priv->signature != NULL)
+    g_value_unset (&variant->priv->value);
+  g_value_init (&variant->priv->value, G_TYPE_UCHAR);
+  g_value_set_uchar (&variant->priv->value, value);
+  set_signature (variant, "y");
+}
+
+/**
+ * g_dbus_variant_set_int16:
+ * @variant: A #GDBusVariant.
+ * @value: A #gint16.
+ *
+  * Makes @variant hold a #gint16 equal to @value.
+ **/
+void
+g_dbus_variant_set_int16 (GDBusVariant *variant,
+                          gint16        value)
+{
+  g_return_if_fail (G_IS_DBUS_VARIANT (variant));
+  if (variant->priv->signature != NULL)
+    g_value_unset (&variant->priv->value);
+  g_value_init (&variant->priv->value, G_TYPE_INT); //TODO:16 DBUS_TYPE_INT16);
+  g_value_set_int (&variant->priv->value, value);
+  set_signature (variant, "n");
+}
+
+/**
+ * g_dbus_variant_set_uint16:
+ * @variant: A #GDBusVariant.
+ * @value: A #guint16.
+ *
+  * Makes @variant hold a #guint64 equal to @value.
+ **/
+void
+g_dbus_variant_set_uint16 (GDBusVariant *variant,
+                           guint16       value)
+{
+  g_return_if_fail (G_IS_DBUS_VARIANT (variant));
+  if (variant->priv->signature != NULL)
+    g_value_unset (&variant->priv->value);
+  g_value_init (&variant->priv->value, G_TYPE_UINT); //TODO:16 G_DBUS_TYPE_UINT16);
+  g_value_set_uint (&variant->priv->value, value);
+  set_signature (variant, "q");
+}
+
+/**
+ * g_dbus_variant_set_int:
+ * @variant: A #GDBusVariant.
+ * @value: A #gint.
+ *
+  * Makes @variant hold a #gint equal to @value.
+ **/
+void
+g_dbus_variant_set_int (GDBusVariant *variant,
+                          gint        value)
+{
+  g_return_if_fail (G_IS_DBUS_VARIANT (variant));
+  if (variant->priv->signature != NULL)
+    g_value_unset (&variant->priv->value);
+  g_value_init (&variant->priv->value, G_TYPE_INT);
+  g_value_set_int (&variant->priv->value, value);
+  set_signature (variant, "i");
+}
+
+/**
+ * g_dbus_variant_set_uint:
+ * @variant: A #GDBusVariant.
+ * @value: A #guint.
+ *
+  * Makes @variant hold a #guint equal to @value.
+ **/
+void
+g_dbus_variant_set_uint (GDBusVariant *variant,
+                           guint       value)
+{
+  g_return_if_fail (G_IS_DBUS_VARIANT (variant));
+  if (variant->priv->signature != NULL)
+    g_value_unset (&variant->priv->value);
+  g_value_init (&variant->priv->value, G_TYPE_UINT);
+  g_value_set_uint (&variant->priv->value, value);
+  set_signature (variant, "u");
+}
+
+/**
+ * g_dbus_variant_set_int64:
+ * @variant: A #GDBusVariant.
+ * @value: A #gint64.
+ *
+  * Makes @variant hold a #gint64 equal to @value.
+ **/
+void
+g_dbus_variant_set_int64 (GDBusVariant *variant,
+                          gint64        value)
+{
+  g_return_if_fail (G_IS_DBUS_VARIANT (variant));
+  if (variant->priv->signature != NULL)
+    g_value_unset (&variant->priv->value);
+  g_value_init (&variant->priv->value, G_TYPE_INT64);
+  g_value_set_int64 (&variant->priv->value, value);
+  set_signature (variant, "x");
+}
+
+/**
+ * g_dbus_variant_set_uint64:
+ * @variant: A #GDBusVariant.
+ * @value: A #guint64.
+ *
+  * Makes @variant hold a #guint64 equal to @value.
+ **/
+void
+g_dbus_variant_set_uint64 (GDBusVariant *variant,
+                           guint64       value)
+{
+  g_return_if_fail (G_IS_DBUS_VARIANT (variant));
+  if (variant->priv->signature != NULL)
+    g_value_unset (&variant->priv->value);
+  g_value_init (&variant->priv->value, G_TYPE_UINT64);
+  g_value_set_uint64 (&variant->priv->value, value);
+  set_signature (variant, "t");
+}
+
+/**
+ * g_dbus_variant_set_boolean:
+ * @variant: A #GDBusVariant.
+ * @value: A #gboolean.
+ *
+  * Makes @variant hold a #gboolean equal to @value.
+ **/
+void
+g_dbus_variant_set_boolean (GDBusVariant *variant,
+                            gboolean      value)
+{
+  g_return_if_fail (G_IS_DBUS_VARIANT (variant));
+  if (variant->priv->signature != NULL)
+    g_value_unset (&variant->priv->value);
+  g_value_init (&variant->priv->value, G_TYPE_BOOLEAN);
+  g_value_set_boolean (&variant->priv->value, value);
+  set_signature (variant, "b");
+}
+
+/**
+ * g_dbus_variant_set_double:
+ * @variant: A #GDBusVariant.
+ * @value: A #gdouble.
+ *
+  * Makes @variant hold a #gdouble equal to @value.
+ **/
+void
+g_dbus_variant_set_double (GDBusVariant *variant,
+                           gdouble       value)
+{
+  g_return_if_fail (G_IS_DBUS_VARIANT (variant));
+  if (variant->priv->signature != NULL)
+    g_value_unset (&variant->priv->value);
+  g_value_init (&variant->priv->value, G_TYPE_DOUBLE);
+  g_value_set_double (&variant->priv->value, value);
+  set_signature (variant, "d");
+}
+
+/**
+ * g_dbus_variant_set_array:
+ * @variant: A #GDBusVariant.
+ * @array: A #GArray.
+ * @element_signature: D-Bus signature of the elements stored in @array.
+ *
+  * Makes @variant hold a reference to @array.
+ **/
+void
+g_dbus_variant_set_array (GDBusVariant   *variant,
+                          GArray         *array,
+                          const gchar    *element_signature)
+{
+  g_return_if_fail (G_IS_DBUS_VARIANT (variant));
+  if (variant->priv->signature != NULL)
+    g_value_unset (&variant->priv->value);
+  g_value_init (&variant->priv->value, G_TYPE_ARRAY);
+  g_value_set_boxed (&variant->priv->value, array);
+  set_signature_for_array (variant, element_signature);
+}
+
+/**
+ * g_dbus_variant_set_ptr_array:
+ * @variant: A #GDBusVariant.
+ * @array: A #GPtrArray.
+ * @element_signature: D-Bus signature of the elements stored in @array.
+ *
+  * Makes @variant hold a reference to @array.
+ **/
+void
+g_dbus_variant_set_ptr_array (GDBusVariant   *variant,
+                              GPtrArray      *array,
+                              const gchar    *element_signature)
+{
+  g_return_if_fail (G_IS_DBUS_VARIANT (variant));
+  if (variant->priv->signature != NULL)
+    g_value_unset (&variant->priv->value);
+  g_value_init (&variant->priv->value, G_TYPE_PTR_ARRAY);
+  g_value_set_boxed (&variant->priv->value, array);
+  set_signature_for_array (variant, element_signature);
+}
+
+/**
+ * g_dbus_variant_set_hash_table:
+ * @variant: A #GDBusVariant.
+ * @hash_table: A #GHashTable.
+ * @key_signature: D-Bus signature of the keys stored in @hash_table.
+ * @value_signature: D-Bus signature of the values stored in @hash_table.
+ *
+  * Makes @variant hold a reference to @hash_table.
+ **/
+void
+g_dbus_variant_set_hash_table (GDBusVariant *variant,
+                               GHashTable   *hash_table,
+                               const gchar  *key_signature,
+                               const gchar  *value_signature)
+{
+  g_return_if_fail (G_IS_DBUS_VARIANT (variant));
+  if (variant->priv->signature != NULL)
+    g_value_unset (&variant->priv->value);
+  g_value_init (&variant->priv->value, G_TYPE_HASH_TABLE);
+  g_value_set_boxed (&variant->priv->value, hash_table);
+  set_signature_for_hash_table (variant,
+                                key_signature,
+                                value_signature);
+}
+
+/**
+ * g_dbus_variant_set_structure:
+ * @variant: A #GDBusVariant.
+ * @structure: A #GDBusStructure.
+ *
+ * Makes @variant hold a reference to @structure.
+ **/
+void
+g_dbus_variant_set_structure (GDBusVariant   *variant,
+                              GDBusStructure *structure)
+{
+  g_return_if_fail (G_IS_DBUS_VARIANT (variant));
+  if (variant->priv->signature != NULL)
+    g_value_unset (&variant->priv->value);
+  g_value_init (&variant->priv->value, G_TYPE_DBUS_STRUCTURE);
+  g_value_set_object (&variant->priv->value, structure);
+  set_signature (variant, g_dbus_structure_get_signature (structure));
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/**
+ * g_dbus_variant_get_string:
+ * @variant: A #GDBusVariant.
+ *
+ * Gets the string stored in @variant.
+ *
+ * Returns: A string owned by @variant. Do not free.
+ **/
+const gchar *
+g_dbus_variant_get_string (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant) && g_dbus_variant_is_string (variant), NULL);
+  return g_value_get_string (&variant->priv->value);
+}
+
+/**
+ * g_dbus_variant_get_object_path:
+ * @variant: A #GDBusVariant.
+ *
+ * Gets the object path stored in @variant.
+ *
+ * Returns: An object path owned by @variant. Do not free.
+ **/
+const gchar *
+g_dbus_variant_get_object_path (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant) && g_dbus_variant_is_object_path (variant), NULL);
+  return g_value_get_string (&variant->priv->value); // TODO:o
+}
+
+/**
+ * g_dbus_variant_get_signature:
+ * @variant: A #GDBusVariant.
+ *
+ * Gets the signature stored in @variant.
+ *
+ * Returns: A signature owned by @variant. Do not free.
+ **/
+const gchar *
+g_dbus_variant_get_signature (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant) && g_dbus_variant_is_signature (variant), NULL);
+  return g_value_get_string (&variant->priv->value); // TODO:g
+}
+
+/**
+ * g_dbus_variant_get_string_array:
+ * @variant: A #GDBusVariant.
+ *
+ * Gets the string array stored in @variant.
+ *
+ * Returns: A %NULL-terminated string array signature owned by @variant. Do not free.
+ **/
+gchar **
+g_dbus_variant_get_string_array (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant) && g_dbus_variant_is_string_array (variant), NULL);
+  return g_value_get_boxed (&variant->priv->value);
+}
+
+/**
+ * g_dbus_variant_get_object_path_array:
+ * @variant: A #GDBusVariant.
+ *
+ * Gets the object path array stored in @variant.
+ *
+ * Returns: A %NULL-terminated object path array owned by @variant. Do not free.
+ **/
+gchar **
+g_dbus_variant_get_object_path_array (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant) && g_dbus_variant_is_object_path_array (variant), NULL);
+  return g_value_get_boxed (&variant->priv->value);
+}
+
+/**
+ * g_dbus_variant_get_signature_array:
+ * @variant: A #GDBusVariant.
+ *
+ * Gets the signature array stored in @variant.
+ *
+ * Returns: A %NULL-terminated signature array owned by @variant. Do not free.
+ **/
+gchar **
+g_dbus_variant_get_signature_array (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant) && g_dbus_variant_is_signature_array (variant), NULL);
+  return g_value_get_boxed (&variant->priv->value);
+}
+
+/**
+ * g_dbus_variant_get_byte:
+ * @variant: A #GDBusVariant.
+ *
+ * Gets the #guchar stored in @variant.
+ *
+ * Returns: A #guchar.
+ **/
+guchar
+g_dbus_variant_get_byte (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant) && g_dbus_variant_is_byte (variant), 0);
+  return g_value_get_uchar (&variant->priv->value);
+}
+
+/**
+ * g_dbus_variant_get_int16:
+ * @variant: A #GDBusVariant.
+ *
+ * Gets the #gint16 stored in @variant.
+ *
+ * Returns: A #gint16.
+ **/
+gint16
+g_dbus_variant_get_int16 (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant) && g_dbus_variant_is_int16 (variant), 0);
+  return g_value_get_int (&variant->priv->value); // TODO:16
+}
+
+/**
+ * g_dbus_variant_get_uint16:
+ * @variant: A #GDBusVariant.
+ *
+ * Gets the #guint16 stored in @variant.
+ *
+ * Returns: A #guint16.
+ **/
+guint16
+g_dbus_variant_get_uint16 (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant) && g_dbus_variant_is_uint16 (variant), 0);
+  return g_value_get_uint (&variant->priv->value); // TODO:16
+}
+
+/**
+ * g_dbus_variant_get_int:
+ * @variant: A #GDBusVariant.
+ *
+ * Gets the #gint stored in @variant.
+ *
+ * Returns: A #gint.
+ **/
+gint
+g_dbus_variant_get_int (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant) && g_dbus_variant_is_int (variant), 0);
+  return g_value_get_int (&variant->priv->value);
+}
+
+/**
+ * g_dbus_variant_get_uint:
+ * @variant: A #GDBusVariant.
+ *
+ * Gets the #guint stored in @variant.
+ *
+ * Returns: A #guint.
+ **/
+guint
+g_dbus_variant_get_uint (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant) && g_dbus_variant_is_uint (variant), 0);
+  return g_value_get_uint (&variant->priv->value);
+}
+
+/**
+ * g_dbus_variant_get_int64:
+ * @variant: A #GDBusVariant.
+ *
+ * Gets the #gint64 stored in @variant.
+ *
+ * Returns: A #gint64.
+ **/
+gint64
+g_dbus_variant_get_int64 (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant) && g_dbus_variant_is_int64 (variant), 0);
+  return g_value_get_int64 (&variant->priv->value);
+}
+
+/**
+ * g_dbus_variant_get_uint64:
+ * @variant: A #GDBusVariant.
+ *
+ * Gets the #guint64 stored in @variant.
+ *
+ * Returns: A #guint64.
+ **/
+guint64
+g_dbus_variant_get_uint64 (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant) && g_dbus_variant_is_uint64 (variant), 0);
+  return g_value_get_uint64 (&variant->priv->value);
+}
+
+/**
+ * g_dbus_variant_get_boolean:
+ * @variant: A #GDBusVariant.
+ *
+ * Gets the #gboolean stored in @variant.
+ *
+ * Returns: A #gboolean.
+ **/
+gboolean
+g_dbus_variant_get_boolean (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant) && g_dbus_variant_is_boolean (variant), FALSE);
+  return g_value_get_boolean (&variant->priv->value);
+}
+
+/**
+ * g_dbus_variant_get_double:
+ * @variant: A #GDBusVariant.
+ *
+ * Gets the #gdouble stored in @variant.
+ *
+ * Returns: A #gdouble.
+ **/
+gdouble
+g_dbus_variant_get_double (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant) && g_dbus_variant_is_double (variant), 0.0);
+  return g_value_get_double (&variant->priv->value);
+}
+
+/**
+ * g_dbus_variant_get_array:
+ * @variant: A #GDBusVariant.
+ *
+ * Gets the array stored in @variant.
+ *
+ * Returns: A #GArray owned by @variant. Do not free.
+ **/
+GArray *
+g_dbus_variant_get_array (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant) && g_dbus_variant_is_array (variant), NULL);
+  return g_value_get_boxed (&variant->priv->value);
+}
+
+/**
+ * g_dbus_variant_get_ptr_array:
+ * @variant: A #GDBusVariant.
+ *
+ * Gets the pointer array stored in @variant.
+ *
+ * Returns: A #GPtrArray owned by @variant. Do not free.
+ **/
+GPtrArray *
+g_dbus_variant_get_ptr_array (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant) && g_dbus_variant_is_ptr_array (variant), NULL);
+  return g_value_get_boxed (&variant->priv->value);
+}
+
+/**
+ * g_dbus_variant_get_hash_table:
+ * @variant: A #GDBusVariant.
+ *
+ * Gets the hash table stored in @variant.
+ *
+ * Returns: A #GHashTable owned by @variant. Do not free.
+ **/
+GHashTable  *
+g_dbus_variant_get_hash_table (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant) && g_dbus_variant_is_hash_table (variant), NULL);
+  return g_value_get_boxed (&variant->priv->value);
+}
+
+/**
+ * g_dbus_variant_get_structure:
+ * @variant: A #GDBusVariant.
+ *
+ * Gets the structure stored in @variant.
+ *
+ * Returns: A #GDBusStructure owned by @variant. Do not free.
+ **/
+GDBusStructure *
+g_dbus_variant_get_structure (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant) && g_dbus_variant_is_structure (variant), NULL);
+  return g_value_get_object (&variant->priv->value);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+/**
+ * g_dbus_variant_is_unset:
+ * @variant: A #GDBusVariant.
+ *
+ * Checks if @variant holds a value.
+ *
+ * Returns: %TRUE if @variant does not hold a value, %FALSE is @variant holds a value.
+ **/
+gboolean
+g_dbus_variant_is_unset (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant), FALSE);
+  return variant->priv->signature != NULL;
+}
+
+/**
+ * g_dbus_variant_is_string:
+ * @variant: A #GDBusVariant.
+ *
+ * Checks if @variant holds a string.
+ *
+ * Returns: %TRUE only if @variant holds a string.
+ **/
+gboolean
+g_dbus_variant_is_string (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant), FALSE);
+  return
+    variant->priv->signature != NULL &&
+    variant->priv->signature[0] == 's';
+}
+
+/**
+ * g_dbus_variant_is_object_path:
+ * @variant: A #GDBusVariant.
+ *
+ * Checks if @variant holds an object path.
+ *
+ * Returns: %TRUE only if @variant holds an object path.
+ **/
+gboolean
+g_dbus_variant_is_object_path (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant), FALSE);
+  return
+    variant->priv->signature != NULL &&
+    variant->priv->signature[0] == 'o';
+}
+
+/**
+ * g_dbus_variant_is_signature
+ * @variant: A #GDBusVariant.
+ *
+ * Checks if @variant holds an signature.
+ *
+ * Returns: %TRUE only if @variant holds an signature.
+ **/
+gboolean
+g_dbus_variant_is_signature (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant), FALSE);
+  return
+    variant->priv->signature != NULL &&
+    variant->priv->signature[0] == 'g';
+}
+
+/**
+ * g_dbus_variant_is_string_array:
+ * @variant: A #GDBusVariant.
+ *
+ * Checks if @variant holds a string array.
+ *
+ * Returns: %TRUE only if @variant holds a string array.
+ **/
+gboolean
+g_dbus_variant_is_string_array (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant), FALSE);
+  return
+    variant->priv->signature != NULL &&
+    variant->priv->signature[0] == 'a' &&
+    variant->priv->signature[1] == 's';
+}
+
+/**
+ * g_dbus_variant_is_object_path_array:
+ * @variant: A #GDBusVariant.
+ *
+ * Checks if @variant holds an object path array.
+ *
+ * Returns: %TRUE only if @variant holds an object path array.
+ **/
+gboolean
+g_dbus_variant_is_object_path_array (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant), FALSE);
+  return
+    variant->priv->signature != NULL &&
+    variant->priv->signature[0] == 'a' &&
+    variant->priv->signature[1] == 'o';
+}
+
+/**
+ * g_dbus_variant_is_signature_array:
+ * @variant: A #GDBusVariant.
+ *
+ * Checks if @variant holds a signature array.
+ *
+ * Returns: %TRUE only if @variant holds a signature array.
+ **/
+gboolean
+g_dbus_variant_is_signature_array (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant), FALSE);
+  return
+    variant->priv->signature != NULL &&
+    variant->priv->signature[0] == 'a' &&
+    variant->priv->signature[1] == 'g';
+}
+
+/**
+ * g_dbus_variant_is_byte:
+ * @variant: A #GDBusVariant.
+ *
+ * Checks if @variant holds a #guchar.
+ *
+ * Returns: %TRUE only if @variant holds a #guchar.
+ **/
+gboolean
+g_dbus_variant_is_byte (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant), FALSE);
+  return
+    variant->priv->signature != NULL &&
+    variant->priv->signature[0] == 'y';
+}
+
+/**
+ * g_dbus_variant_is_int16:
+ * @variant: A #GDBusVariant.
+ *
+ * Checks if @variant holds a #gint16.
+ *
+ * Returns: %TRUE only if @variant holds a #gint16.
+ **/
+gboolean
+g_dbus_variant_is_int16 (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant), FALSE);
+  return
+    variant->priv->signature != NULL &&
+    variant->priv->signature[0] == 'n';
+}
+
+/**
+ * g_dbus_variant_is_uint16:
+ * @variant: A #GDBusVariant.
+ *
+ * Checks if @variant holds a #guint16.
+ *
+ * Returns: %TRUE only if @variant holds a #guint16.
+ **/
+gboolean
+g_dbus_variant_is_uint16 (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant), FALSE);
+  return
+    variant->priv->signature != NULL &&
+    variant->priv->signature[0] == 'q';
+}
+
+/**
+ * g_dbus_variant_is_int:
+ * @variant: A #GDBusVariant.
+ *
+ * Checks if @variant holds a #gint.
+ *
+ * Returns: %TRUE only if @variant holds a #gint.
+ **/
+gboolean
+g_dbus_variant_is_int (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant), FALSE);
+  return
+    variant->priv->signature != NULL &&
+    variant->priv->signature[0] == 'i';
+}
+
+/**
+ * g_dbus_variant_is_uint:
+ * @variant: A #GDBusVariant.
+ *
+ * Checks if @variant holds a #guint.
+ *
+ * Returns: %TRUE only if @variant holds a #guint.
+ **/
+gboolean
+g_dbus_variant_is_uint (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant), FALSE);
+  return
+    variant->priv->signature != NULL &&
+    variant->priv->signature[0] == 'u';
+}
+
+/**
+ * g_dbus_variant_is_int64:
+ * @variant: A #GDBusVariant.
+ *
+ * Checks if @variant holds a #gint64.
+ *
+ * Returns: %TRUE only if @variant holds a #gint64.
+ **/
+gboolean
+g_dbus_variant_is_int64 (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant), FALSE);
+  return
+    variant->priv->signature != NULL &&
+    variant->priv->signature[0] == 'x';
+}
+
+/**
+ * g_dbus_variant_is_uint64:
+ * @variant: A #GDBusVariant.
+ *
+ * Checks if @variant holds a #guint64.
+ *
+ * Returns: %TRUE only if @variant holds a #guint64.
+ **/
+gboolean
+g_dbus_variant_is_uint64 (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant), FALSE);
+  return
+    variant->priv->signature != NULL &&
+    variant->priv->signature[0] == 't';
+}
+
+/**
+ * g_dbus_variant_is_boolean:
+ * @variant: A #GDBusVariant.
+ *
+ * Checks if @variant holds a #gboolean.
+ *
+ * Returns: %TRUE only if @variant holds a #gboolean.
+ **/
+gboolean
+g_dbus_variant_is_boolean (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant), FALSE);
+  return
+    variant->priv->signature != NULL &&
+    variant->priv->signature[0] == 'b';
+}
+
+/**
+ * g_dbus_variant_is_double:
+ * @variant: A #GDBusVariant.
+ *
+ * Checks if @variant holds a #gdouble.
+ *
+ * Returns: %TRUE only if @variant holds a #gdouble.
+ **/
+gboolean
+g_dbus_variant_is_double (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant), FALSE);
+  return
+    variant->priv->signature != NULL &&
+    variant->priv->signature[0] == 'd';
+}
+
+/**
+ * g_dbus_variant_is_array:
+ * @variant: A #GDBusVariant.
+ *
+ * Checks if @variant holds an array.
+ *
+ * Returns: %TRUE only if @variant holds an array.
+ **/
+gboolean
+g_dbus_variant_is_array (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant), FALSE);
+  return
+    variant->priv->signature != NULL &&
+    variant->priv->signature[0] == 'a' &&
+    (variant->priv->signature[1] == 'y' ||
+     variant->priv->signature[1] == 'n' ||
+     variant->priv->signature[1] == 'q' ||
+     variant->priv->signature[1] == 'i' ||
+     variant->priv->signature[1] == 'x' ||
+     variant->priv->signature[1] == 't' ||
+     variant->priv->signature[1] == 'd' ||
+     variant->priv->signature[1] == 'b');
+}
+
+/**
+ * g_dbus_variant_is_ptr_array:
+ * @variant: A #GDBusVariant.
+ *
+ * Checks if @variant holds a pointer array.
+ *
+ * Returns: %TRUE only if @variant holds a pointer array.
+ **/
+gboolean
+g_dbus_variant_is_ptr_array (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant), FALSE);
+  return
+    variant->priv->signature != NULL &&
+    variant->priv->signature[0] == 'a' &&
+    (variant->priv->signature[1] == 'a' ||
+     variant->priv->signature[1] == 'v' ||
+     variant->priv->signature[1] == '(');
+}
+
+/**
+ * g_dbus_variant_is_hash_table:
+ * @variant: A #GDBusVariant.
+ *
+ * Checks if @variant holds a hash table.
+ *
+ * Returns: %TRUE only if @variant holds a hash table.
+ **/
+gboolean
+g_dbus_variant_is_hash_table (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant), FALSE);
+  return
+    variant->priv->signature != NULL &&
+    variant->priv->signature[0] == 'a' &&
+    variant->priv->signature[1] == '{';
+}
+
+/**
+ * g_dbus_variant_is_structure:
+ * @variant: A #GDBusVariant.
+ *
+ * Checks if @variant holds a structure.
+ *
+ * Returns: %TRUE only if @variant holds a structure.
+ **/
+gboolean
+g_dbus_variant_is_structure (GDBusVariant *variant)
+{
+  g_return_val_if_fail (G_IS_DBUS_VARIANT (variant), FALSE);
+  return
+    variant->priv->signature != NULL &&
+    variant->priv->signature[0] == '(';
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+#define __G_DBUS_VARIANT_C__
+#include "gdbusaliasdef.c"
diff --git a/gdbus/gdbusvariant.h b/gdbus/gdbusvariant.h
new file mode 100644
index 0000000..d455642
--- /dev/null
+++ b/gdbus/gdbusvariant.h
@@ -0,0 +1,201 @@
+/* GDBus - GLib D-Bus Library
+ *
+ * Copyright (C) 2008-2009 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place, Suite 330,
+ * Boston, MA 02111-1307, USA.
+ *
+ * Author: David Zeuthen <davidz redhat com>
+ */
+
+#if !defined (__G_DBUS_G_DBUS_H_INSIDE__) && !defined (G_DBUS_COMPILATION)
+#error "Only <gdbus/gdbus.h> can be included directly."
+#endif
+
+#ifndef __G_DBUS_VARIANT_H__
+#define __G_DBUS_VARIANT_H__
+
+#include <gdbus/gdbustypes.h>
+#include <dbus/dbus.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_DBUS_VARIANT         (g_dbus_variant_get_type ())
+#define G_DBUS_VARIANT(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_DBUS_VARIANT, GDBusVariant))
+#define G_DBUS_VARIANT_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_DBUS_VARIANT, GDBusVariantClass))
+#define G_DBUS_VARIANT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_DBUS_VARIANT, GDBusVariantClass))
+#define G_IS_DBUS_VARIANT(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_DBUS_VARIANT))
+#define G_IS_DBUS_VARIANT_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_DBUS_VARIANT))
+
+typedef struct _GDBusVariantClass   GDBusVariantClass;
+typedef struct _GDBusVariantPrivate GDBusVariantPrivate;
+
+/**
+ * GDBusVariant:
+ *
+ * The #GDBusVariant structure contains only private data and
+ * should only be accessed using the provided API.
+ */
+struct _GDBusVariant
+{
+  /*< private >*/
+  GObject parent_instance;
+  GDBusVariantPrivate *priv;
+};
+
+/**
+ * GDBusVariantClass:
+ *
+ * Class structure for #GDBusVariant.
+ */
+struct _GDBusVariantClass
+{
+  /*< private >*/
+  GObjectClass parent_class;
+
+  /*< public >*/
+
+  /*< private >*/
+  /* Padding for future expansion */
+  void (*_g_reserved1) (void);
+  void (*_g_reserved2) (void);
+  void (*_g_reserved3) (void);
+  void (*_g_reserved4) (void);
+  void (*_g_reserved5) (void);
+  void (*_g_reserved6) (void);
+  void (*_g_reserved7) (void);
+  void (*_g_reserved8) (void);
+};
+
+GType            g_dbus_variant_get_type                  (void) G_GNUC_CONST;
+const gchar     *g_dbus_variant_get_variant_signature     (GDBusVariant    *variant);
+gboolean         g_dbus_variant_equal                     (GDBusVariant    *variant1,
+                                                           GDBusVariant    *variant2);
+/* constructors */
+GDBusVariant    *g_dbus_variant_new                       (void);
+GDBusVariant    *g_dbus_variant_new_for_string            (const gchar     *value);
+GDBusVariant    *g_dbus_variant_new_for_object_path       (const gchar     *value);
+GDBusVariant    *g_dbus_variant_new_for_signature         (const gchar     *value);
+GDBusVariant    *g_dbus_variant_new_for_string_array      (gchar          **value);
+GDBusVariant    *g_dbus_variant_new_for_object_path_array (gchar          **value);
+GDBusVariant    *g_dbus_variant_new_for_signature_array   (gchar          **value);
+GDBusVariant    *g_dbus_variant_new_for_byte              (guchar           value);
+GDBusVariant    *g_dbus_variant_new_for_int16             (gint16           value);
+GDBusVariant    *g_dbus_variant_new_for_uint16            (guint16          value);
+GDBusVariant    *g_dbus_variant_new_for_int               (gint             value);
+GDBusVariant    *g_dbus_variant_new_for_uint              (guint            value);
+GDBusVariant    *g_dbus_variant_new_for_int64             (gint64           value);
+GDBusVariant    *g_dbus_variant_new_for_uint64            (guint64          value);
+GDBusVariant    *g_dbus_variant_new_for_boolean           (gboolean         value);
+GDBusVariant    *g_dbus_variant_new_for_double            (gdouble          value);
+GDBusVariant    *g_dbus_variant_new_for_array             (GArray          *array,
+                                                           const gchar     *element_signature);
+GDBusVariant    *g_dbus_variant_new_for_ptr_array         (GPtrArray       *array,
+                                                           const gchar     *element_signature);
+GDBusVariant    *g_dbus_variant_new_for_hash_table        (GHashTable      *hash_table,
+                                                           const gchar     *key_signature,
+                                                           const gchar     *value_signature);
+GDBusVariant    *g_dbus_variant_new_for_structure         (GDBusStructure  *structure);
+
+/* getters */
+const gchar     *g_dbus_variant_get_string            (GDBusVariant   *variant);
+const gchar     *g_dbus_variant_get_object_path       (GDBusVariant   *variant);
+const gchar     *g_dbus_variant_get_signature         (GDBusVariant   *variant);
+gchar          **g_dbus_variant_get_string_array      (GDBusVariant   *variant);
+gchar          **g_dbus_variant_get_object_path_array (GDBusVariant   *variant);
+gchar          **g_dbus_variant_get_signature_array   (GDBusVariant   *variant);
+guchar           g_dbus_variant_get_byte              (GDBusVariant   *variant);
+gint16           g_dbus_variant_get_int16             (GDBusVariant   *variant);
+guint16          g_dbus_variant_get_uint16            (GDBusVariant   *variant);
+gint             g_dbus_variant_get_int               (GDBusVariant   *variant);
+guint            g_dbus_variant_get_uint              (GDBusVariant   *variant);
+gint64           g_dbus_variant_get_int64             (GDBusVariant   *variant);
+guint64          g_dbus_variant_get_uint64            (GDBusVariant   *variant);
+gboolean         g_dbus_variant_get_boolean           (GDBusVariant   *variant);
+gdouble          g_dbus_variant_get_double            (GDBusVariant   *variant);
+GArray          *g_dbus_variant_get_array             (GDBusVariant   *variant);
+GPtrArray       *g_dbus_variant_get_ptr_array         (GDBusVariant   *variant);
+GHashTable      *g_dbus_variant_get_hash_table        (GDBusVariant   *variant);
+GDBusStructure  *g_dbus_variant_get_structure         (GDBusVariant   *variant);
+
+/* setters */
+void             g_dbus_variant_set_string            (GDBusVariant   *variant,
+                                                       const gchar    *value);
+void             g_dbus_variant_set_object_path       (GDBusVariant   *variant,
+                                                       const gchar    *value);
+void             g_dbus_variant_set_signature         (GDBusVariant   *variant,
+                                                       const gchar    *value);
+void             g_dbus_variant_set_string_array      (GDBusVariant   *variant,
+                                                       gchar         **value);
+void             g_dbus_variant_set_object_path_array (GDBusVariant   *variant,
+                                                       gchar         **value);
+void             g_dbus_variant_set_signature_array   (GDBusVariant   *variant,
+                                                       gchar         **value);
+void             g_dbus_variant_set_byte              (GDBusVariant   *variant,
+                                                       guchar          value);
+void             g_dbus_variant_set_int16             (GDBusVariant   *variant,
+                                                       gint16          value);
+void             g_dbus_variant_set_uint16            (GDBusVariant   *variant,
+                                                       guint16         value);
+void             g_dbus_variant_set_int               (GDBusVariant   *variant,
+                                                       gint            value);
+void             g_dbus_variant_set_uint              (GDBusVariant   *variant,
+                                                       guint           value);
+void             g_dbus_variant_set_int64             (GDBusVariant   *variant,
+                                                       gint64          value);
+void             g_dbus_variant_set_uint64            (GDBusVariant   *variant,
+                                                       guint64         value);
+void             g_dbus_variant_set_boolean           (GDBusVariant   *variant,
+                                                       gboolean        value);
+void             g_dbus_variant_set_double            (GDBusVariant   *variant,
+                                                       gdouble         value);
+void             g_dbus_variant_set_array             (GDBusVariant   *variant,
+                                                       GArray         *array,
+                                                       const gchar    *element_signature);
+void             g_dbus_variant_set_ptr_array         (GDBusVariant   *variant,
+                                                       GPtrArray      *array,
+                                                       const gchar    *element_signature);
+void             g_dbus_variant_set_hash_table        (GDBusVariant   *variant,
+                                                       GHashTable     *hash_table,
+                                                       const gchar    *key_signature,
+                                                       const gchar    *value_signature);
+void             g_dbus_variant_set_structure         (GDBusVariant   *variant,
+                                                       GDBusStructure *structure);
+
+/* identification */
+gboolean         g_dbus_variant_is_unset              (GDBusVariant     *variant);
+gboolean         g_dbus_variant_is_string             (GDBusVariant     *variant);
+gboolean         g_dbus_variant_is_object_path        (GDBusVariant     *variant);
+gboolean         g_dbus_variant_is_signature          (GDBusVariant     *variant);
+gboolean         g_dbus_variant_is_string_array       (GDBusVariant     *variant);
+gboolean         g_dbus_variant_is_object_path_array  (GDBusVariant     *variant);
+gboolean         g_dbus_variant_is_signature_array    (GDBusVariant     *variant);
+gboolean         g_dbus_variant_is_byte               (GDBusVariant     *variant);
+gboolean         g_dbus_variant_is_int16              (GDBusVariant     *variant);
+gboolean         g_dbus_variant_is_uint16             (GDBusVariant     *variant);
+gboolean         g_dbus_variant_is_int                (GDBusVariant     *variant);
+gboolean         g_dbus_variant_is_uint               (GDBusVariant     *variant);
+gboolean         g_dbus_variant_is_int64              (GDBusVariant     *variant);
+gboolean         g_dbus_variant_is_uint64             (GDBusVariant     *variant);
+gboolean         g_dbus_variant_is_boolean            (GDBusVariant     *variant);
+gboolean         g_dbus_variant_is_double             (GDBusVariant     *variant);
+gboolean         g_dbus_variant_is_array              (GDBusVariant     *variant);
+gboolean         g_dbus_variant_is_ptr_array          (GDBusVariant     *variant);
+gboolean         g_dbus_variant_is_hash_table         (GDBusVariant     *variant);
+gboolean         g_dbus_variant_is_structure          (GDBusVariant     *variant);
+
+G_END_DECLS
+
+#endif /* __G_DBUS_VARIANT_H__ */
diff --git a/gdbus/tests/proxy.c b/gdbus/tests/proxy.c
index d24d4df..2618824 100644
--- a/gdbus/tests/proxy.c
+++ b/gdbus/tests/proxy.c
@@ -32,6 +32,7 @@ static GMainLoop *loop = NULL;
 /* Test GDBusProxy */
 /* ---------------------------------------------------------------------------------------------------- */
 
+
 static void
 proxy_on_name_appeared (GDBusConnection *connection,
                         const gchar     *name,
@@ -41,6 +42,7 @@ proxy_on_name_appeared (GDBusConnection *connection,
   GDBusProxy *frob;
   GError     *error;
   gboolean   ret;
+  guint      n;
   gchar      v_byte;
   gboolean   v_boolean;
   gint       v_int16;
@@ -53,13 +55,89 @@ proxy_on_name_appeared (GDBusConnection *connection,
   gchar     *v_string;
   gchar     *v_object_path;
   gchar     *v_signature;
+  GArray *ay;
+  GArray *ab;
+  GArray *an;
+  GArray *aq;
+  GArray *ai;
+  GArray *au;
+  GArray *ax;
+  GArray *at;
+  GArray *ad;
+  GArray *v_ay;
+  GArray *v_ab;
+  GArray *v_an;
+  GArray *v_aq;
+  GArray *v_ai;
+  GArray *v_au;
+  GArray *v_ax;
+  GArray *v_at;
+  GArray *v_ad;
+  gchar *as[] = {"bla", "baz", NULL};
+  gchar *ao[] = {"/foo", "/bar", NULL};
+  gchar *ag[] = {"ass", "git", NULL};
+  gchar **v_as;
+  gchar **v_ao;
+  gchar **v_ag;
+  GHashTable *hyy;
+  GHashTable *hbb;
+  GHashTable *hnn;
+  GHashTable *hqq;
+  GHashTable *hii;
+  GHashTable *huu;
+  GHashTable *hxx;
+  GHashTable *htt;
+  GHashTable *hdd;
+  GHashTable *hss;
+  GHashTable *hoo;
+  GHashTable *hgg;
+  gint64 v_int64_a = 16;
+  gint64 v_int64_b = 17;
+  guint64 v_uint64_a = 32;
+  guint64 v_uint64_b = 33;
+  gdouble v_double_a = 1.2;
+  gdouble v_double_b = 2.1;
+  GHashTable *v_hyy;
+  GHashTable *v_hbb;
+  GHashTable *v_hnn;
+  GHashTable *v_hqq;
+  GHashTable *v_hii;
+  GHashTable *v_huu;
+  GHashTable *v_hxx;
+  GHashTable *v_htt;
+  GHashTable *v_hdd;
+  GHashTable *v_hss;
+  GHashTable *v_hoo;
+  GHashTable *v_hgg;
+  GDBusStructure *pair;
+  GDBusStructure *complex_struct;
+  GDBusStructure *v_pair;
+  GDBusStructure *v_complex_struct;
+  GDBusStructure *other_struct;
+  GPtrArray *array_of_pairs;
+  GPtrArray *v_array_of_pairs;
+  GDBusVariant *variant;
+  GDBusVariant *v_variant;
+  gchar *v_str0;
+  gchar *v_str1;
+  gchar *v_str2;
+  gchar *v_str3;
+  gchar *v_str4;
+  gchar *v_str5;
+  gchar *v_str6;
+  gchar *v_str7;
+  gchar *v_str8;
 
   error = NULL;
 
   frob = g_dbus_proxy_new (connection,
                            name,
                            "/com/example/TestObject",
-                           "com.example.Frob");
+                           "com.example.Frob",
+                           NULL,
+                           NULL,
+                           NULL);
+  g_dbus_proxy_block_for_properties (frob);
 
   ret = g_dbus_proxy_invoke_method_sync (frob,
                                          "HelloWorld",
@@ -72,12 +150,13 @@ proxy_on_name_appeared (GDBusConnection *connection,
   g_assert_cmpstr (v_string, ==, "You greeted me with 'Hello World!'. Thanks!");
   g_free (v_string);
 
+  /* test marshalling/demarshalling of primitive types */
   ret = g_dbus_proxy_invoke_method_sync (frob,
                                          "TestPrimitiveTypes",
                                          "ybnqiuxtdsog", "ybnqiuxtdsog",
                                          -1, NULL, &error,
                                          /* in values */
-                                         G_TYPE_CHAR, 1,
+                                         G_TYPE_UCHAR, 1,
                                          G_TYPE_BOOLEAN, TRUE,
                                          G_TYPE_INT, 2,                  /* gint16 */
                                          G_TYPE_UINT, 3,                 /* guint16 */
@@ -91,7 +170,7 @@ proxy_on_name_appeared (GDBusConnection *connection,
                                          G_TYPE_STRING, "ass",           /* a signature */
                                          G_TYPE_INVALID,
                                          /* out values */
-                                         G_TYPE_CHAR, &v_byte,
+                                         G_TYPE_UCHAR, &v_byte,
                                          G_TYPE_BOOLEAN, &v_boolean,
                                          G_TYPE_INT, &v_int16,           /* gint16 */
                                          G_TYPE_UINT, &v_uint16,         /* guint16 */
@@ -122,9 +201,706 @@ proxy_on_name_appeared (GDBusConnection *connection,
   g_free (v_object_path);
   g_free (v_signature);
 
-  g_object_unref (frob);
+  /* same test, but check that type coercion works */
+  ret = g_dbus_proxy_invoke_method_sync (frob,
+                                         "TestPrimitiveTypes",
+                                         "ybnqiuxtdsog", "ybnqiuxtdsog",
+                                         -1, NULL, &error,
+                                         /* in values */
+                                         G_TYPE_INT, 1,
+                                         G_TYPE_INT, TRUE,
+                                         G_TYPE_INT, 2,                  /* gint16 */
+                                         G_TYPE_INT, 3,                 /* guint16 */
+                                         G_TYPE_INT, 4,
+                                         G_TYPE_INT, 5,
+                                         G_TYPE_INT, 6,
+                                         G_TYPE_INT, 7,
+                                         G_TYPE_INT, 8,
+                                         G_TYPE_STRING, "a string",
+                                         G_TYPE_STRING, "/foo",          /* an object path */
+                                         G_TYPE_STRING, "ass",           /* a signature */
+                                         G_TYPE_INVALID,
+                                         /* out values */
+                                         G_TYPE_STRING, &v_str0,
+                                         G_TYPE_STRING, &v_str1,
+                                         G_TYPE_STRING, &v_str2,
+                                         G_TYPE_STRING, &v_str3,
+                                         G_TYPE_STRING, &v_str4,
+                                         G_TYPE_STRING, &v_str5,
+                                         G_TYPE_STRING, &v_str6,
+                                         G_TYPE_STRING, &v_str7,
+                                         G_TYPE_STRING, &v_str8,
+                                         G_TYPE_STRING, &v_string,
+                                         G_TYPE_STRING, &v_object_path,  /* an object path */
+                                         G_TYPE_STRING, &v_signature,    /* a signature */
+                                         G_TYPE_INVALID);
+  g_assert_no_error (error);
+  g_assert (ret);
+  g_assert_cmpstr   (v_str0, ==, "2");
+  g_assert_cmpstr   (v_str1, ==, "FALSE");
+  g_assert_cmpstr   (v_str2, ==, "3");
+  g_assert_cmpstr   (v_str3, ==, "4");
+  g_assert_cmpstr   (v_str4, ==, "5");
+  g_assert_cmpstr   (v_str5, ==, "6");
+  g_assert_cmpstr   (v_str6, ==, "7");
+  g_assert_cmpstr   (v_str7, ==, "8");
+  g_assert_cmpstr   (v_str8, ==, "-7.877000");
+  g_assert_cmpstr   (v_string, ==, "a stringa string");
+  g_assert_cmpstr   (v_object_path, ==, "/foo/modified");
+  g_assert_cmpstr   (v_signature, ==, "assass");
+  g_free (v_str0);
+  g_free (v_str1);
+  g_free (v_str2);
+  g_free (v_str3);
+  g_free (v_str4);
+  g_free (v_str5);
+  g_free (v_str6);
+  g_free (v_str7);
+  g_free (v_str8);
+  g_free (v_string);
+  g_free (v_object_path);
+  g_free (v_signature);
+
+  /* test marshalling/demarhalling of arrays of primitive types */
+  ay = g_array_new (FALSE, FALSE, sizeof (guchar)); g_array_set_size (ay, 1);     ((guchar *) ay->data)[0] = 1;
+  ab = g_array_new (FALSE, FALSE, sizeof (gboolean)); g_array_set_size (ab, 1); ((gboolean *) ab->data)[0] = TRUE;
+  an = g_array_new (FALSE, FALSE, sizeof (gint16)); g_array_set_size (an, 1);     ((gint16 *) an->data)[0] = 3;
+  aq = g_array_new (FALSE, FALSE, sizeof (guint16)); g_array_set_size (aq, 1);   ((guint16 *) aq->data)[0] = 4;
+  ai = g_array_new (FALSE, FALSE, sizeof (gint)); g_array_set_size (ai, 1);         ((gint *) ai->data)[0] = 5;
+  au = g_array_new (FALSE, FALSE, sizeof (guint)); g_array_set_size (au, 1);       ((guint *) au->data)[0] = 6;
+  ax = g_array_new (FALSE, FALSE, sizeof (gint64)); g_array_set_size (ax, 1);     ((gint64 *) ax->data)[0] = 7;
+  at = g_array_new (FALSE, FALSE, sizeof (guint64)); g_array_set_size (at, 1);   ((guint64 *) at->data)[0] = 8;
+  ad = g_array_new (FALSE, FALSE, sizeof (gdouble)); g_array_set_size (ad, 1);   ((gdouble *) ad->data)[0] = 8.2;
+  ret = g_dbus_proxy_invoke_method_sync (frob,
+                                         "TestArrayOfPrimitiveTypes",
+                                         "ayabanaqaiauaxatad", "ayabanaqaiauaxatad",
+                                         -1, NULL, &error,
+                                         /* in values */
+                                         G_TYPE_ARRAY, ay,
+                                         G_TYPE_ARRAY, ab,
+                                         G_TYPE_ARRAY, an,
+                                         G_TYPE_ARRAY, aq,
+                                         G_TYPE_ARRAY, ai,
+                                         G_TYPE_ARRAY, au,
+                                         G_TYPE_ARRAY, ax,
+                                         G_TYPE_ARRAY, at,
+                                         G_TYPE_ARRAY, ad,
+                                         G_TYPE_INVALID,
+                                         /* out values */
+                                         G_TYPE_ARRAY, &v_ay,
+                                         G_TYPE_ARRAY, &v_ab,
+                                         G_TYPE_ARRAY, &v_an,
+                                         G_TYPE_ARRAY, &v_aq,
+                                         G_TYPE_ARRAY, &v_ai,
+                                         G_TYPE_ARRAY, &v_au,
+                                         G_TYPE_ARRAY, &v_ax,
+                                         G_TYPE_ARRAY, &v_at,
+                                         G_TYPE_ARRAY, &v_ad,
+                                         G_TYPE_INVALID);
+  g_assert_no_error (error);
+  g_assert (ret);
+#define CHECK_ARRAY(array, type, val) do {g_assert (array->len == 2 && g_array_get_element_size (array) == sizeof (type) && g_array_index (array, type, 0) == val && g_array_index (array, type, 1) == val);} while (FALSE)
+  CHECK_ARRAY (v_ay, guchar,   1);
+  CHECK_ARRAY (v_ab, gboolean, TRUE);
+  CHECK_ARRAY (v_an, guint16,  3);
+  CHECK_ARRAY (v_aq, gint16,   4);
+  CHECK_ARRAY (v_ai, gint,     5);
+  CHECK_ARRAY (v_au, guint,    6);
+  CHECK_ARRAY (v_ax, gint64,   7);
+  CHECK_ARRAY (v_at, guint64,  8);
+  CHECK_ARRAY (v_ad, gdouble,  8.2);
+  g_array_unref (v_ay);
+  g_array_unref (v_ab);
+  g_array_unref (v_an);
+  g_array_unref (v_aq);
+  g_array_unref (v_ai);
+  g_array_unref (v_au);
+  g_array_unref (v_ax);
+  g_array_unref (v_at);
+  g_array_unref (v_ad);
+
+  /* test marshalling/demarshalling of array of string(ish) types */
+  ret = g_dbus_proxy_invoke_method_sync (frob,
+                                         "TestArrayOfStringTypes",
+                                         "asaoag", "asaoag",
+                                         -1, NULL, &error,
+                                         /* in values */
+                                         G_TYPE_STRV, as,
+                                         G_TYPE_STRV, ao,
+                                         G_TYPE_STRV, ag,
+                                         G_TYPE_INVALID,
+                                         /* out values */
+                                         G_TYPE_STRV, &v_as,
+                                         G_TYPE_STRV, &v_ao,
+                                         G_TYPE_STRV, &v_ag,
+                                         G_TYPE_INVALID);
+  g_assert_no_error (error);
+  g_assert (ret);
+  g_assert_cmpint (g_strv_length (v_as), ==, 4);
+  g_assert_cmpint (g_strv_length (v_ao), ==, 4);
+  g_assert_cmpint (g_strv_length (v_ag), ==, 4);
+  g_assert_cmpstr (v_as[0], ==, "bla");
+  g_assert_cmpstr (v_as[2], ==, "bla");
+  g_assert_cmpstr (v_as[1], ==, "baz");
+  g_assert_cmpstr (v_as[3], ==, "baz");
+  g_assert_cmpstr (v_ao[0], ==, "/foo");
+  g_assert_cmpstr (v_ao[2], ==, "/foo");
+  g_assert_cmpstr (v_ao[1], ==, "/bar");
+  g_assert_cmpstr (v_ao[3], ==, "/bar");
+  g_assert_cmpstr (v_ag[0], ==, "ass");
+  g_assert_cmpstr (v_ag[2], ==, "ass");
+  g_assert_cmpstr (v_ag[1], ==, "git");
+  g_assert_cmpstr (v_ag[3], ==, "git");
+  g_strfreev (v_as);
+  g_strfreev (v_ao);
+  g_strfreev (v_ag);
+
+  /* test marshalling/demarshalling of hash tables with primitive types */
+  hyy = g_hash_table_new (g_direct_hash, g_direct_equal);
+  hbb = g_hash_table_new (g_direct_hash, g_direct_equal);
+  hnn = g_hash_table_new (g_direct_hash, g_direct_equal);
+  hqq = g_hash_table_new (g_direct_hash, g_direct_equal);
+  hii = g_hash_table_new (g_direct_hash, g_direct_equal);
+  huu = g_hash_table_new (g_direct_hash, g_direct_equal);
+  hxx = g_hash_table_new (g_int64_hash, g_int64_equal);
+  htt = g_hash_table_new (g_int64_hash, g_int64_equal);
+  hdd = g_hash_table_new (g_double_hash, g_double_equal);
+  hss = g_hash_table_new (g_str_hash, g_str_equal);
+  hoo = g_hash_table_new (g_str_hash, g_str_equal);
+  hgg = g_hash_table_new (g_str_hash, g_str_equal);
+  g_hash_table_insert (hyy, GINT_TO_POINTER (1), GINT_TO_POINTER (1));
+  g_hash_table_insert (hbb, GINT_TO_POINTER (TRUE), GINT_TO_POINTER (FALSE));
+  g_hash_table_insert (hnn, GINT_TO_POINTER (2), GINT_TO_POINTER (2));
+  g_hash_table_insert (hqq, GINT_TO_POINTER (3), GINT_TO_POINTER (3));
+  g_hash_table_insert (hii, GINT_TO_POINTER (4), GINT_TO_POINTER (4));
+  g_hash_table_insert (huu, GINT_TO_POINTER (5), GINT_TO_POINTER (5));
+  g_hash_table_insert (hxx, &v_int64_a, &v_int64_b);
+  g_hash_table_insert (htt, &v_uint64_a, &v_uint64_b);
+  g_hash_table_insert (hdd, &v_double_a, &v_double_b);
+  g_hash_table_insert (hss, "foo", "bar");
+  g_hash_table_insert (hss, "frak", "frack");
+  g_hash_table_insert (hoo, "/foo", "/bar");
+  g_hash_table_insert (hgg, "g", "sad");
+  ret = g_dbus_proxy_invoke_method_sync (frob,
+                                         "TestHashTables",
+                                         "a{yy}a{bb}a{nn}a{qq}a{ii}a{uu}a{xx}a{tt}a{dd}a{ss}a{oo}a{gg}",
+                                         "a{yy}a{bb}a{nn}a{qq}a{ii}a{uu}a{xx}a{tt}a{dd}a{ss}a{oo}a{gg}",
+                                         -1, NULL, &error,
+                                         /* in values */
+                                         G_TYPE_HASH_TABLE, hyy,
+                                         G_TYPE_HASH_TABLE, hbb,
+                                         G_TYPE_HASH_TABLE, hnn,
+                                         G_TYPE_HASH_TABLE, hqq,
+                                         G_TYPE_HASH_TABLE, hii,
+                                         G_TYPE_HASH_TABLE, huu,
+                                         G_TYPE_HASH_TABLE, hxx,
+                                         G_TYPE_HASH_TABLE, htt,
+                                         G_TYPE_HASH_TABLE, hdd,
+                                         G_TYPE_HASH_TABLE, hss,
+                                         G_TYPE_HASH_TABLE, hoo,
+                                         G_TYPE_HASH_TABLE, hgg,
+                                         G_TYPE_INVALID,
+                                         /* out values */
+                                         G_TYPE_HASH_TABLE, &v_hyy,
+                                         G_TYPE_HASH_TABLE, &v_hbb,
+                                         G_TYPE_HASH_TABLE, &v_hnn,
+                                         G_TYPE_HASH_TABLE, &v_hqq,
+                                         G_TYPE_HASH_TABLE, &v_hii,
+                                         G_TYPE_HASH_TABLE, &v_huu,
+                                         G_TYPE_HASH_TABLE, &v_hxx,
+                                         G_TYPE_HASH_TABLE, &v_htt,
+                                         G_TYPE_HASH_TABLE, &v_hdd,
+                                         G_TYPE_HASH_TABLE, &v_hss,
+                                         G_TYPE_HASH_TABLE, &v_hoo,
+                                         G_TYPE_HASH_TABLE, &v_hgg,
+                                         G_TYPE_INVALID);
+  g_assert_no_error (error);
+  g_assert (ret);
+  g_assert_cmpint (GPOINTER_TO_INT (g_hash_table_lookup (v_hyy, GINT_TO_POINTER (1 * 2))), ==, 1 * 3);
+  g_assert_cmpint (GPOINTER_TO_INT (g_hash_table_lookup (v_hbb, GINT_TO_POINTER (TRUE))),  ==, TRUE);
+  g_assert_cmpint (GPOINTER_TO_INT (g_hash_table_lookup (v_hnn, GINT_TO_POINTER (2 * 2))), ==, 2 * 3);
+  g_assert_cmpint (GPOINTER_TO_INT (g_hash_table_lookup (v_hqq, GINT_TO_POINTER (3 * 2))), ==, 3 * 3);
+  g_assert_cmpint (GPOINTER_TO_INT (g_hash_table_lookup (v_hii, GINT_TO_POINTER (4 * 2))), ==, 4 * 3);
+  g_assert_cmpint (GPOINTER_TO_INT (g_hash_table_lookup (v_huu, GINT_TO_POINTER (5 * 2))), ==, 5 * 3);
+  v_int64_a += 2; v_int64_b += 1;
+  g_assert_cmpint (*((gint64 *) g_hash_table_lookup (v_hxx, &v_int64_a)), ==, v_int64_b);
+  v_uint64_a += 2; v_uint64_b += 1;
+  g_assert_cmpint (*((guint64 *) g_hash_table_lookup (v_htt, &v_uint64_a)), ==, v_uint64_b);
+  v_double_a += 2.5; v_double_b += 5.0;
+  g_assert_cmpint (*((gdouble *) g_hash_table_lookup (v_hdd, &v_double_a)), ==, v_double_b);
+  g_assert_cmpstr (g_hash_table_lookup (v_hss, "foomod"), ==, "barbar");
+  g_assert_cmpstr (g_hash_table_lookup (v_hss, "frakmod"), ==, "frackfrack");
+  g_assert_cmpstr (g_hash_table_lookup (v_hoo, "/foo/mod"), ==, "/bar/mod2");
+  g_assert_cmpstr (g_hash_table_lookup (v_hgg, "gassgit"), ==, "sadsad");
+  g_hash_table_unref (v_hyy);
+  g_hash_table_unref (v_hbb);
+  g_hash_table_unref (v_hnn);
+  g_hash_table_unref (v_hqq);
+  g_hash_table_unref (v_hii);
+  g_hash_table_unref (v_huu);
+  g_hash_table_unref (v_hxx);
+  g_hash_table_unref (v_htt);
+  g_hash_table_unref (v_hdd);
+  g_hash_table_unref (v_hss);
+  g_hash_table_unref (v_hoo);
+  g_hash_table_unref (v_hgg);
+
+  /* test marshalling/demarshalling of structs */
+  pair = g_dbus_structure_new ("(ii)",
+                               G_TYPE_INT, 42,
+                               G_TYPE_INT, 43,
+                               G_TYPE_INVALID);
+  complex_struct = g_dbus_structure_new ("(s(ii)aya{ss})",
+                                         G_TYPE_STRING, "The time is right to make new friends",
+                                         G_TYPE_DBUS_STRUCTURE, pair,
+                                         G_TYPE_ARRAY, ay,
+                                         G_TYPE_HASH_TABLE, hss,
+                                         G_TYPE_INVALID);
+  ret = g_dbus_proxy_invoke_method_sync (frob,
+                                         "TestStructureTypes",
+                                         "(ii)(s(ii)aya{ss})", "(ii)(s(ii)aya{ss})",
+                                         -1, NULL, &error,
+                                         /* in values */
+                                         G_TYPE_DBUS_STRUCTURE, pair,
+                                         G_TYPE_DBUS_STRUCTURE, complex_struct,
+                                         G_TYPE_INVALID,
+                                         /* out values */
+                                         G_TYPE_DBUS_STRUCTURE, &v_pair,
+                                         G_TYPE_DBUS_STRUCTURE, &v_complex_struct,
+                                         G_TYPE_INVALID);
+  g_assert_no_error (error);
+  g_assert (ret);
+  g_dbus_structure_get_element (v_pair,
+                                0, &v_int32,
+                                1, &v_uint32,
+                                -1);
+  g_assert_cmpint (v_int32, ==, 42 + 1);
+  g_assert_cmpint (v_uint32, ==, 43 + 1);
+  g_dbus_structure_get_element (v_complex_struct,
+                                0, &v_string,
+                                1, &other_struct,
+                                2, &v_ay,
+                                3, &v_hss,
+                                -1);
+  g_assert_cmpstr (v_string, ==, "The time is right to make new friends ... in bed!"); /* lame chinese fortune joke */
+  g_dbus_structure_get_element (other_struct,
+                                0, &v_int32,
+                                1, &v_uint32,
+                                -1);
+  g_assert_cmpint (v_int32, ==, 42 + 2);
+  g_assert_cmpint (v_uint32, ==, 43 + 2);
+  CHECK_ARRAY (v_ay, guchar,   1);
+  g_assert_cmpstr (g_hash_table_lookup (v_hss, "foo"), ==, "bar ... in bed!");
+  g_assert_cmpstr (g_hash_table_lookup (v_hss, "frak"), ==, "frack ... in bed!");
+  g_assert (ret);
+  g_object_unref (v_pair);
+  g_object_unref (v_complex_struct);
+
+  /* test marshalling/demarshalling of complex arrays */
+  GPtrArray *array_of_array_of_pairs;
+  GPtrArray *v_array_of_array_of_pairs;
+  GPtrArray *array_of_array_of_strings;
+  GPtrArray *v_array_of_array_of_strings;
+  GPtrArray *array_of_hashes;
+  GPtrArray *v_array_of_hashes;
+  GPtrArray *array_of_ay;
+  GPtrArray *v_array_of_ay;
+  GPtrArray *av;
+  GPtrArray *v_av;
+  GPtrArray *aav;
+  GPtrArray *v_aav;
+  array_of_pairs = g_ptr_array_new ();
+  g_ptr_array_add (array_of_pairs, pair);
+  g_ptr_array_add (array_of_pairs, pair);
+  array_of_array_of_pairs = g_ptr_array_new ();
+  g_ptr_array_add (array_of_array_of_pairs, array_of_pairs);
+  g_ptr_array_add (array_of_array_of_pairs, array_of_pairs);
+  g_ptr_array_add (array_of_array_of_pairs, array_of_pairs);
+  array_of_array_of_strings = g_ptr_array_new ();
+  g_ptr_array_add (array_of_array_of_strings, as);
+  array_of_hashes = g_ptr_array_new ();
+  g_ptr_array_add (array_of_hashes, hss);
+  g_ptr_array_add (array_of_hashes, hss);
+  g_ptr_array_add (array_of_hashes, hss);
+  g_ptr_array_add (array_of_hashes, hss);
+  array_of_ay = g_ptr_array_new ();
+  g_ptr_array_add (array_of_ay, ay);
+  g_ptr_array_add (array_of_ay, ay);
+  g_ptr_array_add (array_of_ay, ay);
+  GDBusVariant *variant1;
+  GDBusVariant *variant2;
+  variant1 = g_dbus_variant_new_for_signature ("assgit");
+  variant2 = g_dbus_variant_new_for_structure (pair);
+  av = g_ptr_array_new ();
+  g_ptr_array_add (av, variant1);
+  g_ptr_array_add (av, variant2);
+  g_ptr_array_add (av, variant1);
+  aav = g_ptr_array_new ();
+  g_ptr_array_add (aav, av);
+  g_ptr_array_add (aav, av);
+  g_ptr_array_add (aav, av);
+  g_ptr_array_add (aav, av);
+  g_ptr_array_add (aav, av);
+  ret = g_dbus_proxy_invoke_method_sync (frob,
+                                         "TestComplexArrays",
+                                         "a(ii)aa(ii)aasaa{ss}aayavaav", "a(ii)aa(ii)aasaa{ss}aayavaav",
+                                         -1, NULL, &error,
+                                         /* in values */
+                                         G_TYPE_PTR_ARRAY, array_of_pairs,
+                                         G_TYPE_PTR_ARRAY, array_of_array_of_pairs,
+                                         G_TYPE_PTR_ARRAY, array_of_array_of_strings,
+                                         G_TYPE_PTR_ARRAY, array_of_hashes,
+                                         G_TYPE_PTR_ARRAY, array_of_ay,
+                                         G_TYPE_PTR_ARRAY, av,
+                                         G_TYPE_PTR_ARRAY, aav,
+                                         G_TYPE_INVALID,
+                                         /* out values */
+                                         G_TYPE_PTR_ARRAY, &v_array_of_pairs,
+                                         G_TYPE_PTR_ARRAY, &v_array_of_array_of_pairs,
+                                         G_TYPE_PTR_ARRAY, &v_array_of_array_of_strings,
+                                         G_TYPE_PTR_ARRAY, &v_array_of_hashes,
+                                         G_TYPE_PTR_ARRAY, &v_array_of_ay,
+                                         G_TYPE_PTR_ARRAY, &v_av,
+                                         G_TYPE_PTR_ARRAY, &v_aav,
+                                         G_TYPE_INVALID);
+  g_assert_no_error (error);
+  g_assert (ret);
+  g_assert_cmpint (v_array_of_pairs->len, ==, 4);
+  g_assert_cmpint (v_array_of_array_of_pairs->len, ==, 6);
+  g_assert_cmpint (v_array_of_array_of_strings->len, ==, 2);
+  g_assert_cmpint (v_array_of_hashes->len, ==, 8);
+  g_assert_cmpint (v_array_of_ay->len, ==, 6);
+  g_assert_cmpint (v_av->len, ==, 6);
+  g_assert_cmpint (v_aav->len, ==, 10);
+  g_ptr_array_unref (v_array_of_pairs);
+  g_ptr_array_unref (v_array_of_array_of_pairs);
+  g_ptr_array_unref (v_array_of_array_of_strings);
+  g_ptr_array_unref (v_array_of_hashes);
+  g_ptr_array_unref (v_array_of_ay);
+  g_ptr_array_unref (v_av);
+  g_ptr_array_unref (v_aav);
 
+  /* test hash tables with non-primitive types */
+  GHashTable *h_str_to_pair;
+  GHashTable *v_h_str_to_pair;
+  GHashTable *h_str_to_variant;
+  GHashTable *v_h_str_to_variant;
+  GHashTable *h_str_to_av;
+  GHashTable *v_h_str_to_av;
+  GHashTable *h_str_to_aav;
+  GHashTable *v_h_str_to_aav;
+  GHashTable *h_str_to_array_of_pairs;
+  GHashTable *v_h_str_to_array_of_pairs;
+  GHashTable *hash_of_hashes;
+  GHashTable *v_hash_of_hashes;
+  h_str_to_pair = g_hash_table_new (g_str_hash, g_str_equal);
+  g_hash_table_insert (h_str_to_pair, "first", pair);
+  g_hash_table_insert (h_str_to_pair, "second", pair);
+  h_str_to_variant = g_hash_table_new (g_str_hash, g_str_equal);
+  g_hash_table_insert (h_str_to_variant, "1st", variant1);
+  g_hash_table_insert (h_str_to_variant, "2nd", variant2);
+  h_str_to_av = g_hash_table_new (g_str_hash, g_str_equal);
+  g_hash_table_insert (h_str_to_av, "1", av);
+  g_hash_table_insert (h_str_to_av, "2", av);
+  h_str_to_aav = g_hash_table_new (g_str_hash, g_str_equal);
+  g_hash_table_insert (h_str_to_aav, "1", aav);
+  g_hash_table_insert (h_str_to_aav, "2", aav);
+  h_str_to_array_of_pairs = g_hash_table_new (g_str_hash, g_str_equal);
+  g_hash_table_insert (h_str_to_array_of_pairs, "1", array_of_pairs);
+  g_hash_table_insert (h_str_to_array_of_pairs, "2", array_of_pairs);
+  hash_of_hashes = g_hash_table_new (g_str_hash, g_str_equal);
+  g_hash_table_insert (hash_of_hashes, "1", hss);
+  g_hash_table_insert (hash_of_hashes, "2", hss);
+  ret = g_dbus_proxy_invoke_method_sync (frob,
+                                         "TestComplexHashTables",
+                                         "a{s(ii)}a{sv}a{sav}a{saav}a{sa(ii)}a{sa{ss}}",
+                                         "a{s(ii)}a{sv}a{sav}a{saav}a{sa(ii)}a{sa{ss}}",
+                                         -1, NULL, &error,
+                                         /* in values */
+                                         G_TYPE_HASH_TABLE, h_str_to_pair,
+                                         G_TYPE_HASH_TABLE, h_str_to_variant,
+                                         G_TYPE_HASH_TABLE, h_str_to_av,
+                                         G_TYPE_HASH_TABLE, h_str_to_aav,
+                                         G_TYPE_HASH_TABLE, h_str_to_array_of_pairs,
+                                         G_TYPE_HASH_TABLE, hash_of_hashes,
+                                         G_TYPE_INVALID,
+                                         /* out values */
+                                         G_TYPE_HASH_TABLE, &v_h_str_to_pair,
+                                         G_TYPE_HASH_TABLE, &v_h_str_to_variant,
+                                         G_TYPE_HASH_TABLE, &v_h_str_to_av,
+                                         G_TYPE_HASH_TABLE, &v_h_str_to_aav,
+                                         G_TYPE_HASH_TABLE, &v_h_str_to_array_of_pairs,
+                                         G_TYPE_HASH_TABLE, &v_hash_of_hashes,
+                                         G_TYPE_INVALID);
+  g_assert_no_error (error);
+  g_assert (ret);
+  g_assert ((v_pair = g_hash_table_lookup (v_h_str_to_pair, "first_baz")) != NULL);
+  g_assert (g_dbus_structure_equal (pair, v_pair));
+  g_assert ((v_pair = g_hash_table_lookup (v_h_str_to_pair, "second_baz")) != NULL);
+  g_assert (g_dbus_structure_equal (pair, v_pair));
+  g_hash_table_unref (v_h_str_to_pair);
+  g_assert ((v_variant = g_hash_table_lookup (v_h_str_to_variant, "1st_baz")) != NULL);
+  g_assert_cmpstr (g_dbus_variant_get_signature (v_variant), ==, "assgit");
+  g_assert ((v_variant = g_hash_table_lookup (v_h_str_to_variant, "2nd_baz")) != NULL);
+  g_assert (g_dbus_structure_equal (pair, g_dbus_variant_get_structure (v_variant)));
+  g_hash_table_unref (v_h_str_to_variant);
+  g_assert_cmpint (g_hash_table_size (h_str_to_av), ==, g_hash_table_size (v_h_str_to_av));
+  g_hash_table_unref (v_h_str_to_av);
+  g_assert_cmpint (g_hash_table_size (h_str_to_aav), ==, g_hash_table_size (v_h_str_to_aav));
+  g_hash_table_unref (v_h_str_to_aav);
+  g_assert_cmpint (g_hash_table_size (h_str_to_array_of_pairs), ==, g_hash_table_size (v_h_str_to_array_of_pairs));
+  g_hash_table_unref (v_h_str_to_array_of_pairs);
+  g_assert_cmpint (g_hash_table_size (hash_of_hashes), ==, g_hash_table_size (v_hash_of_hashes));
+  g_hash_table_unref (v_hash_of_hashes);
+
+  g_hash_table_unref (h_str_to_av);
+  g_hash_table_unref (h_str_to_aav);
+  g_hash_table_unref (h_str_to_array_of_pairs);
+  g_hash_table_unref (hash_of_hashes);
+
+  /* test marshalling/demarshalling of variants (this also tests various _equal() funcs) */
+  for (n = 0; ; n++)
+    {
+      variant = NULL;
+      switch (n)
+        {
+        case 0:
+          variant = g_dbus_variant_new_for_string ("frak");
+          break;
+        case 1:
+          variant = g_dbus_variant_new_for_object_path ("/blah");
+          break;
+        case 2:
+          variant = g_dbus_variant_new_for_signature ("assgit");
+          break;
+        case 3:
+          variant = g_dbus_variant_new_for_string_array (as);
+          break;
+        case 4:
+          variant = g_dbus_variant_new_for_object_path_array (ao);
+          break;
+        case 5:
+          variant = g_dbus_variant_new_for_signature_array (ag);
+          break;
+        case 6:
+          variant = g_dbus_variant_new_for_byte (1);
+          break;
+        case 7:
+          variant = g_dbus_variant_new_for_int16 (2);
+          break;
+        case 8:
+          variant = g_dbus_variant_new_for_uint16 (3);
+          break;
+        case 9:
+          variant = g_dbus_variant_new_for_int (4);
+          break;
+        case 10:
+          variant = g_dbus_variant_new_for_uint (5);
+          break;
+        case 11:
+          variant = g_dbus_variant_new_for_int64 (6);
+          break;
+        case 12:
+          variant = g_dbus_variant_new_for_uint64 (7);
+          break;
+        case 13:
+          variant = g_dbus_variant_new_for_boolean (TRUE);
+          break;
+        case 14:
+          variant = g_dbus_variant_new_for_double (8.1);
+          break;
+        case 15:
+          variant = g_dbus_variant_new_for_array (ai, "i");
+          break;
+        case 16:
+          variant = g_dbus_variant_new_for_hash_table (hii, "i", "i");
+          break;
+        case 17:
+          variant = g_dbus_variant_new_for_hash_table (hoo, "o", "o");
+          break;
+        case 18:
+          variant = g_dbus_variant_new_for_structure (complex_struct);
+          break;
+        case 19:
+          variant = g_dbus_variant_new_for_ptr_array (array_of_pairs, "(ii)");
+          break;
+
+        default:
+          break;
+        }
+
+      if (variant == NULL)
+        break;
+
+      /* first make the service returns the identical variant - this also tests
+       * that all the _equal() functions work as intended
+       */
+      ret = g_dbus_proxy_invoke_method_sync (frob,
+                                             "TestVariant",
+                                             "vb", "v",
+                                             -1, NULL, &error,
+                                             /* in values */
+                                             G_TYPE_DBUS_VARIANT, variant,
+                                             G_TYPE_BOOLEAN, FALSE,
+                                             G_TYPE_INVALID,
+                                             /* out values */
+                                             G_TYPE_DBUS_VARIANT, &v_variant,
+                                             G_TYPE_INVALID);
+      g_assert_no_error (error);
+      g_assert (ret);
+      g_assert (g_dbus_variant_equal (variant, v_variant));
+      g_object_unref (v_variant);
+
+      /* this make the service modify the variant */
+      ret = g_dbus_proxy_invoke_method_sync (frob,
+                                             "TestVariant",
+                                             "vb", "v",
+                                             -1, NULL, &error,
+                                             /* in values */
+                                             G_TYPE_DBUS_VARIANT, variant,
+                                             G_TYPE_BOOLEAN, TRUE,
+                                             G_TYPE_INVALID,
+                                             /* out values */
+                                             G_TYPE_DBUS_VARIANT, &v_variant,
+                                             G_TYPE_INVALID);
+      g_assert (!g_dbus_variant_equal (variant, v_variant));
+      g_assert_no_error (error);
+      g_assert (ret);
+      switch (n)
+        {
+        case 0:
+          g_assert_cmpstr (g_dbus_variant_get_string (v_variant), ==, "frakfrak");
+          break;
+        case 1:
+          g_assert_cmpstr (g_dbus_variant_get_object_path (v_variant), ==, "/blah/blah");
+          break;
+        case 2:
+          g_assert_cmpstr (g_dbus_variant_get_signature (v_variant), ==, "assgitassgit");
+          break;
+        case 3:
+          v_as = g_dbus_variant_get_string_array (v_variant);
+          g_assert_cmpint (g_strv_length (v_as), ==, 4);
+          g_assert_cmpstr (v_as[0], ==, "bla");
+          g_assert_cmpstr (v_as[2], ==, "bla");
+          g_assert_cmpstr (v_as[1], ==, "baz");
+          g_assert_cmpstr (v_as[3], ==, "baz");
+          break;
+        case 4:
+          v_ao = g_dbus_variant_get_object_path_array (v_variant);
+          g_assert_cmpint (g_strv_length (v_ao), ==, 4);
+          g_assert_cmpstr (v_ao[0], ==, "/foo");
+          g_assert_cmpstr (v_ao[2], ==, "/foo");
+          g_assert_cmpstr (v_ao[1], ==, "/bar");
+          g_assert_cmpstr (v_ao[3], ==, "/bar");
+          break;
+        case 5:
+          v_ag = g_dbus_variant_get_signature_array (v_variant);
+          g_assert_cmpint (g_strv_length (v_ag), ==, 4);
+          g_assert_cmpstr (v_ag[0], ==, "ass");
+          g_assert_cmpstr (v_ag[2], ==, "ass");
+          g_assert_cmpstr (v_ag[1], ==, "git");
+          g_assert_cmpstr (v_ag[3], ==, "git");
+          break;
+        case 6:
+          g_assert_cmpint (g_dbus_variant_get_byte (v_variant), ==, g_dbus_variant_get_byte (variant) * 2);
+          break;
+        case 7:
+          g_assert_cmpint (g_dbus_variant_get_int16 (v_variant), ==, g_dbus_variant_get_int16 (variant) * 2);
+          break;
+        case 8:
+          g_assert_cmpint (g_dbus_variant_get_uint16 (v_variant), ==, g_dbus_variant_get_uint16 (variant) * 2);
+          break;
+        case 9:
+          g_assert_cmpint (g_dbus_variant_get_int (v_variant), ==, g_dbus_variant_get_int (variant) * 2);
+          break;
+        case 10:
+          g_assert_cmpint (g_dbus_variant_get_uint (v_variant), ==, g_dbus_variant_get_uint (variant) * 2);
+          break;
+        case 11:
+          g_assert_cmpint (g_dbus_variant_get_int64 (v_variant), ==, g_dbus_variant_get_int64 (variant) * 2);
+          break;
+        case 12:
+          g_assert_cmpint (g_dbus_variant_get_uint64 (v_variant), ==, g_dbus_variant_get_uint64 (variant) * 2);
+          break;
+        case 13:
+          g_assert (!g_dbus_variant_get_boolean (v_variant));
+          break;
+        case 14:
+          g_assert_cmpfloat (g_dbus_variant_get_double (v_variant), ==, g_dbus_variant_get_double (variant) * 2);
+          break;
+        case 15:
+          v_ai = g_dbus_variant_get_array (v_variant);
+          CHECK_ARRAY (v_ai, gint,     5);
+          break;
+        case 16:
+          v_hii = g_dbus_variant_get_hash_table (v_variant);
+          g_assert_cmpint (GPOINTER_TO_INT (g_hash_table_lookup (v_hii, GINT_TO_POINTER (4))), ==, 4 * 2);
+          break;
+        case 17:
+          v_hoo = g_dbus_variant_get_hash_table (v_variant);
+          g_assert_cmpstr (g_hash_table_lookup (v_hoo, "/foo"), ==, "/bar/bar");
+          break;
+        case 18:
+          other_struct = g_dbus_variant_get_structure (v_variant);
+          g_dbus_structure_get_element (other_struct,
+                                        0, &v_string,
+                                        1, &v_int16,
+                                        -1);
+          g_assert_cmpstr (v_string, ==, "other struct");
+          g_assert_cmpint (v_int16, ==, 100);
+          break;
+        case 19:
+          g_assert_cmpint (g_dbus_variant_get_ptr_array (v_variant)->len, ==, array_of_pairs->len * 2);
+          break;
+        default:
+          g_assert_not_reached ();
+          break;
+        }
+      g_object_unref (v_variant);
+
+      g_object_unref (variant);
+    }
+
+  /* free all objects constructed for testing */
+  g_array_unref (ay);
+  g_array_unref (ab);
+  g_array_unref (an);
+  g_array_unref (aq);
+  g_array_unref (ai);
+  g_array_unref (au);
+  g_array_unref (ax);
+  g_array_unref (at);
+  g_array_unref (ad);
+  g_hash_table_unref (hyy);
+  g_hash_table_unref (hbb);
+  g_hash_table_unref (hnn);
+  g_hash_table_unref (hqq);
+  g_hash_table_unref (hii);
+  g_hash_table_unref (huu);
+  g_hash_table_unref (hxx);
+  g_hash_table_unref (htt);
+  g_hash_table_unref (hdd);
+  g_hash_table_unref (hss);
+  g_hash_table_unref (hoo);
+  g_hash_table_unref (hgg);
+  g_object_unref (pair);
+  g_object_unref (complex_struct);
+  g_ptr_array_unref (array_of_pairs);
+  g_ptr_array_unref (array_of_array_of_pairs);
+  g_hash_table_unref (h_str_to_pair);
+  g_hash_table_unref (h_str_to_variant);
+  g_object_unref (variant1);
+  g_object_unref (variant2);
+  g_ptr_array_unref (array_of_array_of_strings);
+  g_ptr_array_unref (array_of_hashes);
+  g_ptr_array_unref (array_of_ay);
+  g_ptr_array_unref (av);
+  g_ptr_array_unref (aav);
+
+  g_object_unref (frob);
   g_main_loop_quit (loop);
+
+#undef CHECK_ARRAY
 }
 
 static void
diff --git a/gdbus/tests/sessionbus.c b/gdbus/tests/sessionbus.c
index b4a466e..57ffc20 100644
--- a/gdbus/tests/sessionbus.c
+++ b/gdbus/tests/sessionbus.c
@@ -245,6 +245,13 @@ session_bus_up_with_address (const gchar *given_address)
   g_snprintf (buf, sizeof buf, "add %d\n", (guint) pid);
   write (pipe_fds[1], buf, strlen (buf));
 
+  /* start dbus-monitor */
+  if (g_getenv ("GDBUS_MONITOR") != NULL)
+    {
+      g_spawn_command_line_async ("dbus-monitor --session", NULL);
+      usleep (500 * 1000);
+    }
+
   g_hash_table_insert (session_bus_address_to_pid, address, GUINT_TO_POINTER (pid));
 
  out:
diff --git a/gdbus/tests/testserver.py b/gdbus/tests/testserver.py
index 9c30999..5ce3dfd 100755
--- a/gdbus/tests/testserver.py
+++ b/gdbus/tests/testserver.py
@@ -12,7 +12,8 @@ class TestException(dbus.DBusException):
 
 class TestService(dbus.service.Object):
 
-    # $ dbus-send --session --print-reply --dest=com.example.TestService /com/example/TestService com.example.TestService.HelloWorld string:Hi!
+    # ----------------------------------------------------------------------------------------------------
+
     @dbus.service.method("com.example.Frob",
                          in_signature='s', out_signature='s')
     def HelloWorld(self, hello_message):
@@ -21,60 +22,42 @@ class TestService(dbus.service.Object):
         else:
             return "You greeted me with '%s'. Thanks!"%(str(hello_message))
 
-    # $ dbus-send --session --print-reply --dest=com.example.TestService /com/example/TestService com.example.TestService.TestPrimitiveTypes byte:1 boolean:false int16:160 uint16:161 int32:320 uint32:321 int64:640 uint64:641 double:4.5 string:foo objpath:/foo
+    # ----------------------------------------------------------------------------------------------------
+
     @dbus.service.method("com.example.Frob",
                          in_signature='ybnqiuxtdsog', out_signature='ybnqiuxtdsog')
     def TestPrimitiveTypes(self, val_byte, val_boolean, val_int16, val_uint16, val_int32, val_uint32, val_int64, val_uint64, val_double, val_string, val_objpath, val_signature):
         return val_byte + 1, not val_boolean, val_int16 + 1, val_uint16 + 1, val_int32 + 1, val_uint32 + 1, val_int64 + 1, val_uint64 + 1, -val_double + 0.123, val_string * 2, val_objpath + "/modified", val_signature * 2
 
-    # $ dbus-send --session --print-reply --dest=com.example.TestService /com/example/TestService com.example.TestService.TestArrayOfPrimitiveTypes array:byte:1,2 array:boolean:false,true array:int16:160,170 array:uint16:161,171 array:int32:320,330 array:uint32:321,331 array:int64:640,650 array:uint64:641,651 array:double:4.5,5.5 array:string:foo,foo2 array:objpath:/foo,/foo2
-    @dbus.service.method("com.example.Frob",
-                         in_signature='ayabanaqaiauaxatadasao', out_signature='ayabanaqaiauaxatadasao')
-    def TestArrayOfPrimitiveTypes(self, val_byte, val_boolean, val_int16, val_uint16, val_int32, val_uint32, val_int64, val_uint64, val_double, val_string, val_objpath):
-        return val_byte*2, val_boolean*2, val_int16*2, val_uint16*2, val_int32*2, val_uint32*2, val_int64*2, val_uint64*2, val_double*2, val_string*2, val_objpath*2
+    # ----------------------------------------------------------------------------------------------------
 
-    # no support in dbus-send
     @dbus.service.method("com.example.Frob",
-                         in_signature='(ii)(s(ii))', out_signature='(ii)(s(ii))')
-    def TestStructureTypes(self, s1, s2):
-        (x, y) = s1;
-        (desc, (x1, y1)) = s2;
-        return (x + 1, y + 1), ("replaced desc", (x1 + 2, y1 + 2))
+                         in_signature='ayabanaqaiauaxatad', out_signature='ayabanaqaiauaxatad')
+    def TestArrayOfPrimitiveTypes(self, val_byte, val_boolean, val_int16, val_uint16, val_int32, val_uint32, val_int64, val_uint64, val_double):
+        return val_byte*2, val_boolean*2, val_int16*2, val_uint16*2, val_int32*2, val_uint32*2, val_int64*2, val_uint64*2, val_double*2
 
-    # no support in dbus-send
-    @dbus.service.method("com.example.Frob",
-                         in_signature='a(s(ii)s)', out_signature='a(s(ii)s)')
-    def TestArrayOfStructureTypes(self, a):
-        r = []
-        n = 0
-        for i in a:
-            (d, (x, y), e) = i;
-            r.append(("%s_%d"%(d,n), (x + 1, y + 1), "%s_%d"%(e,n)))
-            n+=1
-        return r
-
-    # no support in dbus-send
-    @dbus.service.method("com.example.Frob",
-                         in_signature  = 'a{ss}a{oo}a{ii}a{yy}a{nn}a{qq}a{uu}a{bb}a{xx}a{tt}a{dd}',
-                         out_signature = 'a{ss}a{oo}a{ii}a{yy}a{nn}a{qq}a{uu}a{bb}a{xx}a{tt}a{dd}')
-    def TestHashTables(self, hss, hoo, hii, hyy, hnn, hqq, huu, hbb, hxx, htt, hdd):
+    # ----------------------------------------------------------------------------------------------------
 
-        ret_hss = {}
-        for i in hss:
-            ret_hss[i + "mod"] = hss[i]*2
+    @dbus.service.method("com.example.Frob",
+                         in_signature='asaoag', out_signature='asaoag')
+    def TestArrayOfStringTypes(self, val_string, val_objpath, val_signature):
+        return val_string * 2, val_objpath * 2, val_signature * 2
 
-        ret_hoo = {}
-        for i in hoo:
-            ret_hoo[i + "/mod"] = hoo[i] + "/mod2"
+    # ----------------------------------------------------------------------------------------------------
 
-        ret_hii = {}
-        for i in hii:
-            ret_hii[i*2] = hii[i]*3
+    @dbus.service.method("com.example.Frob",
+                         in_signature  = 'a{yy}a{bb}a{nn}a{qq}a{ii}a{uu}a{xx}a{tt}a{dd}a{ss}a{oo}a{gg}',
+                         out_signature = 'a{yy}a{bb}a{nn}a{qq}a{ii}a{uu}a{xx}a{tt}a{dd}a{ss}a{oo}a{gg}')
+    def TestHashTables(self, hyy, hbb, hnn, hqq, hii, huu, hxx, htt, hdd, hss, hoo, hgg):
 
         ret_hyy = {}
         for i in hyy:
             ret_hyy[i*2] = (hyy[i]*3) & 255
 
+        ret_hbb = {}
+        for i in hbb:
+            ret_hbb[i] = True
+
         ret_hnn = {}
         for i in hnn:
             ret_hnn[i*2] = hnn[i]*3
@@ -83,14 +66,14 @@ class TestService(dbus.service.Object):
         for i in hqq:
             ret_hqq[i*2] = hqq[i]*3
 
+        ret_hii = {}
+        for i in hii:
+            ret_hii[i*2] = hii[i]*3
+
         ret_huu = {}
         for i in huu:
             ret_huu[i*2] = huu[i]*3
 
-        ret_hbb = {}
-        for i in hbb:
-            ret_hbb[i] = True
-
         ret_hxx = {}
         for i in hxx:
             ret_hxx[i + 2] = hxx[i] + 1
@@ -103,218 +86,77 @@ class TestService(dbus.service.Object):
         for i in hdd:
             ret_hdd[i + 2.5] = hdd[i] + 5.0
 
-        return ret_hss, ret_hoo, ret_hii, ret_hyy, ret_hnn, ret_hqq, ret_huu, ret_hbb, ret_hxx, ret_htt, ret_hdd
+        ret_hss = {}
+        for i in hss:
+            ret_hss[i + "mod"] = hss[i]*2
+
+        ret_hoo = {}
+        for i in hoo:
+            ret_hoo[i + "/mod"] = hoo[i] + "/mod2"
 
-    # no support in dbus-send
-    @dbus.service.method("com.example.Frob",
-                         in_signature  = 'a{s(ii)}',
-                         out_signature = 'a{s(s(ii))}')
-    def TestHashTableOfStructures(self, hash_of_points):
-        ret = {}
-        for s in hash_of_points:
-            (x, y) = hash_of_points[s]
-            ret[s + "_new"] = (s, (x + 100, y + 200))
-        return ret
-
-    # no support in dbus-send
-    @dbus.service.method("com.example.Frob",
-                         in_signature  = 'a{sas}a{sao}a{sai}a{say}a{san}a{saq}a{sau}a{sab}a{sax}a{sat}a{sad}a{sa(ii)}',
-                         out_signature = 'a{sas}a{sao}a{sai}a{say}a{san}a{saq}a{sau}a{sab}a{sax}a{sat}a{sad}a{sa(ii)}')
-    def TestHashTablesOfArrays(self, hsas, hsao, hsai, hsay, hsan, hsaq, hsau, hsab, hsax, hsat, hsad, hash_of_point_arrays):
-
-        ret_hsas = {}
-        for i in hsas:
-            ret_hsas[i] = hsas[i]*2
-
-        ret_hsao = {}
-        for i in hsao:
-            ret_hsao[i] = hsao[i]*2
-
-        ret_hsai = {"k" : [1, 2]}
-        ret_hsay = {"k" : [3, 4]}
-        ret_hsan = {"k" : [5, 6]}
-        ret_hsaq = {"k" : [7, 8]}
-        ret_hsau = {"k" : [9, 10]}
-        ret_hsab = {"k" : [True, False, True]}
-        ret_hsax = {"k" : [11, 12]}
-        ret_hsat = {"k" : [13, 14]}
-        ret_hsad = {"k" : [15.5, 16.5]}
-
-        ret_hash_of_point_arrays = {}
-        for s in hash_of_point_arrays:
-            point_array = hash_of_point_arrays[s]
-            new_point_array = []
-            for point in point_array:
-                (x, y) = point
-                new_point_array.append ((x + 100, y + 200))
-            ret_hash_of_point_arrays[s + "_new"] = new_point_array
-
-        return ret_hsas, ret_hsao, ret_hsai, ret_hsay, ret_hsan, ret_hsaq, ret_hsau, ret_hsab, ret_hsax, ret_hsat, ret_hsad, ret_hash_of_point_arrays
-
-    # no support in dbus-send
-    @dbus.service.method("com.example.Frob",
-                         in_signature  = 'a{sa{s(ii)}}',
-                         out_signature = 'a{sa{s(ii)}}')
-    def TestHashTableOfHashTablesOfStructures(self, hash_of_hash_of_points):
-
-        ret = {}
-        for h in hash_of_hash_of_points:
-            hash_of_points = hash_of_hash_of_points[h]
-            newh = {}
-            for s in hash_of_points:
-                (x, y) = hash_of_points[s]
-                newh[s + "_new_new"] = (x + 100, y + 200)
-            ret[h + "_new"] = newh
-
-        return ret
-
-    # no support in dbus-send
-    @dbus.service.method("com.example.Frob",
-                         in_signature  = 'aa{s(ii)}',
-                         out_signature = 'aa{s(ii)}')
-    def TestArrayOfHashTablesOfStructures(self, list_of_hash_of_points):
+        ret_hgg = {}
+        for i in hgg:
+            ret_hgg[i + "assgit"] = hgg[i]*2
 
-        ret = []
-        for hash_of_points in list_of_hash_of_points:
-            newh = {}
-            for s in hash_of_points:
-                (x, y) = hash_of_points[s]
-                newh[s + "_new_new"] = (x + 100, y + 200)
-            ret.append(newh)
+        return ret_hyy, ret_hbb, ret_hnn, ret_hqq, ret_hii, ret_huu, ret_hxx, ret_htt, ret_hdd, ret_hss, ret_hoo, ret_hgg
 
-        return ret
+    # ----------------------------------------------------------------------------------------------------
 
-    # no support in dbus-send
-    @dbus.service.method("com.example.Frob",
-                         in_signature  = 'aasaa(ii)aaoaaiaayaanaaqaauaabaaxaataadaaas',
-                         out_signature = 'aasaa(ii)aaoaaiaayaanaaqaauaabaaxaataadaaas')
-    def TestArrayOfArrays(self, aas, aastruct, aao, aai, aay, aan, aaq, aau, aab, aax, aat, aad, aaas):
-
-        ret_aas = aas*2;
-        ret_aastruct = aastruct*2;
-        ret_aao = aao*2;
-        ret_aai = aai*2;
-        ret_aay = aay*2;
-        ret_aan = aan*2;
-        ret_aaq = aaq*2;
-        ret_aau = aau*2;
-        ret_aab = aab*2;
-        ret_aax = aax*2;
-        ret_aat = aat*2;
-        ret_aad = aad*2;
-        ret_aaas = aaas*2
-
-        return ret_aas, ret_aastruct, ret_aao, ret_aai, ret_aay, ret_aan, ret_aaq, ret_aau, ret_aab, ret_aax, ret_aat, ret_aad, ret_aaas
-
-    # dbus-send --print-reply --session --dest=com.example.TestService /com/example/TestService com.example.TestService.TestVariantReturn string:o
     @dbus.service.method("com.example.Frob",
-                         in_signature  = 's',
-                         out_signature = 'v')
-    def TestVariantReturn(self, desired_variant):
-
-        if desired_variant == "y":
-            ret = dbus.Byte(1)
-        elif desired_variant == "b":
-            ret = True
-        elif desired_variant == "n":
-            ret = dbus.Int16(2)
-        elif desired_variant == "q":
-            ret = dbus.UInt16(3)
-        elif desired_variant == "i":
-            ret = dbus.Int32(4)
-        elif desired_variant == "u":
-            ret = dbus.UInt32(5)
-        elif desired_variant == "x":
-            ret = dbus.Int64(6)
-        elif desired_variant == "t":
-            ret = dbus.UInt64(7)
-        elif desired_variant == "d":
-            ret = 7.5
-        elif desired_variant == "s":
-            ret = "a string"
-        elif desired_variant == "o":
-            ret = dbus.ObjectPath("/some/object/path")
-        elif desired_variant == "ai":
-            ret = []
-            ret.append(dbus.Int32(4))
-            ret.append(dbus.Int32(5))
-        elif desired_variant == "as":
-            ret = []
-            ret.append("a string")
-            ret.append("another string")
-        elif desired_variant == "point":
-            ret = (1, 2)
-        elif desired_variant == "dpoint":
-            ret = ("the desc", (3, 4))
-        elif desired_variant == "hash_of_points":
-            ret = {}
-            ret["key0"] = (1, 2)
-            ret["key1"] = (3, 4)
-            ret["key2"] = (5, 6)
-            ret["key3"] = (7, 8)
-        elif desired_variant == "unregistered_struct":
-            ret = ("string",
-                   dbus.Byte(2),
-                   dbus.Int16(3),
-                   4,
-                   [0, 1, 2, 3],
-                   {"rip" : 2, "rap" : 4, "rup" : 6},
-                   [(1, 2), (1, 2), (1, 2)],
-                   [[1, 2, 3], [4, 5, 6], [7, 8, 9]],
-                   [True, False, False],
-                   [[True, True], [False, True]],
-                   [dbus.Byte(0xca), dbus.Byte(0xfe), dbus.Byte(0xba), dbus.Byte(0xbe), dbus.Byte(0x00), dbus.Byte(0xde), dbus.Byte(0xad), dbus.Byte(0xbe), dbus.Byte(0xef)],
-                   {"huey" : (2, 1), "dewey" : (4, 2), "louie" : (6, 3)},
-                   {"innie" : [10, 11], "minnie" : [12, 13], "minie" : [14, 15], "moe" : [16, 17]},
-                   {dbus.Byte(0xa0) : [10, 11], dbus.Byte(0xa1) : [12, 13], dbus.Byte(0xa2) : [14, 15]})
-        else:
-            raise TestException("unknown desired variant '%s'"%desired_variant)
+                         in_signature='(ii)(s(ii)aya{ss})', out_signature='(ii)(s(ii)aya{ss})')
+    def TestStructureTypes(self, s1, s2):
+        (x, y) = s1;
+        (desc, (x1, y1), ay, hss) = s2;
+        ret_hss = {}
+        for i in hss:
+            ret_hss[i] = hss[i] + " ... in bed!"
+        return (x + 1, y + 1), (desc + " ... in bed!", (x1 + 2, y1 + 2), ay * 2, ret_hss)
 
-        return ret
+    # ----------------------------------------------------------------------------------------------------
 
-    # $ dbus-send --print-reply --session --dest=com.example.TestService /com/example/TestService com.example.TestService.TestVariantReturn2 string:one
     @dbus.service.method("com.example.Frob",
-                         in_signature  = 's',
-                         out_signature = 'ava{sv}(siivx)')
-    def TestVariantReturn2(self, desired_variant):
-
-        ret_av = []
-        ret_hsv = {}
-
-        if desired_variant == "one":
-            ret_av.append (dbus.Byte(1))
-            ret_av.append ("a string")
-            ret_av.append ((1, 2))
-            ret_av.append ([dbus.Int16(100), dbus.Int16(100), dbus.Int16(100)])
-            ret_av.append ({"rip" : 2, "rap" : 4, "rup" : 6})
-            ret_struct = ("ok, steady", 10, 11, "one", 2)
-        elif desired_variant == "two":
-            ret_hsv["1st"] = dbus.Byte(1)
-            ret_hsv["2nd"] = "a string"
-            ret_hsv["3rd"] = (1, 2)
-            ret_hsv["4th"] = [dbus.Int16(100), dbus.Int16(100), dbus.Int16(100)]
-            ret_hsv["5th"] = {"rip" : 2, "rap" : 4, "rup" : 6}
-            ret_struct = ("anger. fear. acceptance. CA$H!!", 10, 11, {"foo": 42, "bar": 43}, 2)
+                         in_signature='vb', out_signature='v')
+    def TestVariant(self, v, modify):
+
+        if modify:
+            if type(v)==dbus.Boolean:
+                ret = False
+            elif type(v)==dbus.Dictionary:
+                ret = {}
+                for i in v:
+                    ret[i] = v[i] * 2
+            elif type(v)==dbus.Struct:
+                ret = ["other struct", dbus.Int16(100)]
+            else:
+                ret = v * 2
         else:
-            raise TestException("unknown desired variant '%s'"%desired_variant)
+            ret = v
+        return (type(v))(ret)
 
-        return ret_av, ret_hsv, ret_struct
+    # ----------------------------------------------------------------------------------------------------
 
-    # $ dbus-send --print-reply --session --dest=com.example.TestService /com/example/TestService com.example.TestService.TestPassVariant variant:string:dbus-rocks
     @dbus.service.method("com.example.Frob",
-                         in_signature  = 'v',
-                         out_signature = 's')
-    def TestPassVariant(self, variant):
+                         in_signature='a(ii)aa(ii)aasaa{ss}aayavaav', out_signature='a(ii)aa(ii)aasaa{ss}aayavaav')
+    def TestComplexArrays(self, aii, aaii, aas, ahashes, aay, av, aav):
+        return aii * 2, aaii * 2, aas * 2, ahashes * 2, aay * 2, av *2, aav * 2
 
-        return dbus.String(variant) + "-and-a-bag-of-chips"
+    # ----------------------------------------------------------------------------------------------------
 
-    # $ dbus-send --print-reply --session --dest=com.example.TestService /com/example/TestService com.example.TestService.TestPassVariant variant:string:dbus-rocks
     @dbus.service.method("com.example.Frob",
-                         in_signature  = 'ava{sv}a{sav}(siivx)',
-                         out_signature = 's')
-    def TestPassVariant2(self, array_of_variants, dict_of_string_to_variants, dict_of_string_to_array_of_variants, struct_with_variant):
+                          in_signature='a{s(ii)}a{sv}a{sav}a{saav}a{sa(ii)}a{sa{ss}}',
+                         out_signature='a{s(ii)}a{sv}a{sav}a{saav}a{sa(ii)}a{sa{ss}}')
+    def TestComplexHashTables(self, h_str_to_pair, h_str_to_variant, h_str_to_av, h_str_to_aav,
+                              h_str_to_array_of_pairs, hash_of_hashes):
 
-        return dbus.String(array_of_variants) + dbus.String(dict_of_string_to_variants) + dbus.String(dict_of_string_to_array_of_variants) + dbus.String(struct_with_variant)
+        ret_h_str_to_pair = {}
+        for i in h_str_to_pair:
+            ret_h_str_to_pair[i + "_baz"] = h_str_to_pair[i]
+
+        ret_h_str_to_variant = {}
+        for i in h_str_to_variant:
+            ret_h_str_to_variant[i + "_baz"] = h_str_to_variant[i]
+
+        return ret_h_str_to_pair, ret_h_str_to_variant, h_str_to_av, h_str_to_aav, h_str_to_array_of_pairs, hash_of_hashes
 
     @dbus.service.method("org.freedesktop.DBus.Properties",
                          in_signature  = 'ss',
@@ -323,8 +165,6 @@ class TestService(dbus.service.Object):
 
         if interface_name == "com.example.Frob":
             return self.frob_props[property_name]
-        elif interface_name == "com.example.Tweak":
-            return self.tweak_props[property_name]
         else:
             raise TestException("No such interface " + interface_name)
 
@@ -334,48 +174,9 @@ class TestService(dbus.service.Object):
     def GetAll(self, interface_name):
         if interface_name == "com.example.Frob":
             return self.frob_props
-        elif interface_name == "com.example.Tweak":
-            return self.tweak_props
         else:
             raise TestException("No such interface " + interface_name)
 
-    @dbus.service.method("com.example.Tweak",
-                         in_signature='s', out_signature='s')
-    def ICanHazGreetingz(self, hello_message):
-        return "Word. You haz greetz '%s'. KTHXBYE!"%(str(hello_message))
-
-    @dbus.service.method("com.example.Tweak",
-                         out_signature='s')
-    def GetServerUniqueName(self):
-        return session_bus.get_unique_name()
-
-
-
-
-
-    @dbus.service.signal("com.example.Tweak")
-    def NewzNotifz(self, newz):
-        pass
-
-    @dbus.service.method("com.example.Tweak",
-                         in_signature='s')
-    def BroadcastzNewz(self, newz_to_pass):
-        self.NewzNotifz("Word. Broadcastz '%s'. KTHXBYE!"%(str(newz_to_pass)))
-
-
-    @dbus.service.method("com.example.Twiddle",
-                         in_signature='s')
-    def BroadcastzNewzOnTwiddle(self, newz_to_pass):
-        string = "Sez '%s'. KTHXBYE!"%(str(newz_to_pass))
-        # manually construct the message since dbus-python doesn't appear
-        # to support identical signal names on different interfaces...
-        #
-        message = dbus.lowlevel.SignalMessage("/com/example/TestObject", "com.example.Twiddle", "NewzNotifz")
-        message.append(string)
-        session_bus.send_message(message)
-
-
-
 if __name__ == '__main__':
     dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
 
@@ -411,10 +212,5 @@ if __name__ == '__main__':
     obj.frob_props["ao"] = [dbus.ObjectPath("/some/path"), dbus.ObjectPath("/another/path")]
     obj.frob_props["foo"] = "a frobbed string"
 
-    obj.tweak_props = {}
-    obj.tweak_props["foo"] = "a tweaked string"
-    obj.tweak_props["bar"] = [1, 2]
-    obj.tweak_props["baz"] = (3, 4)
-
     mainloop = gobject.MainLoop()
     mainloop.run()



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]