2005-06-12 Colin Walters <walters@verbum.org>
authorColin Walters <walters@verbum.org>
Mon, 13 Jun 2005 03:01:30 +0000 (03:01 +0000)
committerColin Walters <walters@verbum.org>
Mon, 13 Jun 2005 03:01:30 +0000 (03:01 +0000)
Async signals and various bugfixes and testing by
Ross Burton <ross@burtonini.com>.

* glib/dbus-gvalue.h: (struct DBusBasicGValue): Delete.
(dbus_gvalue_genmarshal_name_from_type)
(dbus_gvalue_ctype_from_type): Moved to dbus-binding-tool-glib.c.
(dbus_gtype_to_dbus_type): Renamed to dbus_gtype_from_signature.
(dbus_g_value_types_init, dbus_gtype_from_signature)
(dbus_gtype_from_signature_iter, dbus_gtype_to_signature)
(dbus_gtypes_from_arg_signature): New function prototypes.
(dbus_gvalue_demarshal): Take context and error arguments.
(dbus_gvalue_demarshal_variant): New function.
(dbus_gvalue_demarshal_message): New function.
(dbus_gvalue_store): Delete.

* glib/dbus-gvalue.c:

File has been almost entirely rewritten; now we special-case
more types such as DBUS_TYPE_SIGNATURE, handle arrays and
hash tables correctly, etc.  Full support for recursive values
is not yet complete.

* glib/dbus-gproxy.c (dbus_g_proxy_class_init): Change last
argument of signal to G_TYPE_POINTER since we now pass a
structure.
(lookup_g_marshaller): Delete in favor of
_dbus_gobject_lookup_marshaller.
(marshal_dbus_message_to_g_marshaller): Use
_dbus_gobject_lookup_marshaller and dbus_gvalue_demarshal_message
to handle remote signal callbacks.
(dbus_g_proxy_new_from_proxy): New function; creates a new
DBusGProxy by copying an existing one.
(dbus_g_proxy_get_interface, dbus_g_proxy_set_interface)
(dbus_g_proxy_get_path): New functions.
(dbus_g_proxy_marshal_args_to_message): New function;
factored out of existing code.
(DBUS_G_VALUE_ARRAY_COLLECT_ALL): Collect all arguments
from a varargs array.
(dbus_g_proxy_begin_call_internal): New function.
(dbus_g_proxy_end_call_internal): New function.
(dbus_g_proxy_begin_call): Take GTypes instead of DBus types
as arguments; simply invoke dbus_g_proxy_begin_call_internal
after collecting args into value array.
(dbus_g_proxy_end_call): Take GTypes instead of DBus types;
invoke dbus_g_proxy_end_call_internal.
(dbus_g_proxy_invoke): Simply invoke begin_call_interanl and
end_call_internal.
(dbus_g_proxy_call_no_reply): Take GTypes instead of DBus
types.
(array_free_all): New function.
(dbus_g_proxy_add_signal): Take GTypes.

* glib/dbus-gobject.h:
(_dbus_glib_marshal_dbus_message_to_gvalue_array): Delete.
(_dbus_gobject_get_path, _dbus_gobject_lookup_marshaller):
Prototype.

* glib/dbus-gobject.c: Add a global marshal_table hash which
stores mappings from type signatures to marshallers.  Change lots
of invocations of dbus_gtype_to_dbus_type to
dbus_gtype_to_signature.
(_dbus_glib_marshal_dbus_message_to_gvalue_array): Delete.
(introspect_signals): Fix test for query.return_type.
(set_object_property): Update invocation of dbus_gvalue_demarshal.
(invoke_object_method): Many changes.  Handle asynchronous
invocations.  Convert arguments with
dbus_gvalue_demarshal_message.  Handle errors.  Use
DBusSignatureIter instead of strlen on args. Handle all arguments
generically.  Special-case variants.
(dbus_g_method_return, dbus_g_method_return_error): New function.
(DBusGSignalClosure): New structure, closes over signal
information.
(dbus_g_signal_closure_new): New function.
(dbus_g_signal_closure_finalize): New function.
(signal_emitter_marshaller): New function; is special marshaller
which emits signals on bus.
(export_signals): New function; introspects object signals and
connects to them.
(dbus_g_object_type_install_info): Take GType instead of
GObjectClass.
(dbus_g_connection_register_g_object): Invoke export_signals.
(dbus_g_connection_lookup_g_object): New function.
(DBusGFuncSignature) New structure; used for mapping type
signatures to marshallers.
(funcsig_hash): New function; hashes DBusGFuncSignature.
(funcsig_equal): New function; compares DBusGFuncSignature.
(_dbus_gobject_lookup_marshaller): New function.
(dbus_g_object_register_marshaller): New function; used to
register a marshaller at runtime for a particular signature.

* glib/dbus-gmain.c (_dbus_gmain_test): Add various tests.

* glib/dbus-binding-tool-glib.h: Add DBUS_GLIB_ANNOTATION_ASYNC
which notes a server method implementation should be
asynchronous.

* glib/dbus-binding-tool-glib.c
(dbus_binding_tool_output_glib_server): Call
dbus_g_value_types_init.
(write_formal_parameters): Use dbus_gtype_from_signature.  Handle
variants specially.
(dbus_g_type_get_lookup_function): Turn GType into an invocation
of a lookup function.
(write_args_for_direction): Use dbus_g_type_get_lookup_function.
(write_untyped_out_args): New method; write output arguments.
(write_formal_declarations_for_direction): Function for
writing prototypes.
(write_formal_parameters_for_direction): Function for
writing implementations.
(write_typed_args_for_direction): Function for writing
arguments prefixed with GTypes.
(write_async_method_client): Write out async version
of method.

* glib/dbus-binding-tool-glib.c: Include dbus-gvalue-utils.h.
(dbus_g_type_get_marshal_name): Move mapping from GType
to marshal name into here.
(dbus_g_type_get_c_name): Move into here.
(compute_marshaller): Convert signature to type with
dbus_gtype_from_signature, use dbus_g_type_get_marshal_name.
(compute_marshaller_name): Ditto.
(compute_marshaller): Handle async signal annotations.
(gather_marshallers): Return if we don't have a known
prefix.
(generate_glue): Collect introspection blob here, and
write all of the blob at the end.  This allows an object
with multiple interfaces to work.
Mark async methods in introspection blob.

* glib/Makefile.am (libdbus_glib_1_la_SOURCES): Add
dbus-gtype-specialized.c, dbus-gtype-specialized.h,
dbus-gvalue-utils.h, dbus-gvalue-utils.c.

* dbus/dbus-glib.h: Don't include dbus-protocol.h; this
avoids people accidentally using DBUS_TYPE_* which should
not be necessary anymore.
Do include dbus-gtype-specialized.h, which are utilities
for GLib container types.
Add various #defines for types such as
DBUS_TYPE_G_BOOLEAN_ARRAY.
(DBusGValueIterator, DBusGValue): Define, not fully used
yet.
(dbus_g_value_get_g_type): Type for recursive value.
(dbus_g_value_open, dbus_g_value_iterator_get_value)
(dbus_g_value_iterator_get_values, dbus_g_value_iterator_recurse)
(dbus_g_value_free): Prototypes.
(dbus_g_object_register_marshaller, dbus_g_proxy_new_from_proxy): Prototype.
(dbus_g_proxy_set_interface): Prototype.
(dbus_g_proxy_begin_call, dbus_g_proxy_end_call)
(dbus_g_proxy_call_no_reply): Take GLib types instead of DBus
types.
(dbus_g_proxy_get_path, dbus_g_proxy_get_interface):
Accessors.
(DBusGAsyncData, DBusGMethodInvocation): Structures for
doing async invocations.
(dbus_g_method_return, dbus_g_method_return_error):
Prototypes.
* doc/dbus-tutorial.xml: Update GLib section.

* tools/dbus-viewer.c (load_child_nodes): Update
for new invocation type of dbus_g_proxy_end_call.
(load_from_service_thread_func): Ditto.

* tools/print-introspect.c (main): Ditto.

* tools/dbus-names-model.c (have_names_notify)
(names_model_reload, names_model_set_connection)
Use GTypes.

* python/Makefile.am (INCLUDES): Define DBUS_COMPILATION,
needed since Python bindings use GLib bindings.

* test/glib/Makefile.am (INCLUDES): Define DBUS_COMPILATION.
Add --prefix argument.

* tools/Makefile.am: Define DBUS_COMPILATION.  Remove
unneeded --ignore-unsupported arg.

* test/glib/test-service-glib.c:
* test/glib/test-service-glib.xml:
* test/glib/test-dbus-glib.c: Add many more tests.

26 files changed:
ChangeLog
dbus/dbus-glib.h
doc/TODO
doc/dbus-tutorial.xml
glib/Makefile.am
glib/dbus-binding-tool-glib.c
glib/dbus-binding-tool-glib.h
glib/dbus-gmain.c
glib/dbus-gobject.c
glib/dbus-gobject.h
glib/dbus-gproxy.c
glib/dbus-gtype-specialized.c [new file with mode: 0644]
glib/dbus-gtype-specialized.h [new file with mode: 0644]
glib/dbus-gvalue-utils.c [new file with mode: 0644]
glib/dbus-gvalue-utils.h [new file with mode: 0644]
glib/dbus-gvalue.c
glib/dbus-gvalue.h
python/Makefile.am
test/glib/Makefile.am
test/glib/test-dbus-glib.c
test/glib/test-service-glib.c
test/glib/test-service-glib.xml
tools/Makefile.am
tools/dbus-names-model.c
tools/dbus-viewer.c
tools/print-introspect.c

index bf71a3b..305d767 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,187 @@
+2005-06-12  Colin Walters  <walters@verbum.org>
+
+       Async signals and various bugfixes and testing by
+       Ross Burton <ross@burtonini.com>.
+
+       * glib/dbus-gvalue.h: (struct DBusBasicGValue): Delete.
+       (dbus_gvalue_genmarshal_name_from_type)
+       (dbus_gvalue_ctype_from_type): Moved to dbus-binding-tool-glib.c.
+       (dbus_gtype_to_dbus_type): Renamed to dbus_gtype_from_signature.
+       (dbus_g_value_types_init, dbus_gtype_from_signature)
+       (dbus_gtype_from_signature_iter, dbus_gtype_to_signature)
+       (dbus_gtypes_from_arg_signature): New function prototypes.
+       (dbus_gvalue_demarshal): Take context and error arguments.
+       (dbus_gvalue_demarshal_variant): New function.
+       (dbus_gvalue_demarshal_message): New function.
+       (dbus_gvalue_store): Delete.
+
+       * glib/dbus-gvalue.c:
+
+       File has been almost entirely rewritten; now we special-case
+       more types such as DBUS_TYPE_SIGNATURE, handle arrays and
+       hash tables correctly, etc.  Full support for recursive values
+       is not yet complete.
+
+       * glib/dbus-gproxy.c (dbus_g_proxy_class_init): Change last
+       argument of signal to G_TYPE_POINTER since we now pass a
+       structure.
+       (lookup_g_marshaller): Delete in favor of
+       _dbus_gobject_lookup_marshaller.
+       (marshal_dbus_message_to_g_marshaller): Use
+       _dbus_gobject_lookup_marshaller and dbus_gvalue_demarshal_message
+       to handle remote signal callbacks.
+       (dbus_g_proxy_new_from_proxy): New function; creates a new
+       DBusGProxy by copying an existing one.
+       (dbus_g_proxy_get_interface, dbus_g_proxy_set_interface)
+       (dbus_g_proxy_get_path): New functions.
+       (dbus_g_proxy_marshal_args_to_message): New function;
+       factored out of existing code.
+       (DBUS_G_VALUE_ARRAY_COLLECT_ALL): Collect all arguments
+       from a varargs array.
+       (dbus_g_proxy_begin_call_internal): New function.
+       (dbus_g_proxy_end_call_internal): New function.
+       (dbus_g_proxy_begin_call): Take GTypes instead of DBus types
+       as arguments; simply invoke dbus_g_proxy_begin_call_internal
+       after collecting args into value array.
+       (dbus_g_proxy_end_call): Take GTypes instead of DBus types;
+       invoke dbus_g_proxy_end_call_internal.
+       (dbus_g_proxy_invoke): Simply invoke begin_call_interanl and
+       end_call_internal.
+       (dbus_g_proxy_call_no_reply): Take GTypes instead of DBus
+       types.
+       (array_free_all): New function.
+       (dbus_g_proxy_add_signal): Take GTypes.
+
+       * glib/dbus-gobject.h:
+       (_dbus_glib_marshal_dbus_message_to_gvalue_array): Delete.
+       (_dbus_gobject_get_path, _dbus_gobject_lookup_marshaller):
+       Prototype.
+
+       * glib/dbus-gobject.c: Add a global marshal_table hash which
+       stores mappings from type signatures to marshallers.  Change lots
+       of invocations of dbus_gtype_to_dbus_type to
+       dbus_gtype_to_signature.
+       (_dbus_glib_marshal_dbus_message_to_gvalue_array): Delete.
+       (introspect_signals): Fix test for query.return_type.
+       (set_object_property): Update invocation of dbus_gvalue_demarshal.
+       (invoke_object_method): Many changes.  Handle asynchronous
+       invocations.  Convert arguments with
+       dbus_gvalue_demarshal_message.  Handle errors.  Use
+       DBusSignatureIter instead of strlen on args. Handle all arguments
+       generically.  Special-case variants.
+       (dbus_g_method_return, dbus_g_method_return_error): New function.
+       (DBusGSignalClosure): New structure, closes over signal
+       information.
+       (dbus_g_signal_closure_new): New function.
+       (dbus_g_signal_closure_finalize): New function.
+       (signal_emitter_marshaller): New function; is special marshaller
+       which emits signals on bus.
+       (export_signals): New function; introspects object signals and
+       connects to them.
+       (dbus_g_object_type_install_info): Take GType instead of
+       GObjectClass.
+       (dbus_g_connection_register_g_object): Invoke export_signals.
+       (dbus_g_connection_lookup_g_object): New function.
+       (DBusGFuncSignature) New structure; used for mapping type
+       signatures to marshallers.
+       (funcsig_hash): New function; hashes DBusGFuncSignature.
+       (funcsig_equal): New function; compares DBusGFuncSignature.
+       (_dbus_gobject_lookup_marshaller): New function.
+       (dbus_g_object_register_marshaller): New function; used to
+       register a marshaller at runtime for a particular signature.
+
+       * glib/dbus-gmain.c (_dbus_gmain_test): Add various tests.
+
+       * glib/dbus-binding-tool-glib.h: Add DBUS_GLIB_ANNOTATION_ASYNC
+       which notes a server method implementation should be
+       asynchronous.
+
+       * glib/dbus-binding-tool-glib.c
+       (dbus_binding_tool_output_glib_server): Call
+       dbus_g_value_types_init.
+       (write_formal_parameters): Use dbus_gtype_from_signature.  Handle
+       variants specially.
+       (dbus_g_type_get_lookup_function): Turn GType into an invocation
+       of a lookup function.
+       (write_args_for_direction): Use dbus_g_type_get_lookup_function.
+       (write_untyped_out_args): New method; write output arguments.
+       (write_formal_declarations_for_direction): Function for
+       writing prototypes.
+       (write_formal_parameters_for_direction): Function for
+       writing implementations.
+       (write_typed_args_for_direction): Function for writing
+       arguments prefixed with GTypes.
+       (write_async_method_client): Write out async version
+       of method.
+
+       * glib/dbus-binding-tool-glib.c: Include dbus-gvalue-utils.h.
+       (dbus_g_type_get_marshal_name): Move mapping from GType
+       to marshal name into here.
+       (dbus_g_type_get_c_name): Move into here.
+       (compute_marshaller): Convert signature to type with
+       dbus_gtype_from_signature, use dbus_g_type_get_marshal_name.
+       (compute_marshaller_name): Ditto.
+       (compute_marshaller): Handle async signal annotations.
+       (gather_marshallers): Return if we don't have a known
+       prefix.
+       (generate_glue): Collect introspection blob here, and
+       write all of the blob at the end.  This allows an object
+       with multiple interfaces to work.
+       Mark async methods in introspection blob.
+
+       * glib/Makefile.am (libdbus_glib_1_la_SOURCES): Add
+       dbus-gtype-specialized.c, dbus-gtype-specialized.h,
+       dbus-gvalue-utils.h, dbus-gvalue-utils.c.
+
+       * dbus/dbus-glib.h: Don't include dbus-protocol.h; this
+       avoids people accidentally using DBUS_TYPE_* which should
+       not be necessary anymore.
+       Do include dbus-gtype-specialized.h, which are utilities
+       for GLib container types.
+       Add various #defines for types such as
+       DBUS_TYPE_G_BOOLEAN_ARRAY.
+       (DBusGValueIterator, DBusGValue): Define, not fully used
+       yet.
+       (dbus_g_value_get_g_type): Type for recursive value.
+       (dbus_g_value_open, dbus_g_value_iterator_get_value)
+       (dbus_g_value_iterator_get_values, dbus_g_value_iterator_recurse)
+       (dbus_g_value_free): Prototypes.
+       (dbus_g_object_register_marshaller, dbus_g_proxy_new_from_proxy): Prototype.
+       (dbus_g_proxy_set_interface): Prototype.
+       (dbus_g_proxy_begin_call, dbus_g_proxy_end_call)
+       (dbus_g_proxy_call_no_reply): Take GLib types instead of DBus
+       types.
+       (dbus_g_proxy_get_path, dbus_g_proxy_get_interface):
+       Accessors.
+       (DBusGAsyncData, DBusGMethodInvocation): Structures for
+       doing async invocations.
+       (dbus_g_method_return, dbus_g_method_return_error):
+       Prototypes.
+       * doc/dbus-tutorial.xml: Update GLib section.
+       
+       * tools/dbus-viewer.c (load_child_nodes): Update
+       for new invocation type of dbus_g_proxy_end_call.
+       (load_from_service_thread_func): Ditto.
+
+       * tools/print-introspect.c (main): Ditto.
+
+       * tools/dbus-names-model.c (have_names_notify)
+       (names_model_reload, names_model_set_connection)
+       Use GTypes.
+
+       * python/Makefile.am (INCLUDES): Define DBUS_COMPILATION,
+       needed since Python bindings use GLib bindings.
+
+       * test/glib/Makefile.am (INCLUDES): Define DBUS_COMPILATION.
+       Add --prefix argument.
+
+       * tools/Makefile.am: Define DBUS_COMPILATION.  Remove
+       unneeded --ignore-unsupported arg.
+       
+       * test/glib/test-service-glib.c: 
+       * test/glib/test-service-glib.xml:
+       * test/glib/test-dbus-glib.c: Add many more tests.
+
 2005-06-06  David Zeuthen  <davidz@redhat.com>
 
        * doc/TODO: Add item about need to remove deprecated functions.
index e1b4a70..7921c91 100644 (file)
@@ -25,7 +25,6 @@
 #define DBUS_GLIB_H
 
 #include <glib-object.h>
-#include <dbus/dbus-protocol.h>
 #include <dbus/dbus-shared.h>
 
 G_BEGIN_DECLS
@@ -115,12 +114,88 @@ struct DBusGObjectInfo
   const char *data;             /**< Introspection data */
 };
 
-void dbus_g_object_class_install_info    (GObjectClass          *object_class,
+void dbus_g_object_type_install_info     (GType                 object_type,
                                           const DBusGObjectInfo *info);
-void dbus_g_connection_register_g_object (DBusGConnection       *connection,
-                                          const char            *at_path,
-                                          GObject               *object);
 
+void       dbus_g_connection_register_g_object (DBusGConnection       *connection,
+                                               const char            *at_path,
+                                               GObject               *object);
+GObject *  dbus_g_connection_lookup_g_object   (DBusGConnection       *connection,
+                                               const char            *at_path);
+
+
+/**
+ * Generic recursive value
+ */
+
+typedef struct DBusGValueIterator DBusGValueIterator;
+struct DBusGValueIterator
+{
+  void *dummy1;         /**< Don't use this */
+  void *dummy2;         /**< Don't use this */
+  guint32 dummy3;     /**< Don't use this */
+  int dummy4;           /**< Don't use this */
+  int dummy5;           /**< Don't use this */
+  int dummy6;           /**< Don't use this */
+  int dummy7;           /**< Don't use this */
+  int dummy8;           /**< Don't use this */
+  int dummy9;           /**< Don't use this */
+  int dummy10;          /**< Don't use this */
+  int dummy11;          /**< Don't use this */
+  int pad1;             /**< Don't use this */
+  int pad2;             /**< Don't use this */
+  void *pad3;           /**< Don't use this */
+  void *pad4;           /**< Don't use this */
+  void *pad5;           /**< Don't use this */
+};
+
+typedef struct DBusGValue DBusGValue;
+
+#ifdef DBUS_COMPILATION
+#include "glib/dbus-gtype-specialized.h"
+#else
+#include <dbus/dbus-gtype-specialized.h>
+#endif
+
+/* definitions for some basic array types */
+#define DBUS_TYPE_G_BOOLEAN_ARRAY  (dbus_g_type_get_collection ("GArray", G_TYPE_BOOLEAN))
+#define DBUS_TYPE_G_UCHAR_ARRAY    (dbus_g_type_get_collection ("GArray", G_TYPE_UCHAR))
+#define DBUS_TYPE_G_UINT_ARRAY     (dbus_g_type_get_collection ("GArray", G_TYPE_UINT))
+#define DBUS_TYPE_G_INT_ARRAY      (dbus_g_type_get_collection ("GArray", G_TYPE_INT))
+#define DBUS_TYPE_G_UINT64_ARRAY   (dbus_g_type_get_collection ("GArray", G_TYPE_UINT64))
+#define DBUS_TYPE_G_INT64_ARRAY    (dbus_g_type_get_collection ("GArray", G_TYPE_INT64))
+#define DBUS_TYPE_G_OBJECT_ARRAY   (dbus_g_type_get_collection ("GPtrArray", G_TYPE_OBJECT))
+
+#define DBUS_TYPE_G_STRING_STRING_HASHTABLE (dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_STRING))
+
+/* D-BUS-specific types */
+#define DBUS_TYPE_G_PROXY_ARRAY          (dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_PROXY))
+
+/* Generic recursive value */
+GType        dbus_g_value_get_g_type               (void) G_GNUC_CONST;
+#define DBUS_TYPE_G_VALUE                (dbus_g_value_get_g_type ())
+
+void         dbus_g_value_open              (DBusGValue          *value,
+                                            DBusGValueIterator  *iter);
+
+GType        dbus_g_value_iterator_get_value        (DBusGValueIterator   *iter,
+                                                    GValue               *value);
+
+gboolean     dbus_g_value_iterator_get_values       (DBusGValueIterator   *iter,
+                                                    GError              **error,
+                                                    GValue               *first_val,
+                                                    ...);
+
+void         dbus_g_value_iterator_recurse          (DBusGValueIterator   *iter,
+                                                    DBusGValueIterator   *sub);
+
+void         dbus_g_value_free              (DBusGValue          *value);
+
+
+void         dbus_g_object_register_marshaller  (GType             rettype,
+                                                guint             n_types,
+                                                const GType      *param_types,
+                                                GClosureMarshal   marshaller);
 
 typedef struct DBusGProxy       DBusGProxy;
 typedef struct DBusGProxyClass  DBusGProxyClass;
@@ -143,12 +218,20 @@ DBusGProxy*       dbus_g_proxy_new_for_name_owner    (DBusGConnection   *connect
                                                       const char        *path,
                                                       const char        *interface,
                                                       GError           **error);
+DBusGProxy*       dbus_g_proxy_new_from_proxy        (DBusGProxy        *proxy,
+                                                      const char        *interface,
+                                                      const char        *path_name);
 DBusGProxy*       dbus_g_proxy_new_for_peer          (DBusGConnection   *connection,
                                                       const char        *path_name,
                                                       const char        *interface_name);
+
+void              dbus_g_proxy_set_interface         (DBusGProxy        *proxy,
+                                                     const char        *interface_name);
 void              dbus_g_proxy_add_signal            (DBusGProxy        *proxy,
-                                                      const char        *signal_name,
-                                                      const char        *signature);
+                                                     const char        *signal_name,
+                                                     GType              first_type, 
+                                                     ...);
+
 void              dbus_g_proxy_connect_signal        (DBusGProxy        *proxy,
                                                       const char        *signal_name,
                                                       GCallback          handler,
@@ -160,27 +243,46 @@ void              dbus_g_proxy_disconnect_signal     (DBusGProxy        *proxy,
                                                       void              *data);
 DBusGPendingCall* dbus_g_proxy_begin_call            (DBusGProxy        *proxy,
                                                       const char        *method,
-                                                      int                first_arg_type,
+                                                      GType              first_arg_type,
                                                       ...);
 gboolean          dbus_g_proxy_end_call              (DBusGProxy        *proxy,
                                                       DBusGPendingCall  *pending,
                                                       GError           **error,
-                                                      int                first_arg_type,
+                                                      GType              first_arg_type,
                                                       ...);
 void              dbus_g_proxy_call_no_reply         (DBusGProxy        *proxy,
                                                       const char        *method,
-                                                      int                first_arg_type,
+                                                      GType              first_arg_type,
                                                       ...);
 
+const char*       dbus_g_proxy_get_path              (DBusGProxy        *proxy);
+
 const char*       dbus_g_proxy_get_bus_name          (DBusGProxy        *proxy);
 
+const char*       dbus_g_proxy_get_interface         (DBusGProxy        *proxy);
+
 gboolean          dbus_g_proxy_invoke                (DBusGProxy        *proxy,
                                                      const char        *method,
-                                                     const char        *insig,
-                                                     const char        *outsig,
                                                      GError           **error,
+                                                     GType              first_arg_type,
                                                      ...);
 
+typedef struct {
+  DBusGProxy *proxy;
+  gpointer cb;
+  gpointer userdata;
+} DBusGAsyncData;
+
+typedef struct {
+  DBusGConnection *connection;
+  DBusGMessage *message;
+  const DBusGObjectInfo *object;
+  const DBusGMethodInfo *method;
+} DBusGMethodInvocation;
+
+void dbus_g_method_return (DBusGMethodInvocation *context, ...);
+void dbus_g_method_return_error (DBusGMethodInvocation *context, GError *error);
+
 #undef DBUS_INSIDE_DBUS_GLIB_H
 
 G_END_DECLS
index 28a772d..659dde1 100644 (file)
--- a/doc/TODO
+++ b/doc/TODO
@@ -39,12 +39,11 @@ Important for 1.0
 Important for 1.0 GLib Bindings
 ===
 
- - finish dbus-glib-tool support for adding introspection 
-   data to GObject and autoexporting GObject using same
+ - Annotations for "do not take ownership of this return value" on server
 
- - Need to make sure that dbus-glib.h never returns any 
-   dbus_malloc() memory, only g_malloc(). 
-   dbus_g_proxy_end_call() is the major offender. 
+ - Fix signals
+
+ - Fix errors - need to get specific error back, not UnmappedError crap
 
  - DBusGProxy doesn't emit "destroy" when it should
 
index 22906ac..6ee3292 100644 (file)
     <title>GLib API: Using Remote Objects</title>
 
     <para>
-      
       The GLib binding is defined in the header file
-      &lt;dbus/dbus-glib.h&gt;. The API is very small, in sharp contrast to the
-      low-level &lt;dbus/dbus.h&gt;.
-
+      &lt;dbus/dbus-glib.h&gt;.
     </para>
 
-    <para>
-      The GLib bindings are incomplete, see the TODO file and comments in the
-      source code.
-    </para>
-
-    <para>
-Here is a D-BUS program using the GLib bindings.
+    <sect2 id="glib-typemappings">
+      <title>D-BUS - GLib type mappings</title>
+      <para>
+       The heart of the GLib bindings for D-BUS is the mapping it
+       provides between D-BUS "type signatures" and GLib types
+       (<literal>GType</literal>). The D-BUS type system is composed of
+       a number of "basic" types, along with several "container" types.
+      </para>
+      <sect3 id="glib-basic-typemappings">
+       <title>Basic type mappings</title>
+       <para>
+         Below is a list of the basic types, along with their associated
+         mapping to a <literal>GType</literal>.
+         <informaltable>
+           <tgroup cols="4">
+             <thead>
+               <row>
+                 <entry>D-BUS basic type</entry>
+                 <entry>GType</entry>
+                 <entry>Free function</entry>
+                 <entry>Notes</entry>
+               </row>
+             </thead>
+             <tbody>
+               <row>
+                 <entry><literal>BYTE</literal></entry>
+                 <entry><literal>G_TYPE_UCHAR</literal></entry>
+                 <entry></entry>
+                 <entry></entry>
+                 </row><row>
+                 <entry><literal>BOOLEAN</literal></entry>
+                 <entry><literal>G_TYPE_BOOLEAN</literal></entry>
+                 <entry></entry>
+                 <entry></entry>
+                 </row><row>
+                 <entry><literal>INT16</literal></entry>
+                 <entry><literal>G_TYPE_INT</literal></entry>
+                 <entry></entry>
+                 <entry>Will be changed to a G_TYPE_INT16 once GLib has it</entry>
+                 </row><row>
+                 <entry><literal>UINT16</literal></entry>
+                 <entry><literal>G_TYPE_UINT</literal></entry>
+                 <entry></entry>
+                 <entry>Will be changed to a G_TYPE_UINT16 once GLib has it</entry>
+                 </row><row>
+                 <entry><literal>INT32</literal></entry>
+                 <entry><literal>G_TYPE_INT</literal></entry>
+                 <entry></entry>
+                 <entry>Will be changed to a G_TYPE_INT32 once GLib has it</entry>
+                 </row><row>
+                 <entry><literal>UINT32</literal></entry>
+                 <entry><literal>G_TYPE_UINT</literal></entry>
+                 <entry></entry>
+                 <entry>Will be changed to a G_TYPE_UINT32 once GLib has it</entry>
+                 </row><row>
+                 <entry><literal>INT64</literal></entry>
+                 <entry><literal>G_TYPE_GINT64</literal></entry>
+                 <entry></entry>
+                 <entry></entry>
+                 </row><row>
+                 <entry><literal>UINT64</literal></entry>
+                 <entry><literal>G_TYPE_GUINT64</literal></entry>
+                 <entry></entry>
+                 <entry></entry>
+                 </row><row>
+                 <entry><literal>DOUBLE</literal></entry>
+                 <entry><literal>G_TYPE_DOUBLE</literal></entry>
+                 <entry></entry>
+                 <entry></entry>
+                 </row><row>
+                 <entry><literal>STRING</literal></entry>
+                 <entry><literal>G_TYPE_STRING</literal></entry>
+                 <entry>g_free</entry>
+                 <entry></entry>
+                 </row><row>
+                 <entry><literal>OBJECT_PATH</literal></entry>
+                 <entry><literal>DBUS_TYPE_G_PROXY</literal></entry>
+                 <entry>g_object_unref</entry>
+                 <entry>The returned proxy does not have an interface set; use <literal>dbus_g_proxy_set_interface</literal> to invoke methods</entry>
+               </row>
+             </tbody>
+           </tgroup>
+         </informaltable>
+         As you can see, the basic mapping is fairly straightforward.
+       </para>
+      </sect3>
+      <sect3 id="glib-container-typemappings">
+       <title>Container type mappings</title>
+       <para>
+         The D-BUS type system also has a number of "container"
+         types, such as <literal>DBUS_TYPE_ARRAY</literal> and
+         <literal>DBUS_TYPE_STRUCT</literal>.  The D-BUS type system
+         is fully recursive, so one can for example have an array of
+         array of strings (i.e. type signature
+         <literal>aas</literal>).
+       </para>
+       <para>
+         However, not all of these types are in common use; for
+         example, at the time of this writing the author knows of no
+         one using <literal>DBUS_TYPE_STRUCT</literal>, or a
+         <literal>DBUS_TYPE_ARRAY</literal> containing any non-basic
+         type.  The approach the GLib bindings take is pragmatic; try
+         to map the most common types in the most obvious way, and
+         let using less common and more complex types be less
+         "natural".
+       </para>
+       <para>
+         First, D-BUS type signatures which have an "obvious"
+         corresponding builtin GLib type are mapped using that type:
+         <informaltable>
+           <tgroup cols="6">
+             <thead>
+               <row>
+                 <entry>D-BUS type signature</entry>
+                 <entry>Description</entry>
+                 <entry>GType</entry>
+                 <entry>C typedef</entry>
+                 <entry>Free function</entry>
+                 <entry>Notes</entry>
+               </row>
+             </thead>
+             <tbody>
+               <row>
+                 <entry><literal>as</literal></entry>
+                 <entry>Array of strings</entry>
+                 <entry><literal>G_TYPE_STRV</literal></entry>
+                 <entry><literal>char **</literal></entry>
+                 <entry>g_strfreev</entry>
+                 <entry></entry>
+                 </row><row>
+                 <entry><literal>v</literal></entry>
+                 <entry>Generic value container</entry>
+                 <entry><literal>G_TYPE_VALUE</literal></entry>
+                 <entry><literal>GValue *</literal></entry>
+                 <entry>g_value_unset</entry>
+                 <entry>The calling conventions for values expect that method callers have allocated return values; see below.</entry>
+               </row>
+             </tbody>
+           </tgroup>
+         </informaltable>
+       </para>
+       <para>
+         The next most common recursive type signatures are arrays of
+         basic values.  The most obvious mapping for arrays of basic
+         types is a <literal>GArray</literal>.  Now, GLib does not
+         provide a builtin <literal>GType</literal> for
+         <literal>GArray</literal>.  However, we actually need more than
+         that - we need a "parameterized" type which includes the
+         contained type.  Why we need this we will see below.
+       </para>
+       <para>
+         The approach taken is to create these types in the D-BUS GLib
+         bindings; however, there is nothing D-BUS specific about them.
+         In the future, we hope to include such "fundamental" types in GLib
+         itself.
+         <informaltable>
+           <tgroup cols="6">
+             <thead>
+               <row>
+                 <entry>D-BUS type signature</entry>
+                 <entry>Description</entry>
+                 <entry>GType</entry>
+                 <entry>C typedef</entry>
+                 <entry>Free function</entry>
+                 <entry>Notes</entry>
+               </row>
+             </thead>
+             <tbody>
+               <row>
+                 <entry><literal>ay</literal></entry>
+                 <entry>Array of bytes</entry>
+                 <entry><literal>DBUS_TYPE_G_BYTE_ARRAY</literal></entry>
+                 <entry><literal>GArray *</literal></entry>
+                 <entry>g_array_free</entry>
+                 <entry></entry>
+               </row>
+               <row>
+                 <entry><literal>au</literal></entry>
+                 <entry>Array of uint</entry>
+                 <entry><literal>DBUS_TYPE_G_UINT_ARRAY</literal></entry>
+                 <entry><literal>GArray *</literal></entry>
+                 <entry>g_array_free</entry>
+                 <entry></entry>
+               </row>
+               <row>
+                 <entry><literal>ai</literal></entry>
+                 <entry>Array of int</entry>
+                 <entry><literal>DBUS_TYPE_G_INT_ARRAY</literal></entry>
+                 <entry><literal>GArray *</literal></entry>
+                 <entry>g_array_free</entry>
+                 <entry></entry>
+               </row>
+               <row>
+                 <entry><literal>ax</literal></entry>
+                 <entry>Array of int64</entry>
+                 <entry><literal>DBUS_TYPE_G_INT64_ARRAY</literal></entry>
+                 <entry><literal>GArray *</literal></entry>
+                 <entry>g_array_free</entry>
+                 <entry></entry>
+               </row>
+               <row>
+                 <entry><literal>at</literal></entry>
+                 <entry>Array of uint64</entry>
+                 <entry><literal>DBUS_TYPE_G_UINT64_ARRAY</literal></entry>
+                 <entry><literal>GArray *</literal></entry>
+                 <entry>g_array_free</entry>
+                 <entry></entry>
+               </row>
+               <row>
+                 <entry><literal>ad</literal></entry>
+                 <entry>Array of double</entry>
+                 <entry><literal>DBUS_TYPE_G_DOUBLE_ARRAY</literal></entry>
+                 <entry><literal>GArray *</literal></entry>
+                 <entry>g_array_free</entry>
+                 <entry></entry>
+               </row>
+               <row>
+                 <entry><literal>ab</literal></entry>
+                 <entry>Array of boolean</entry>
+                 <entry><literal>DBUS_TYPE_G_BOOLEAN_ARRAY</literal></entry>
+                 <entry><literal>GArray *</literal></entry>
+                 <entry>g_array_free</entry>
+                 <entry></entry>
+               </row>
+             </tbody>
+           </tgroup>
+         </informaltable>
+       </para>
+       <para>
+         D-BUS also includes a special type DBUS_TYPE_DICT_ENTRY which
+         is only valid in arrays.  It's intended to be mapped to a "dictionary"
+         type by bindings.  The obvious GLib mapping here is GHashTable.  Again,
+         however, there is no builtin <literal>GType</literal> for a GHashTable.
+         Moreover, just like for arrays, we need a parameterized type so that
+         the bindings can communiate which types are contained in the hash table.
+       </para>
+       <para>
+         At present, only strings are supported.  Work is in progress to
+         include more types.
+         <informaltable>
+           <tgroup cols="6">
+             <thead>
+               <row>
+                 <entry>D-BUS type signature</entry>
+                 <entry>Description</entry>
+                 <entry>GType</entry>
+                 <entry>C typedef</entry>
+                 <entry>Free function</entry>
+                 <entry>Notes</entry>
+               </row>
+             </thead>
+             <tbody>
+               <row>
+                 <entry><literal>a{ss}</literal></entry>
+                 <entry>Dictionary mapping strings to strings</entry>
+                 <entry><literal>DBUS_TYPE_G_STRING_STRING_HASHTABLE</literal></entry>
+                 <entry><literal>GHashTable *</literal></entry>
+                 <entry>g_hash_table_destroy</entry>
+                 <entry></entry>
+               </row>
+             </tbody>
+           </tgroup>
+         </informaltable>
+       </para>
+      </sect3>
+      <sect3 id="glib-generic-typemappings">
+       <title>Arbitrarily recursive type mappings</title>
+       <para>
+         Finally, it is possible users will want to write or invoke D-BUS
+         methods which have arbitrarily complex type signatures not
+         directly supported by these bindings.  For this case, we have a
+         <literal>DBusGValue</literal> which acts as a kind of special
+         variant value which may be iterated over manually.  The
+         <literal>GType</literal> associated is
+         <literal>DBUS_TYPE_G_VALUE</literal>.
+       </para>
+       <para>
+         TODO insert usage of <literal>DBUS_TYPE_G_VALUE</literal> here.
+       </para>
+      </sect3>
+    </sect2>
+    <sect2 id="sample-program-1">
+      <title>A sample program</title>
+      <para>Here is a D-BUS program using the GLib bindings.
 <programlisting>      
 int
 main (int argc, char **argv)
@@ -470,10 +744,8 @@ main (int argc, char **argv)
   DBusGConnection *connection;
   GError *error;
   DBusGProxy *proxy;
-  DBusGPendingCall *call;
   char **name_list;
-  int name_list_len;
-  int i;
+  char **name_list_ptr;
   
   g_type_init ();
 
@@ -495,15 +767,10 @@ main (int argc, char **argv)
                                      DBUS_PATH_ORG_FREEDESKTOP_DBUS,
                                      DBUS_INTERFACE_ORG_FREEDESKTOP_DBUS);
 
-  /* Call ListNames method */
-  
-  call = dbus_g_proxy_begin_call (proxy, "ListNames", DBUS_TYPE_INVALID);
-
+  /* Call ListNames method, wait for reply */
   error = NULL;
-  if (!dbus_g_proxy_end_call (proxy, call, &amp;error,
-                              DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
-                              &amp;name_list, &amp;name_list_len,
-                              DBUS_TYPE_INVALID))
+  if (!dbus_g_proxy_invoke (proxy, "ListNames", &amp;error, G_TYPE_INVALID,
+                            G_TYPE_STRV, &amp;name_list, G_TYPE_INVALID))
     {
       g_printerr ("Failed to complete ListNames call: %s\n",
                   error-&gt;message);
@@ -514,77 +781,348 @@ main (int argc, char **argv)
   /* Print the results */
  
   g_print ("Names on the message bus:\n");
-  i = 0;
-  while (i &lt; name_list_len)
+  
+  for (name_list_ptr = name_list; *name_list_ptr; name_list_ptr++)
     {
-      g_assert (name_list[i] != NULL);
-      g_print ("  %s\n", name_list[i]);
-      ++i;
+      g_print ("  %s\n", *name_list_ptr);
     }
-  g_assert (name_list[i] == NULL);
-
   g_strfreev (name_list);
 
+  g_object_unref (proxy);
+
   return 0;
 }
 </programlisting>
     </para>
+    </sect2>
+    <sect2 id="glib-program-setup">
+      <title>Program initalization</title>
+      <para>
+       A connection to the bus is acquired using
+       <literal>dbus_g_bus_get</literal>.  Next, a proxy
+       is created for the object "/org/freedesktop/DBus" with
+       interface <literal>org.freedesktop.DBus</literal>
+       on the service <literal>org.freedesktop.DBus</literal>.
+       This is a proxy for the message bus itself.
+      </para>
+    </sect2>
+    <sect2 id="glib-method-invocation">
+      <title>Understanding method invocation</title>
+      <para>
+       You have a number of choices for method invocation.  First, as
+       used above, <literal>dbus_g_proxy_invoke</literal> sends a
+       method call to the remote object, and blocks until reply is
+       recieved.  The outgoing arguments are specified in the varargs
+       array, terminated with <literal>G_TYPE_INVALID</literal>.
+       Next, pointers to return values are specified, followed again
+       by <literal>G_TYPE_INVALID</literal>.
+      </para>
+      <para>
+       To invoke a method asynchronously, use
+       <literal>dbus_g_proxy_begin_call</literal>.  This returns a
+       <literal>DBusGPendingCall</literal> object; you may then set a
+       notification function using
+       <literal>dbus_g_pending_call_set_notify</literal>.
+      </para>
+    </sect2>
+    <sect2 id="glib-signal-connection">
+      <title>Connecting to object signals</title>
+      <para>
+       You may connect to signals using
+       <literal>dbus_g_proxy_add_signal</literal> and
+       <literal>dbus_g_proxy_connect_signal</literal>.  At the
+       moment, <literal>dbus_g_proxy_add_signal</literal> requires
+       the D-BUS types of the remote object; this will likely be
+       changed later.
+      </para>
+    </sect2>
+    <sect2 id="glib-more-examples">
+      <title>More examples of method invocation</title>
+      <sect3 id="glib-sending-stuff">
+       <title>Sending an integer and string, receiving an array of bytes</title>
+       <para>
+<programlisting>
+  GArray *arr;
+  
+  error = NULL;
+  if (!dbus_g_proxy_invoke (proxy, "Foobar", &amp;error,
+                            G_TYPE_INT, 42, G_TYPE_STRING, "hello",
+                           G_TYPE_INVALID,
+                           DBUS_TYPE_G_UCHAR_ARRAY, &amp;arr, G_TYPE_INVALID))
+    {
+      g_printerr ("Failed to complete Foobar: %s\n",
+                  error-&gt;message);
+      g_error_free (error);
+      exit (1);
+    }
+   g_assert (arr != NULL);
+   printf ("got back %u values", arr->len);
+</programlisting>
+       </para>
+      </sect3>
+      <sect3 id="glib-sending-hash">
+       <title>Sending a GHashTable</title>
+       <para>
+<programlisting>
+  GHashTable *hash = g_hash_table_new (g_str_hash, g_str_equal);
+  guint32 ret;
+  
+  g_hash_table_insert (hash, "foo", "bar");
+  g_hash_table_insert (hash, "baz", "whee");
 
-    <para>
-
-      DBusGProxy represents a remote object. dbus_g_proxy_begin_call() sends 
-      a method call to the remote object, and dbus_g_proxy_end_call() retrieves 
-      any return values or exceptions resulting from the method call. 
-      There are also DBusGProxy functions to connect and disconnect signals, 
-      not shown in the code example.
-
-    </para>
-
-    <para>
-      
-      dbus_g_bus_get() assumes that the application will use GMainLoop. The
-      created connection will be associated with the main loop such that
-      messages will be sent and received when the main loop runs.  However, in
-      the above code example the main loop never runs; D-BUS will not run the
-      loop implicitly. Instead, dbus_g_proxy_end_call() will block until the
-      method call has been sent and the reply received. A more complex GUI
-      application might run the main loop while waiting for the method call
-      reply.  (DBusGPendingCall is currently missing the "notify me when the
-      call is complete" functionality found in DBusPendingCall, but it should be
-      added.)
+  error = NULL;
+  if (!dbus_g_proxy_invoke (proxy, "HashSize", &amp;error,
+                            DBUS_TYPE_G_STRING_STRING_HASH, hash, G_TYPE_INVALID,
+                           G_TYPE_UINT, &amp;ret, G_TYPE_INVALID))
+    {
+      g_printerr ("Failed to complete HashSize: %s\n",
+                  error-&gt;message);
+      g_error_free (error);
+      exit (1);
+    }
+  g_assert (ret == 2);
+  g_hash_table_destroy (hash);
+</programlisting>
+       </para>
+      </sect3>
+      <sect3 id="glib-receiving-bool-int">
+       <title>Receiving a boolean and a string</title>
+       <para>
+<programlisting>
+  gboolean boolret;
+  char *strret;
+  
+  error = NULL;
+  if (!dbus_g_proxy_invoke (proxy, "GetStuff", &amp;error,
+                           G_TYPE_INVALID,
+                            G_TYPE_BOOLEAN, &amp;boolret,
+                            G_TYPE_STRING, &amp;strret,
+                           G_TYPE_INVALID))
+    {
+      g_printerr ("Failed to complete GetStuff: %s\n",
+                  error-&gt;message);
+      g_error_free (error);
+      exit (1);
+    }
+  printf ("%s %s", boolret ? "TRUE" : "FALSE", strret);
+  g_free (strret);
+</programlisting>
+       </para>
+      </sect3>
+      <sect3 id="glib-sending-str-arrays">
+       <title>Sending two arrays of strings</title>
+       <para>
+<programlisting>
+  /* NULL terminate */
+  char *strs_static[] = {"foo", "bar", "baz", NULL};
+  /* Take pointer to array; cannot pass array directly */
+  char **strs_static_p = strs_static;
+  char **strs_dynamic;
 
-    </para>
+  strs_dynamic = g_new (char *, 4);
+  strs_dynamic[0] = g_strdup ("hello");
+  strs_dynamic[1] = g_strdup ("world");
+  strs_dynamic[2] = g_strdup ("!");
+  /* NULL terminate */
+  strs_dynamic[3] = NULL;
+  
+  error = NULL;
+  if (!dbus_g_proxy_invoke (proxy, "TwoStrArrays", &amp;error,
+                            G_TYPE_STRV, strs_static_p,
+                            G_TYPE_STRV, strs_dynamic,
+                           G_TYPE_INVALID,
+                           G_TYPE_INVALID))
+    {
+      g_printerr ("Failed to complete TwoStrArrays: %s\n",
+                  error-&gt;message);
+      g_error_free (error);
+      exit (1);
+    }
+   g_strfreev (strs_dynamic);
+</programlisting>
+       </para>
+      </sect3>
+      <sect3 id="glib-getting-str-array">
+       <title>Sending a boolean, receiving an array of strings</title>
+       <para>
+<programlisting>
+  char **strs;
+  char **strs_p;
+  gboolean blah;
 
-    <para>
-      
-      Future plans (see doc/TODO) are to use G_TYPE_STRING in place of
-      DBUS_TYPE_STRING and so forth. In fact the above code is slightly
-      incorrect at the moment, since it uses g_strfreev() to free a string array
-      that was not allocated with g_malloc(). dbus_free_string_array() should
-      really be used. However, once the GLib bindings are complete the returned
-      data from dbus_g_proxy_end_call() will be allocated with g_malloc().
+  error = NULL;
+  blah = TRUE;
+  if (!dbus_g_proxy_invoke (proxy, "GetStrs", &amp;error,
+                            G_TYPE_BOOLEAN, blah,
+                           G_TYPE_INVALID,
+                            G_TYPE_STRV, &amp;strs,
+                           G_TYPE_INVALID))
+    {
+      g_printerr ("Failed to complete GetStrs: %s\n",
+                  error-&gt;message);
+      g_error_free (error);
+      exit (1);
+    }
+   for (strs_p = strs; *strs_p; strs_p++)
+     printf ("got string: \"%s\"", *strs_p);
+   g_strfreev (strs);
+</programlisting>
+       </para>
+      </sect3>
+      <sect3 id="glib-sending-variant">
+       <title>Sending a variant</title>
+       <para>
+<programlisting>
+  GValue val = {0, };
 
-    </para>
+  g_value_init (&amp;val, G_TYPE_STRING);
+  g_value_set_string (&amp;val, "hello world");
+  
+  error = NULL;
+  if (!dbus_g_proxy_invoke (proxy, "SendVariant", &amp;error,
+                            G_TYPE_VALUE, &amp;val, G_TYPE_INVALID,
+                           G_TYPE_INVALID))
+    {
+      g_printerr ("Failed to complete SendVariant: %s\n",
+                  error-&gt;message);
+      g_error_free (error);
+      exit (1);
+    }
+  g_assert (ret == 2);
+  g_value_unset (&amp;val);
+</programlisting>
+       </para>
+      </sect3>
+      <sect3 id="glib-receiving-variant">
+       <title>Receiving a variant</title>
+       <para>
+<programlisting>
+  GValue val = {0, };
 
+  error = NULL;
+  if (!dbus_g_proxy_invoke (proxy, "GetVariant", &amp;error, G_TYPE_INVALID,
+                            G_TYPE_VALUE, &amp;val, G_TYPE_INVALID))
+    {
+      g_printerr ("Failed to complete GetVariant: %s\n",
+                  error-&gt;message);
+      g_error_free (error);
+      exit (1);
+    }
+  if (G_VALUE_TYPE (&amp;val) == G_TYPE_STRING)
+    printf ("%s\n", g_value_get_string (&amp;val));
+  else if (G_VALUE_TYPE (&amp;val) == G_TYPE_INT)
+    printf ("%d\n", g_value_get_int (&amp;val));
+  else
+    ...
+  g_value_unset (&amp;val);
+</programlisting>
+       </para>
+      </sect3>
+    </sect2>
   </sect1>
 
   <sect1 id="glib-server">
     <title>GLib API: Implementing Objects</title>
-
     <para>
-      
-      The GLib binding is defined in the header file
-      &lt;dbus/dbus-glib.h&gt;. To implement an object, it's also necessary
-      to use the dbus-glib-tool command line tool.
-
+      At the moment, to expose a GObject via D-BUS, you must
+      write XML by hand which describes the methods exported
+      by the object.  In the future, this manual step will
+      be obviated by the upcoming GLib introspection support.
     </para>
+    <para>
+      Here is a sample XML file which describes an object that exposes
+      one method, named <literal>ManyArgs</literal>.
+<programlisting>
+&lt;?xml version="1.0" encoding="UTF-8" ?&gt;
+
+&lt;node name="/com/example/MyObject"&gt;
 
+  &lt;interface name="com.example.MyObject"&gt;
+    &lt;annotation name="org.freedesktop.DBus.GLib.CSymbol" value="my_object"/&gt;
+    &lt;method name="ManyArgs"&gt;
+      &lt;!-- This is optional, and in this case is redunundant --&gt;
+      &lt;annotation name="org.freedesktop.DBus.GLib.CSymbol" value="my_object_many_args"/&gt;
+      &lt;arg type="u" name="x" direction="in" /&gt;
+      &lt;arg type="s" name="str" direction="in" /&gt;
+      &lt;arg type="d" name="trouble" direction="in" /&gt;
+      &lt;arg type="d" name="d_ret" direction="out" /&gt;
+      &lt;arg type="s" name="str_ret" direction="out" /&gt;
+    &lt;/method&gt;
+  &lt;/interface&gt;
+&lt;/node&gt;
+</programlisting>
+    </para>
     <para>
-      The GLib bindings are incomplete.  Implementing an object is not yet
-      possible, see the TODO file and comments in the source code for details
-      on what work needs doing.
+      This XML is in the same format as the D-BUS introspection XML
+      format. Except we must include an "annotation" which give the C
+      symbols corresponding to the object implementation prefix
+      (<literal>my_object</literal>).  In addition, if particular
+      methods symbol names deviate from C convention
+      (i.e. <literal>ManyArgs</literal> -&gt;
+      <literal>many_args</literal>), you may specify an annotation
+      giving the C symbol.
+    </para>
+    <para>
+      Once you have written this XML, run <literal>dbus-binding-tool --mode=glib-server <replaceable>FILENAME</replaceable> &gt; <replaceable>HEADER_NAME</replaceable>.</literal> to
+      generate a header file.  For example: <command>dbus-binding-tool --mode=glib-server my-objet.xml &gt; my-object-glue.h</command>.
+    </para>
+    <para>
+      Next, include the generated header in your program, and invoke
+      <literal>dbus_g_object_class_install_info</literal>, passing the
+      object class and "object info" included in the header.  For
+      example:
+      <programlisting>
+       dbus_g_object_type_install_info (COM_FOO_TYPE_MY_OBJECT, &amp;com_foo_my_object_info);
+      </programlisting>
+      This should be done exactly once per object class.
+    </para>
+    <para>
+      To actually implement the method, just define a C function named e.g.
+      <literal>my_object_many_args</literal> in the same file as the info
+      header is included.  At the moment, it is required that this function
+      conform to the following rules:
+      <itemizedlist>
+       <listitem>
+         <para>
+           The function must return a value of type <literal>gboolean</literal>;
+           <literal>TRUE</literal> on success, and <literal>FALSE</literal>
+           otherwise.
+         </para>
+       </listitem>
+       <listitem>
+         <para>
+           The first parameter is a pointer to an instance of the object.
+         </para>
+       </listitem>
+       <listitem>
+         <para>
+           Following the object instance pointer are the method
+           input values.
+         </para>
+       </listitem>
+       <listitem>
+         <para>
+           Following the input values are pointers to return values.
+         </para>
+       </listitem>
+       <listitem>
+         <para>
+           The final parameter must be a <literal>GError **</literal>.
+           If the function returns <literal>FALSE</literal> for an
+           error, the error parameter must be initalized with
+           <literal>g_set_error</literal>.
+         </para>
+       </listitem>
+      </itemizedlist>
+    </para>
+    <para>
+      Finally, you can export an object using <literal>dbus_g_connection_register_g_object</literal>.  For example:
+      <programlisting>
+         dbus_g_connection_register_g_object (connection,
+                                               "/com/foo/MyObject",
+                                               obj);
+      </programlisting>
     </para>
-      
   </sect1>
 
   <sect1 id="qt-client">
index 6d81e7d..b9b9a70 100644 (file)
@@ -15,8 +15,12 @@ libdbus_glib_1_la_SOURCES =                  \
        dbus-gthread.c                          \
        dbus-gutils.c                           \
        dbus-gutils.h                           \
+       dbus-gtype-specialized.c                \
+       dbus-gtype-specialized.h                \
        dbus-gvalue.c                           \
-       dbus-gvalue.h
+       dbus-gvalue.h                           \
+       dbus-gvalue-utils.c                     \
+       dbus-gvalue-utils.h
 
 libdbus_glib_1_la_LIBADD= $(DBUS_GLIB_LIBS) $(top_builddir)/dbus/libdbus-1.la
 ## don't export symbols that start with "_" (we use this 
index 3c86d81..76536aa 100644 (file)
@@ -26,6 +26,7 @@
 #include "dbus-gparser.h"
 #include "dbus-gutils.h"
 #include "dbus-gvalue.h"
+#include "dbus-gvalue-utils.h"
 #include "dbus-glib-tool.h"
 #include "dbus-binding-tool-glib.h"
 #include <glib/gi18n.h>
@@ -45,12 +46,67 @@ typedef struct
   GError **error;
   
   GHashTable *generated;
+  GString *blob;
+  guint count;
 } DBusBindingToolCData;
 
 static gboolean gather_marshallers (BaseInfo *base, DBusBindingToolCData *data, GError **error);
 static gboolean generate_glue (BaseInfo *base, DBusBindingToolCData *data, GError **error);
 static gboolean generate_client_glue (BaseInfo *base, DBusBindingToolCData *data, GError **error);
 
+static const char *
+dbus_g_type_get_marshal_name (GType gtype)
+{
+  switch (G_TYPE_FUNDAMENTAL (gtype))
+    {
+    case G_TYPE_BOOLEAN:
+      return "BOOLEAN";
+    case G_TYPE_UCHAR:
+      return "UCHAR";
+    case G_TYPE_INT:
+      return "INT";
+    case G_TYPE_UINT:
+      return "UINT";
+    case G_TYPE_INT64:
+      return "INT64";
+    case G_TYPE_UINT64:
+      return "UINT64";
+    case G_TYPE_DOUBLE:
+      return "DOUBLE";
+    case G_TYPE_STRING:
+      return "STRING";
+    case G_TYPE_POINTER:
+      return "POINTER";
+    case G_TYPE_BOXED:
+      return "BOXED";
+    case G_TYPE_OBJECT:
+      return "OBJECT";
+    default:
+      return NULL;
+    }
+}
+
+/* This entire function is kind of...ugh. */
+static const char *
+dbus_g_type_get_c_name (GType gtype)
+{
+  if (dbus_g_type_is_collection (gtype))
+    return "GArray";
+  if (dbus_g_type_is_map (gtype))
+    return "GHashTable";
+  
+  if (g_type_is_a (gtype, G_TYPE_STRING))
+    return "char *";
+
+  /* This one is even more hacky...we get an extra *
+   * because G_TYPE_STRV is a G_TYPE_BOXED
+   */
+  if (g_type_is_a (gtype, G_TYPE_STRV))
+    return "char *";
+  
+  return g_type_name (gtype);
+}
+
 static char *
 compute_marshaller (MethodInfo *method, GError **error)
 {
@@ -71,9 +127,10 @@ compute_marshaller (MethodInfo *method, GError **error)
       if (arg_info_get_direction (arg) == ARG_IN)
        {
          const char *marshal_name;
+         GType gtype;
 
-         marshal_name = dbus_gvalue_genmarshal_name_from_type (arg_info_get_type (arg));
-         if (!marshal_name)
+         gtype = dbus_gtype_from_signature (arg_info_get_type (arg), FALSE);
+         if (gtype == G_TYPE_INVALID)
            {
              g_set_error (error,
                           DBUS_BINDING_TOOL_ERROR,
@@ -83,6 +140,10 @@ compute_marshaller (MethodInfo *method, GError **error)
              g_string_free (ret, TRUE);
              return NULL;
            }
+
+         marshal_name = dbus_g_type_get_marshal_name (gtype);
+         g_assert (marshal_name);
+
          if (!first)
            g_string_append (ret, ",");
          else
@@ -91,6 +152,12 @@ compute_marshaller (MethodInfo *method, GError **error)
        }
     }
 
+  if (method_info_get_annotation (method, DBUS_GLIB_ANNOTATION_ASYNC) != NULL) {
+    if (!first)
+      g_string_append (ret, ",");
+    g_string_append (ret, "POINTER");
+    first = FALSE;
+  } else {
   /* Append pointer for each out arg storage */
   for (elt = method_info_get_args (method); elt; elt = elt->next)
     {
@@ -105,12 +172,13 @@ compute_marshaller (MethodInfo *method, GError **error)
          g_string_append (ret, "POINTER");
        }
     }
-
   /* Final GError parameter */
   if (!first)
     g_string_append (ret, ",");
   g_string_append (ret, "POINTER");
 
+  }
+
   return g_string_free (ret, FALSE);
 
 }
@@ -136,25 +204,31 @@ compute_marshaller_name (MethodInfo *method, const char *prefix, GError **error)
        {
          const char *marshal_name;
          const char *type;
+         GType gtype;
 
          type = arg_info_get_type (arg);
-         marshal_name = dbus_gvalue_genmarshal_name_from_type (type);
-         if (!marshal_name)
+         gtype = dbus_gtype_from_signature (type, FALSE);
+         if (gtype == G_TYPE_INVALID)
            {
              g_set_error (error,
                           DBUS_BINDING_TOOL_ERROR,
                           DBUS_BINDING_TOOL_ERROR_UNSUPPORTED_CONVERSION,
-                          _("Unsupported conversion from D-BUS type %s to glib-genmarshal type"),
+                          _("Unsupported conversion from D-BUS type %s to glib type"),
                           type);
              g_string_free (ret, TRUE);
              return NULL;
            }
+         marshal_name = dbus_g_type_get_marshal_name (gtype);
+         g_assert (marshal_name != NULL);
 
          g_string_append (ret, "_");
-         g_string_append (ret, dbus_gvalue_genmarshal_name_from_type (type));
+         g_string_append (ret, marshal_name);
        }
     }
 
+  if (method_info_get_annotation (method, DBUS_GLIB_ANNOTATION_ASYNC) != NULL) {
+    g_string_append (ret, "_POINTER");
+  } else {
   /* Append pointer for each out arg storage */
   for (elt = method_info_get_args (method); elt; elt = elt->next)
     {
@@ -165,9 +239,9 @@ compute_marshaller_name (MethodInfo *method, const char *prefix, GError **error)
          g_string_append (ret, "_POINTER");
        }
     }
-
   /* Final GError parameter */
   g_string_append (ret, "_POINTER");
+  }
 
   return g_string_free (ret, FALSE);
 }
@@ -210,7 +284,8 @@ gather_marshallers (BaseInfo *base, DBusBindingToolCData *data, GError **error)
       interface_c_name = interface_info_get_annotation (interface, DBUS_GLIB_ANNOTATION_C_SYMBOL);
       if (interface_c_name == NULL)
         {
-          return TRUE;
+         if (!data->prefix)
+           return TRUE;
         }
 
       methods = interface_info_get_methods (interface);
@@ -223,10 +298,6 @@ gather_marshallers (BaseInfo *base, DBusBindingToolCData *data, GError **error)
           char *marshaller_name;
 
           method = (MethodInfo *) tmp->data;
-          if (method_info_get_annotation (method, DBUS_GLIB_ANNOTATION_C_SYMBOL) == NULL)
-            {
-              continue;
-            }
 
           marshaller_name = compute_marshaller (method, error);
          if (!marshaller_name)
@@ -291,12 +362,56 @@ generate_glue (BaseInfo *base, DBusBindingToolCData *data, GError **error)
 {
   if (base_info_get_type (base) == INFO_TYPE_NODE)
     {
+      GString *object_introspection_data_blob;
+      GIOChannel *channel;
+      guint i;
+
+      channel = data->channel;
+      
+      object_introspection_data_blob = g_string_new_len ("", 0);
+      
+      data->blob = object_introspection_data_blob;
+      data->count = 0;
+
+      if (!write_printf_to_iochannel ("static const DBusGMethodInfo dbus_glib_%s_methods[] = {\n", channel, error, data->prefix))
+       goto io_lose;
+
       if (!generate_glue_list (node_info_get_nodes ((NodeInfo *) base),
                               data, error))
        return FALSE;
       if (!generate_glue_list (node_info_get_interfaces ((NodeInfo *) base),
                               data, error))
        return FALSE;
+
+      WRITE_OR_LOSE ("};\n\n");
+
+      /* Information about the object. */
+
+      if (!write_printf_to_iochannel ("const DBusGObjectInfo dbus_glib_%s_object_info = {\n",
+                                     channel, error, data->prefix))
+       goto io_lose;
+      WRITE_OR_LOSE ("  0,\n");
+      if (!write_printf_to_iochannel ("  dbus_glib_%s_methods,\n", channel, error, data->prefix))
+       goto io_lose;
+      if (!write_printf_to_iochannel ("  %d,\n", channel, error, data->count))
+       goto io_lose;
+      WRITE_OR_LOSE("  \"");
+      for (i = 0; i < object_introspection_data_blob->len; i++)
+       {
+         if (object_introspection_data_blob->str[i] != '\0')
+           {
+             if (!g_io_channel_write_chars (channel, object_introspection_data_blob->str + i, 1, NULL, error))
+               return FALSE;
+           }
+         else
+           {
+             if (!g_io_channel_write_chars (channel, "\\0", -1, NULL, error))
+               return FALSE;
+           }
+       }
+      WRITE_OR_LOSE ("\"\n};\n\n");
+
+      g_string_free (object_introspection_data_blob, TRUE);
     }
   else
     {
@@ -304,41 +419,43 @@ generate_glue (BaseInfo *base, DBusBindingToolCData *data, GError **error)
       InterfaceInfo *interface;
       GSList *methods;
       GSList *tmp;
-      gsize i;
-      int count;
       const char *interface_c_name;
       GString *object_introspection_data_blob;
 
       channel = data->channel;
+      object_introspection_data_blob = data->blob;
 
       interface = (InterfaceInfo *) base;
       interface_c_name = interface_info_get_annotation (interface, DBUS_GLIB_ANNOTATION_C_SYMBOL);
       if (interface_c_name == NULL)
         {
-          return TRUE;
+         if (data->prefix == NULL)
+           return TRUE;
+         interface_c_name = data->prefix;
         }
 
-      object_introspection_data_blob = g_string_new_len ("", 0);
-
       methods = interface_info_get_methods (interface);
-      count = 0;
 
       /* Table of marshalled methods. */
 
-      if (!write_printf_to_iochannel ("static const DBusGMethodInfo dbus_glib_%s_methods[] = {\n", channel, error, interface_info_get_annotation (interface, DBUS_GLIB_ANNOTATION_C_SYMBOL)))
-       goto io_lose;
       for (tmp = methods; tmp != NULL; tmp = g_slist_next (tmp))
         {
           MethodInfo *method;
           char *marshaller_name;
-         const char *method_c_name;
+         char *method_c_name;
+          gboolean async = FALSE;
          GSList *args;
 
           method = (MethodInfo *) tmp->data;
-         method_c_name = method_info_get_annotation (method, DBUS_GLIB_ANNOTATION_C_SYMBOL);
+         method_c_name = g_strdup (method_info_get_annotation (method, DBUS_GLIB_ANNOTATION_C_SYMBOL));
           if (method_c_name == NULL)
-            {
-              continue;
+           {
+             char *method_name_uscored;
+             method_name_uscored = _dbus_gutils_wincaps_to_uscore (method_info_get_name (method));
+              method_c_name = g_strdup_printf ("%s_%s",
+                                              interface_c_name,
+                                              method_name_uscored);
+             g_free (method_name_uscored);
             }
 
           if (!write_printf_to_iochannel ("  { (GCallback) %s, ", channel, error,
@@ -357,6 +474,9 @@ generate_glue (BaseInfo *base, DBusBindingToolCData *data, GError **error)
              goto io_lose;
            }
 
+          if (method_info_get_annotation (method, DBUS_GLIB_ANNOTATION_ASYNC) != NULL)
+            async = TRUE;
+
          /* Object method data blob format:
           * <iface>\0<name>\0(<argname>\0<argdirection>\0<argtype>\0)*\0
           */
@@ -367,6 +487,9 @@ generate_glue (BaseInfo *base, DBusBindingToolCData *data, GError **error)
          g_string_append (object_introspection_data_blob, method_info_get_name (method));
          g_string_append_c (object_introspection_data_blob, '\0');
 
+         g_string_append_c (object_introspection_data_blob, async ? 'A' : 'S');
+         g_string_append_c (object_introspection_data_blob, '\0');
+
          for (args = method_info_get_args (method); args; args = args->next)
            {
              ArgInfo *arg;
@@ -400,37 +523,8 @@ generate_glue (BaseInfo *base, DBusBindingToolCData *data, GError **error)
 
          g_string_append_c (object_introspection_data_blob, '\0');
 
-          count++;
+          data->count++;
         }
-      WRITE_OR_LOSE ("};\n\n");
-
-      /* Information about the object. */
-
-      if (!write_printf_to_iochannel ("const DBusGObjectInfo dbus_glib_%s_object_info = {\n",
-                                     channel, error, interface_c_name))
-       goto io_lose;
-      WRITE_OR_LOSE ("  0,\n");
-      if (!write_printf_to_iochannel ("  dbus_glib_%s_methods,\n", channel, error, interface_c_name))
-       goto io_lose;
-      if (!write_printf_to_iochannel ("  %d,\n", channel, error, count))
-       goto io_lose;
-      WRITE_OR_LOSE("  \"");
-      for (i = 0; i < object_introspection_data_blob->len; i++)
-       {
-         if (object_introspection_data_blob->str[i] != '\0')
-           {
-             if (!g_io_channel_write_chars (channel, object_introspection_data_blob->str + i, 1, NULL, error))
-               return FALSE;
-           }
-         else
-           {
-             if (!g_io_channel_write_chars (channel, "\\0", -1, NULL, error))
-               return FALSE;
-           }
-       }
-      WRITE_OR_LOSE ("\"\n};\n\n");
-
-      g_string_free (object_introspection_data_blob, TRUE);
     }
   return TRUE;
  io_lose:
@@ -471,6 +565,8 @@ dbus_binding_tool_output_glib_server (BaseInfo *info, GIOChannel *channel, const
 
   memset (&data, 0, sizeof (data));
 
+  dbus_g_value_types_init ();
+
   data.prefix = prefix;
   data.generated = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, NULL);
   data.error = error;
@@ -607,6 +703,8 @@ write_formal_parameters (InterfaceInfo *iface, MethodInfo *method, GIOChannel *c
     {
       ArgInfo *arg;
       const char *type_str;
+      const char *type_suffix;
+      GType gtype;
       int direction;
 
       arg = args->data;
@@ -615,9 +713,8 @@ write_formal_parameters (InterfaceInfo *iface, MethodInfo *method, GIOChannel *c
 
       direction = arg_info_get_direction (arg);
 
-      type_str = dbus_gvalue_ctype_from_type (arg_info_get_type (arg), direction == ARG_IN);
-
-      if (!type_str)
+      gtype = dbus_gtype_from_signature (arg_info_get_type (arg), TRUE);
+      if (gtype == G_TYPE_INVALID)
        {
          g_set_error (error,
                       DBUS_BINDING_TOOL_ERROR,
@@ -628,18 +725,37 @@ write_formal_parameters (InterfaceInfo *iface, MethodInfo *method, GIOChannel *c
                       interface_info_get_name (iface));
          return FALSE;
        }
+      type_str = dbus_g_type_get_c_name (gtype);
+      g_assert (type_str);
+      /* Variants are special...*/
+      if (gtype == G_TYPE_VALUE)
+       {
+         if (direction == ARG_IN)
+           type_suffix = "*";
+         else
+           type_suffix = "";
+       }
+      else if ((g_type_is_a (gtype, G_TYPE_BOXED)
+             || g_type_is_a (gtype, G_TYPE_OBJECT)
+          || g_type_is_a (gtype, G_TYPE_POINTER)))
+       type_suffix = "*";
+      else
+       type_suffix = "";
+
 
       switch (direction)
        {
        case ARG_IN:
-         if (!write_printf_to_iochannel ("%s IN_%s", channel, error,
+         if (!write_printf_to_iochannel ("const %s%s IN_%s", channel, error,
                                          type_str,
+                                         type_suffix,
                                          arg_info_get_name (arg)))
            goto io_lose;
          break;
        case ARG_OUT:
-         if (!write_printf_to_iochannel ("%s* OUT_%s", channel, error,
+         if (!write_printf_to_iochannel ("%s%s* OUT_%s", channel, error,
                                          type_str,
+                                         type_suffix,
                                          arg_info_get_name (arg)))
            goto io_lose;
          break;
@@ -653,32 +769,71 @@ write_formal_parameters (InterfaceInfo *iface, MethodInfo *method, GIOChannel *c
   return FALSE;
 }
 
-static gboolean
-write_args_sig_for_direction (InterfaceInfo *iface, MethodInfo *method, GIOChannel *channel, int direction, GError **error)
+#define MAP_FUNDAMENTAL(NAME) \
+   case G_TYPE_ ## NAME: \
+     return g_strdup ("G_TYPE_" #NAME);
+#define MAP_KNOWN(NAME) \
+    if (gtype == NAME) \
+      return g_strdup (#NAME)
+static char *
+dbus_g_type_get_lookup_function (GType gtype)
 {
-  GSList *args;
-
-  WRITE_OR_LOSE ("\"");
-
-  for (args = method_info_get_args (method); args; args = args->next)
+  char *type_lookup;
+  switch (gtype)
     {
-      ArgInfo *arg;
-
-      arg = args->data;
-
-      if (direction != arg_info_get_direction (arg))
-       continue;
-
-      if (!write_printf_to_iochannel ("%s", channel, error, arg_info_get_type (arg)))
-       goto io_lose;
+      MAP_FUNDAMENTAL(CHAR);
+      MAP_FUNDAMENTAL(UCHAR);
+      MAP_FUNDAMENTAL(BOOLEAN);
+      MAP_FUNDAMENTAL(LONG);
+      MAP_FUNDAMENTAL(ULONG);
+      MAP_FUNDAMENTAL(INT);
+      MAP_FUNDAMENTAL(UINT);
+      MAP_FUNDAMENTAL(INT64);
+      MAP_FUNDAMENTAL(UINT64);
+      MAP_FUNDAMENTAL(FLOAT);
+      MAP_FUNDAMENTAL(DOUBLE);
+      MAP_FUNDAMENTAL(STRING);
     }
-
-  WRITE_OR_LOSE ("\", ");
-
-  return TRUE;
- io_lose:
-  return FALSE;
+  if (dbus_g_type_is_collection (gtype))
+    {
+      GType elt_gtype;
+      char *sublookup;
+      
+      elt_gtype = dbus_g_type_get_collection_specialization (gtype);
+      sublookup = dbus_g_type_get_lookup_function (elt_gtype);
+      g_assert (sublookup);
+      type_lookup = g_strdup_printf ("dbus_g_type_get_collection (\"GArray\", %s)",
+                                    sublookup);
+      g_free (sublookup);
+      return type_lookup;
+    }
+  else if (dbus_g_type_is_map (gtype))
+    {
+      GType key_gtype;
+      char *key_lookup;
+      GType value_gtype;
+      char *value_lookup;
+      
+      key_gtype = dbus_g_type_get_map_key_specialization (gtype);
+      value_gtype = dbus_g_type_get_map_value_specialization (gtype);
+      key_lookup = dbus_g_type_get_lookup_function (key_gtype);
+      g_assert (key_lookup);
+      value_lookup = dbus_g_type_get_lookup_function (value_gtype);
+      g_assert (value_lookup);
+      type_lookup = g_strdup_printf ("dbus_g_type_get_map (\"GHashTable\", %s, %s)",
+                                    key_lookup, value_lookup);
+      g_free (key_lookup);
+      g_free (value_lookup);
+      return type_lookup;
+    }
+  MAP_KNOWN(G_TYPE_VALUE);
+  MAP_KNOWN(G_TYPE_STRV);
+  MAP_KNOWN(DBUS_TYPE_G_PROXY);
+  MAP_KNOWN(DBUS_TYPE_G_PROXY_ARRAY);
+  return NULL;
 }
+#undef MAP_FUNDAMENTAL
+#undef MAP_KNOWN
 
 static gboolean
 write_args_for_direction (InterfaceInfo *iface, MethodInfo *method, GIOChannel *channel, int direction, GError **error)
@@ -688,25 +843,38 @@ write_args_for_direction (InterfaceInfo *iface, MethodInfo *method, GIOChannel *
   for (args = method_info_get_args (method); args; args = args->next)
     {
       ArgInfo *arg;
+      GType gtype;
+      char *type_lookup;
 
       arg = args->data;
 
       if (direction != arg_info_get_direction (arg))
        continue;
 
+      gtype = dbus_gtype_from_signature (arg_info_get_type (arg), TRUE);
+      g_assert (gtype != G_TYPE_INVALID);
+      type_lookup = dbus_g_type_get_lookup_function (gtype);
+      g_assert (type_lookup != NULL);
+
       switch (direction)
        {
+
        case ARG_IN:
-         if (!write_printf_to_iochannel ("IN_%s, ", channel, error, arg_info_get_name (arg)))
+         if (!write_printf_to_iochannel ("%s, IN_%s, ", channel, error,
+                                         type_lookup,
+                                         arg_info_get_name (arg)))
            goto io_lose;
          break;
        case ARG_OUT:
-         if (!write_printf_to_iochannel ("OUT_%s, ", channel, error, arg_info_get_name (arg)))
+         if (!write_printf_to_iochannel ("%s, OUT_%s, ", channel, error,
+                                         type_lookup,
+                                         arg_info_get_name (arg)))
            goto io_lose;
          break;
        case ARG_INVALID:
          break;
        }
+      g_free (type_lookup);
     }
 
   return TRUE;
@@ -722,15 +890,303 @@ check_supported_parameters (MethodInfo *method)
   for (args = method_info_get_args (method); args; args = args->next)
     {
       ArgInfo *arg;
+      GType gtype;
+
       arg = args->data;
-      if (!dbus_gvalue_ctype_from_type (arg_info_get_type (arg),
-                                       arg_info_get_direction (arg)))
+      gtype = dbus_gtype_from_signature (arg_info_get_type (arg), TRUE);
+      if (gtype == G_TYPE_INVALID)
        return FALSE;
     }
   return TRUE;
 }
 
 static gboolean
+write_untyped_out_args (InterfaceInfo *iface, MethodInfo *method, GIOChannel *channel, GError **error)
+{
+  GSList *args;
+
+  for (args = method_info_get_args (method); args; args = args->next)
+    {
+      ArgInfo *arg;
+
+      arg = args->data;
+      if (arg_info_get_direction (arg) != ARG_OUT)
+        continue;
+            
+      if (!write_printf_to_iochannel ("OUT_%s, ", channel, error,
+                                      arg_info_get_name (arg)))
+        goto io_lose;
+     }
+
+   return TRUE;
+ io_lose:
+  return FALSE;
+}
+
+static gboolean
+write_formal_declarations_for_direction (InterfaceInfo *iface, MethodInfo *method, GIOChannel *channel, const int direction, GError **error)
+ {
+   GSList *args;
+   for (args = method_info_get_args (method); args; args = args->next)
+     {
+       ArgInfo *arg;
+      GType gtype;
+      const char *type_str, *type_suffix;
+      int dir;
+
+       arg = args->data;
+
+      dir = arg_info_get_direction (arg);
+
+      gtype = dbus_gtype_from_signature (arg_info_get_type (arg), TRUE);
+      type_str = dbus_g_type_get_c_name (gtype);
+
+      if (!type_str)
+       {
+         g_set_error (error,
+                      DBUS_BINDING_TOOL_ERROR,
+                      DBUS_BINDING_TOOL_ERROR_UNSUPPORTED_CONVERSION,
+                      _("Unsupported conversion from D-BUS type signature \"%s\" to glib C type in method \"%s\" of interface \"%s\""),
+                      arg_info_get_type (arg),
+                      method_info_get_name (method),
+                      interface_info_get_name (iface));
+         return FALSE;
+       }
+
+      /* Variants are special...*/
+      if (gtype == G_TYPE_VALUE)
+       {
+         if (direction == ARG_IN)
+           type_suffix = "*";
+         else
+           type_suffix = "";
+       }
+      else if ((g_type_is_a (gtype, G_TYPE_BOXED)
+             || g_type_is_a (gtype, G_TYPE_OBJECT)
+          || g_type_is_a (gtype, G_TYPE_POINTER)))
+       type_suffix = "*";
+      else
+       type_suffix = "";
+
+      if (direction != dir)
+        continue;
+
+          switch (dir)
+       {
+       case ARG_IN:
+         if (!write_printf_to_iochannel ("  %s%s IN_%s;\n", channel, error,
+                                         type_str, type_suffix,
+                                         arg_info_get_name (arg)))
+           goto io_lose;
+         break;
+       case ARG_OUT:
+         if (!write_printf_to_iochannel ("  %s%s OUT_%s;\n", channel, error,
+                                         type_str, type_suffix,
+                                         arg_info_get_name (arg)))
+           goto io_lose;
+         break;
+       case ARG_INVALID:
+         break;
+       }
+     }
+   return TRUE;
+ io_lose:
+  return FALSE;
+ }
+
+static gboolean
+write_formal_parameters_for_direction (InterfaceInfo *iface, MethodInfo *method, int dir, GIOChannel *channel, GError **error)
+{
+  GSList *args;
+
+  for (args = method_info_get_args (method); args; args = args->next)
+    {
+      ArgInfo *arg;
+      const char *type_str;
+      const char *type_suffix;
+      GType gtype;
+      int direction;
+
+      arg = args->data;
+
+      direction = arg_info_get_direction (arg);
+      if (dir != direction) continue;
+      
+      WRITE_OR_LOSE (", ");
+
+      gtype = dbus_gtype_from_signature (arg_info_get_type (arg), TRUE);
+      type_str = dbus_g_type_get_c_name (gtype);
+      /* Variants are special...*/
+      if (gtype == G_TYPE_VALUE)
+       {
+         if (direction == ARG_IN)
+           type_suffix = "*";
+         else
+           type_suffix = "";
+       }
+      else if ((g_type_is_a (gtype, G_TYPE_BOXED)
+             || g_type_is_a (gtype, G_TYPE_OBJECT)
+          || g_type_is_a (gtype, G_TYPE_POINTER)))
+       type_suffix = "*";
+      else
+       type_suffix = "";
+
+      if (!type_str)
+       {
+         g_set_error (error,
+                      DBUS_BINDING_TOOL_ERROR,
+                      DBUS_BINDING_TOOL_ERROR_UNSUPPORTED_CONVERSION,
+                      _("Unsupported conversion from D-BUS type signature \"%s\" to glib C type in method \"%s\" of interface \"%s\""),
+                      arg_info_get_type (arg),
+                      method_info_get_name (method),
+                      interface_info_get_name (iface));
+         return FALSE;
+       }
+       switch (direction)
+       {
+       case ARG_IN:
+         if (!write_printf_to_iochannel ("const %s%s IN_%s", channel, error,
+                                         type_str,
+                                         type_suffix,
+                                         arg_info_get_name (arg)))
+           goto io_lose;
+         break;
+       case ARG_OUT:
+         if (!write_printf_to_iochannel ("%s%s* OUT_%s", channel, error,
+                                         type_str,
+                                         type_suffix,
+                                         arg_info_get_name (arg)))
+           goto io_lose;
+         break;
+       case ARG_INVALID:
+         break;
+       }
+    }
+  return TRUE;
+ io_lose:
+  return FALSE;
+}
+
+static gboolean
+write_typed_args_for_direction (InterfaceInfo *iface, MethodInfo *method, GIOChannel *channel, const int direction, GError **error)
+ {
+  GSList *args;
+  
+  for (args = method_info_get_args (method); args; args = args->next)
+    {
+      ArgInfo *arg;
+      int dir;
+      GType gtype;
+      const char *type_lookup;
+      
+      arg = args->data;
+
+      dir = arg_info_get_direction (arg);
+
+      if (dir != direction)
+        continue;
+
+      gtype = dbus_gtype_from_signature (arg_info_get_type (arg), TRUE);
+      type_lookup = dbus_g_type_get_lookup_function (gtype);
+
+      if (!write_printf_to_iochannel ("%s, &%s_%s, ", channel, error, type_lookup, direction == ARG_IN ? "IN" : "OUT", arg_info_get_name (arg)))
+          goto io_lose;
+    }
+  return TRUE;
+ io_lose:
+  return FALSE;
+}
+
+static gboolean
+write_async_method_client (GIOChannel *channel, InterfaceInfo *interface, MethodInfo *method, GError **error)
+{
+  char *method_name, *iface_prefix;
+  iface_prefix = iface_to_c_prefix (interface_info_get_name (interface));
+  method_name = compute_client_method_name (iface_prefix, method);
+  
+  /* Write the typedef for the client callback */
+  if (!write_printf_to_iochannel ("typedef void (*%s_reply) (", channel, error, method_name))
+    goto io_lose;
+  {
+    GSList *args;
+    for (args = method_info_get_args (method); args; args = args->next)
+      {
+       ArgInfo *arg;
+       const char *type_suffix, *type_str;
+       GType gtype;
+       
+       arg = args->data;
+       
+       if (arg_info_get_direction (arg) != ARG_OUT)
+         continue;
+       gtype = dbus_gtype_from_signature (arg_info_get_type (arg), TRUE);
+       if (gtype != G_TYPE_VALUE && (g_type_is_a (gtype, G_TYPE_BOXED)
+            || g_type_is_a (gtype, G_TYPE_OBJECT)
+            || g_type_is_a (gtype, G_TYPE_POINTER)))
+         type_suffix = "*";
+       else
+         type_suffix = "";
+       type_str = dbus_g_type_get_c_name (dbus_gtype_from_signature (arg_info_get_type (arg), TRUE));
+       if (!write_printf_to_iochannel ("%s %sOUT_%s, ", channel, error, type_str, type_suffix, arg_info_get_name (arg)))
+         goto io_lose;
+      }
+  }
+  WRITE_OR_LOSE ("GError *error, gpointer userdata);\n\n");
+  
+  
+  /* Write the callback when the call returns */
+  WRITE_OR_LOSE ("static void\n");
+  if (!write_printf_to_iochannel ("%s_async_callback (DBusGPendingCall *pending, DBusGAsyncData *data)\n", channel, error, method_name))
+    goto io_lose;
+  WRITE_OR_LOSE ("{\n");
+  WRITE_OR_LOSE ("  GError *error = NULL;\n");
+  if (!write_formal_declarations_for_direction (interface, method, channel, ARG_OUT, error))
+    goto io_lose;
+  WRITE_OR_LOSE ("  dbus_g_proxy_end_call (data->proxy, pending, &error, ");
+  if (!write_typed_args_for_direction (interface, method, channel, ARG_OUT, error))
+    goto io_lose;
+  WRITE_OR_LOSE("G_TYPE_INVALID);\n");
+  if (!write_printf_to_iochannel ("  (*(%s_reply)data->cb) (", channel, error, method_name))
+    goto io_lose;
+  if (!write_untyped_out_args (interface, method, channel, error))
+    goto io_lose;
+  WRITE_OR_LOSE ("error, data->userdata);\n");
+  WRITE_OR_LOSE ("  return;\n}\n\n");
+  
+
+  /* Write the main wrapper function */
+  WRITE_OR_LOSE ("static\n#ifdef G_HAVE_INLINE\ninline\n#endif\ngboolean\n");
+  if (!write_printf_to_iochannel ("%s_async (DBusGProxy *proxy", channel, error,
+                                  method_name))
+    goto io_lose;
+  if (!write_formal_parameters_for_direction (interface, method, ARG_IN, channel, error))
+    goto io_lose;
+  
+  if (!write_printf_to_iochannel (", %s_reply callback, gpointer userdata)\n\n", channel, error, method_name))
+    goto io_lose;
+  
+  WRITE_OR_LOSE ("{\n");
+  WRITE_OR_LOSE ("  DBusGPendingCall *pending;\n  DBusGAsyncData *stuff;\n  stuff = g_new (DBusGAsyncData, 1);\n  stuff->proxy = proxy;\n  stuff->cb = callback;\n  stuff->userdata = userdata;\n");
+  if (!write_printf_to_iochannel ("  pending = dbus_g_proxy_begin_call (proxy, \"%s\", ", channel, error, method_info_get_name (method)))
+    goto io_lose;
+  if (!write_args_for_direction (interface, method, channel, ARG_IN, error))
+    goto io_lose;
+  WRITE_OR_LOSE ("G_TYPE_INVALID);\n");
+
+  if (!write_printf_to_iochannel ("  dbus_g_pending_call_set_notify(pending, (DBusGPendingCallNotify)%s_async_callback, stuff, g_free);\n", channel, error, method_name))
+    goto io_lose;
+
+  WRITE_OR_LOSE ("  return TRUE;\n}\n\n");
+
+  g_free (method_name);
+  return TRUE;
+ io_lose:
+  return FALSE;
+ }
+
+static gboolean
 generate_client_glue_list (GSList *list, DBusBindingToolCData *data, GError **error)
 {
   GSList *tmp;
@@ -818,21 +1274,19 @@ generate_client_glue (BaseInfo *base, DBusBindingToolCData *data, GError **error
                                          method_info_get_name (method)))
            goto io_lose;
 
-         if (!write_args_sig_for_direction (interface, method, channel, ARG_IN, error))
-           goto io_lose;
-
-         if (!write_args_sig_for_direction (interface, method, channel, ARG_OUT, error))
-           goto io_lose;
-
          WRITE_OR_LOSE ("error, ");
 
          if (!write_args_for_direction (interface, method, channel, ARG_IN, error))
            goto io_lose;
 
+         WRITE_OR_LOSE ("G_TYPE_INVALID, ");
+
          if (!write_args_for_direction (interface, method, channel, ARG_OUT, error))
            goto io_lose;
 
-         WRITE_OR_LOSE ("NULL);\n}\n\n");
+         WRITE_OR_LOSE ("G_TYPE_INVALID);\n}\n\n");
+
+         write_async_method_client (channel, interface, method, error);
        }
 
       if (!write_printf_to_iochannel ("#endif /* defined DBUS_GLIB_CLIENT_WRAPPERS_%s */\n\n", channel, error, iface_prefix))
@@ -853,6 +1307,8 @@ dbus_binding_tool_output_glib_client (BaseInfo *info, GIOChannel *channel, gbool
   DBusBindingToolCData data;
   gboolean ret;
 
+  dbus_g_value_types_init ();
+
   memset (&data, 0, sizeof (data));
   
   data.channel = channel;
index 9988df2..257b534 100644 (file)
@@ -26,6 +26,7 @@
 G_BEGIN_DECLS
 
 #define DBUS_GLIB_ANNOTATION_C_SYMBOL "org.freedesktop.DBus.GLib.CSymbol"
+#define DBUS_GLIB_ANNOTATION_ASYNC "org.freedesktop.DBus.GLib.Async"
 
 gboolean dbus_binding_tool_output_glib_client (BaseInfo *info, GIOChannel *channel, gboolean ignore_unsupported, GError **error);
 gboolean dbus_binding_tool_output_glib_server (BaseInfo *info, GIOChannel *channel, const char *prefix, GError **error);
index 046c493..08f8aef 100644 (file)
@@ -27,6 +27,9 @@
 #include <dbus/dbus-glib-lowlevel.h>
 #include "dbus-gtest.h"
 #include "dbus-gutils.h"
+#include "dbus-gvalue.h"
+#include "dbus-gvalue-utils.h"
+#include <string.h>
 
 #include <libintl.h>
 #define _(x) dgettext (GETTEXT_PACKAGE, x)
@@ -709,6 +712,8 @@ dbus_g_bus_get (DBusBusType     type,
   DBusError derror;
 
   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+  dbus_g_value_types_init ();
   
   dbus_error_init (&derror);
 
@@ -738,6 +743,30 @@ dbus_g_bus_get (DBusBusType     type,
 gboolean
 _dbus_gmain_test (const char *test_data_dir)
 {
+  GType rectype;
+  GType gtype;
+
+  g_type_init ();
+  dbus_g_value_types_init ();
+
+  rectype = dbus_g_type_get_collection ("GArray", G_TYPE_UINT);
+  g_assert (rectype != G_TYPE_INVALID);
+  g_assert (!strcmp (g_type_name (rectype), "GArray+guint"));
+
+  gtype = dbus_gtype_from_signature ("au", TRUE);
+  g_assert (gtype == rectype);
+
+  rectype = dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_STRING);
+  g_assert (rectype != G_TYPE_INVALID);
+  g_assert (!strcmp (g_type_name (rectype), "GHashTable+gchararray+gchararray"));
+
+  gtype = dbus_gtype_from_signature ("a{ss}", TRUE);
+  g_assert (gtype == rectype);
+
+  gtype = dbus_gtype_from_signature ("o", FALSE);
+  g_assert (gtype == G_TYPE_OBJECT);
+  gtype = dbus_gtype_from_signature ("o", TRUE);
+  g_assert (gtype == DBUS_TYPE_G_PROXY);
   
   return TRUE;
 }
index 09fc97f..e2645f4 100644 (file)
  */
 
 #include <config.h>
+#include <gobject/gvaluecollector.h>
 #include <dbus/dbus-glib.h>
 #include <dbus/dbus-glib-lowlevel.h>
 #include "dbus-gtest.h"
 #include "dbus-gutils.h"
 #include "dbus-gobject.h"
 #include "dbus-gvalue.h"
+#include "dbus-gmarshal.h"
+#include "dbus-gvalue-utils.h"
 #include <string.h>
 
 /**
@@ -35,8 +38,9 @@
  * @{
  */
 
-static GStaticRWLock info_hash_lock = G_STATIC_RW_LOCK_INIT;
+static GStaticRWLock globals_lock = G_STATIC_RW_LOCK_INIT;
 static GHashTable *info_hash = NULL;
+static GHashTable *marshal_table = NULL;
 
 static char*
 uscore_to_wincaps (const char *uscore)
@@ -129,7 +133,7 @@ static const char *
 method_arg_info_from_object_info (const DBusGObjectInfo *object,
                                  const DBusGMethodInfo *method)
 {
-  return string_table_lookup (get_method_data (object, method), 2);
+  return string_table_lookup (get_method_data (object, method), 3);/*RB was 2*/
 }
 
 static const char *
@@ -199,42 +203,6 @@ method_output_signature_from_object_info (const DBusGObjectInfo *object,
   return method_dir_signature_from_object_info (object, method, FALSE);
 }
 
-/**
- * Converts the args of a message into an array of GValue.
- *
- * @param message the message
- * @returns #NULL if conversion fails, otherwise the values.
- */
-GValueArray *
-_dbus_glib_marshal_dbus_message_to_gvalue_array (DBusMessage *message)
-{
-  GValueArray *ret;
-  DBusMessageIter iter;
-  int dtype;
-
-  ret = g_value_array_new (6);  /* 6 is a typical maximum for arguments */
-  dbus_message_iter_init (message, &iter);
-  
-  while ((dtype = dbus_message_iter_get_arg_type (&iter)) != DBUS_TYPE_INVALID)
-    {
-      GValue value = { 0, };
-
-      if (!dbus_gvalue_demarshal (&iter, &value))
-        {
-          g_warning ("Unable to convert arg type %d to GValue", dtype);
-          g_value_array_free (ret);
-          ret = NULL;
-          goto out;
-        }
-      g_value_array_append (ret, &value);
-      
-      dbus_message_iter_next (&iter);
-    }
-
- out:
-  return ret;
-}
-
 static void
 gobject_unregister_function (DBusConnection  *connection,
                              void            *user_data)
@@ -267,7 +235,7 @@ introspect_properties (GObject *object, GString *xml)
       gboolean can_get;
       GParamSpec *spec = specs[i];
       
-      dbus_type = dbus_gtype_to_dbus_type (G_PARAM_SPEC_VALUE_TYPE (spec));
+      dbus_type = dbus_gtype_to_signature (G_PARAM_SPEC_VALUE_TYPE (spec));
       if (dbus_type == NULL)
        continue;
       
@@ -347,7 +315,7 @@ introspect_signals (GType type, GString *xml)
       
       g_signal_query (ids[i], &query);
 
-      if (query.return_type)
+      if (query.return_type != G_TYPE_NONE)
        continue; /* FIXME: these could be listed as methods ? */
 
       g_string_append (xml, "    <signal name=\"");
@@ -356,8 +324,11 @@ introspect_signals (GType type, GString *xml)
 
       for (arg = 0; arg < query.n_params; arg++)
        {
-         const char *dbus_type = dbus_gtype_to_dbus_type (query.param_types[arg]);
+         const char *dbus_type = dbus_gtype_to_signature (query.param_types[arg]);
 
+         if (!dbus_type)
+           continue;
+         
           g_string_append (xml, "      <arg type=\"");
           g_string_append (xml, dbus_type);
           g_string_append (xml, "\"/>\n");
@@ -428,7 +399,7 @@ introspect_interfaces (GObject *object, GString *xml)
 {
   GType classtype;
 
-  g_static_rw_lock_reader_lock (&info_hash_lock);
+  g_static_rw_lock_reader_lock (&globals_lock);
 
   for (classtype = G_TYPE_FROM_INSTANCE (object); classtype != 0; classtype = g_type_parent (classtype))
     {
@@ -475,7 +446,7 @@ introspect_interfaces (GObject *object, GString *xml)
        }
     }
 
-  g_static_rw_lock_reader_lock (&info_hash_lock);
+  g_static_rw_lock_reader_unlock (&globals_lock);
 }
 
 static DBusHandlerResult
@@ -560,15 +531,15 @@ set_object_property (DBusConnection  *connection,
   GValue value = { 0, };
   DBusMessage *ret;
   DBusMessageIter sub;
+  DBusGValueMarshalCtx context;
 
   dbus_message_iter_recurse (iter, &sub);
-  
-  /* The g_object_set_property() will transform some types, e.g. it
-   * will let you use a uchar to set an int property etc. Note that
-   * any error in value range or value conversion will just
-   * g_warning(). These GObject skels are not for secure applications.
-   */
-  if (dbus_gvalue_demarshal (&sub, &value))
+
+  context.gconnection = DBUS_G_CONNECTION_FROM_CONNECTION (connection);
+  context.proxy = NULL;
+
+  g_value_init (&value, pspec->value_type);
+  if (dbus_gvalue_demarshal (&context, &sub, &value, NULL))
     {
       g_object_set_property (object,
                              pspec->name,
@@ -644,7 +615,7 @@ lookup_object_and_method (GObject      *object,
   signature = dbus_message_get_signature (message);
   ret = FALSE;
 
-  g_static_rw_lock_reader_lock (&info_hash_lock);
+  g_static_rw_lock_reader_lock (&globals_lock);
 
   if (!info_hash)
     goto out;
@@ -674,6 +645,7 @@ lookup_object_and_method (GObject      *object,
              expected_interface = method_interface_from_object_info (*object_ret, method);
              expected_member = method_name_from_object_info (*object_ret, method);
              expected_signature = method_input_signature_from_object_info (*object_ret, method);
+
              if ((interface == NULL
                   || strcmp (expected_interface, interface) == 0)
                  && strcmp (expected_member, member) == 0
@@ -689,7 +661,7 @@ lookup_object_and_method (GObject      *object,
        }
     }
  out:
-  g_static_rw_lock_reader_lock (&info_hash_lock);
+  g_static_rw_lock_reader_unlock (&globals_lock);
   return ret;
 }
 
@@ -759,74 +731,147 @@ invoke_object_method (GObject         *object,
                      DBusConnection  *connection,
                      DBusMessage     *message)
 {
-  gboolean had_error;
+  gboolean had_error, call_only;
   GError *gerror;
   GValueArray *value_array;
   GValue object_value = {0,};
   GValue error_value = {0,};
   GValue return_value = {0,};
   GClosure closure;
-  char *out_signature;
-  int out_signature_len;
-  GArray *out_param_values;
-  int i;
+  char *in_signature;
+  char *out_signature = NULL;
+  int current_type;
+  DBusSignatureIter out_signature_iter;
+  GArray *out_param_values = NULL;
+  GValueArray *out_param_gvalues = NULL;
+  int out_param_count;
+  int out_param_pos, out_param_gvalue_pos;
   DBusHandlerResult result;
   DBusMessage *reply;
 
   gerror = NULL;
 
+  if (strcmp (string_table_lookup (get_method_data (object_info, method), 2), "A") == 0) {
+    call_only = TRUE;
+  } else {
+    call_only = FALSE;
+  }
+
   /* This is evil.  We do this to work around the fact that
    * the generated glib marshallers check a flag in the closure object
    * which we don't care about.  We don't need/want to create
    * a new closure for each invocation.
    */
   memset (&closure, 0, sizeof (closure));
+
+  in_signature = method_input_signature_from_object_info (object_info, method); 
   
   /* Convert method IN parameters to GValueArray */
-  value_array = _dbus_glib_marshal_dbus_message_to_gvalue_array (message);
-
-  g_return_val_if_fail (value_array != NULL, DBUS_HANDLER_RESULT_NOT_YET_HANDLED);
+  {
+    GArray *types_array;
+    guint n_params;
+    const GType *types;
+    DBusGValueMarshalCtx context;
+
+    context.gconnection = DBUS_G_CONNECTION_FROM_CONNECTION (connection);
+    context.proxy = NULL;
+
+    types_array = dbus_gtypes_from_arg_signature (in_signature, FALSE);
+    n_params = types_array->len;
+    types = (const GType*) types_array->data;
+
+    value_array = dbus_gvalue_demarshal_message (&context, message, n_params, types, NULL);
+    if (value_array == NULL)
+      {
+       g_free (in_signature); 
+       g_array_free (types_array, TRUE);
+       return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+      }
+    g_array_free (types_array, TRUE);
+  }
 
   /* Prepend object as first argument */ 
   g_value_init (&object_value, G_TYPE_OBJECT);
   g_value_set_object (&object_value, object);
   g_value_array_prepend (value_array, &object_value);
 
+  if (call_only) {
+    GValue context_value = {0,};
+    DBusGMethodInvocation *context;
+    context = g_new (DBusGMethodInvocation, 1);
+    context->connection = dbus_g_connection_ref (DBUS_G_CONNECTION_FROM_CONNECTION (connection));
+    context->message = dbus_g_message_ref (DBUS_G_MESSAGE_FROM_MESSAGE (message));
+    context->object = object_info;
+    context->method = method;
+    g_value_init (&context_value, G_TYPE_POINTER);
+    g_value_set_pointer (&context_value, context);
+    g_value_array_append (value_array, &context_value);
+  } else {
   out_signature = method_output_signature_from_object_info (object_info, method); 
-  out_signature_len = strlen (out_signature);
+
+  /* Count number of output parameters */
+  dbus_signature_iter_init (&out_signature_iter, out_signature);
+  out_param_count = 0;
+  while ((current_type = dbus_signature_iter_get_current_type (&out_signature_iter)) != DBUS_TYPE_INVALID)
+    {
+      out_param_count++;
+      dbus_signature_iter_next (&out_signature_iter);
+    }
 
   /* Create an array to store the actual values of OUT
    * parameters.  Then, create a GValue boxed POINTER
    * to each of those values, and append to the invocation,
    * so the method can return the OUT parameters.
    */
-  out_param_values = g_array_new (FALSE, TRUE, sizeof (DBusBasicGValue));
-  for (i = 0; i < out_signature_len; i++)
+  out_param_values = g_array_sized_new (FALSE, TRUE, sizeof (GTypeCValue), out_param_count);
+
+  /* We have a special array of GValues for toplevel GValue return
+   * types.
+   */
+  out_param_gvalues = g_value_array_new (out_param_count);
+  out_param_pos = 0;
+  out_param_gvalue_pos = 0;
+  dbus_signature_iter_init (&out_signature_iter, out_signature);
+  while ((current_type = dbus_signature_iter_get_current_type (&out_signature_iter)) != DBUS_TYPE_INVALID)
     {
       GValue value = {0, };
-      DBusBasicGValue basic;
+      GTypeCValue storage;
 
-      memset (&basic, 0, sizeof (basic));
-
-      /* FIXME - broken for container types */
-
-      g_array_append_val (out_param_values, basic);
       g_value_init (&value, G_TYPE_POINTER);
-      g_value_set_pointer (&value, &(g_array_index (out_param_values, DBusBasicGValue, i)));
+
+      /* We special case variants to make method invocation a bit nicer */
+      if (current_type != DBUS_TYPE_VARIANT)
+       {
+         memset (&storage, 0, sizeof (storage));
+         g_array_append_val (out_param_values, storage);
+         g_value_set_pointer (&value, &(g_array_index (out_param_values, GTypeCValue, out_param_pos)));
+         out_param_pos++;
+       }
+      else
+       {
+         g_value_array_append (out_param_gvalues, NULL);
+         g_value_set_pointer (&value, out_param_gvalues->values + out_param_gvalue_pos);
+         out_param_gvalue_pos++;
+       }
       g_value_array_append (value_array, &value);
+      dbus_signature_iter_next (&out_signature_iter);
     }
 
   /* Append GError as final argument */
   g_value_init (&error_value, G_TYPE_POINTER);
   g_value_set_pointer (&error_value, &gerror);
   g_value_array_append (value_array, &error_value);
-
+  }
   /* Actually invoke method */
   g_value_init (&return_value, G_TYPE_BOOLEAN);
   method->marshaller (&closure, &return_value,
                      value_array->n_values,
                      value_array->values,
                      NULL, method->function);
+  if (call_only) {
+    result = DBUS_HANDLER_RESULT_HANDLED;
+    goto done;
+  }
   had_error = !g_value_get_boolean (&return_value);
 
   if (!had_error)
@@ -839,16 +884,34 @@ invoke_object_method (GObject         *object,
 
       /* Append OUT arguments to reply */
       dbus_message_iter_init_append (reply, &iter);
-      for (i = 0; i < out_signature_len; i++)
+      dbus_signature_iter_init (&out_signature_iter, out_signature);
+      out_param_pos = 0;
+      out_param_gvalue_pos = 0;
+      while ((current_type = dbus_signature_iter_get_current_type (&out_signature_iter)) != DBUS_TYPE_INVALID)
        {
-         DBusBasicGValue *value;
-
-         /* FIXME - broken for container types */
-
-         value = &(g_array_index (out_param_values, DBusBasicGValue, i));
-         if (!dbus_message_iter_append_basic (&iter, out_signature[i], value))
-           goto nomem;
+         GValue gvalue = {0, };
          
+         g_value_init (&gvalue, dbus_gtype_from_signature_iter (&out_signature_iter, FALSE));
+         if (current_type != DBUS_TYPE_VARIANT)
+           {
+             if (!dbus_gvalue_take (&gvalue,
+                                    &(g_array_index (out_param_values, GTypeCValue, out_param_pos))))
+               g_assert_not_reached ();
+             out_param_pos++;
+           }
+         else
+           {
+             g_value_set_static_boxed (&gvalue, out_param_gvalues->values + out_param_gvalue_pos);
+             out_param_gvalue_pos++;
+           }
+             
+         if (!dbus_gvalue_marshal (&iter, &gvalue))
+           goto nomem;
+         /* Here we actually free the allocated value; we
+          * took ownership of it with dbus_gvalue_take.
+          */
+         g_value_unset (&gvalue);
+         dbus_signature_iter_next (&out_signature_iter);
        }
     }
   else
@@ -860,30 +923,17 @@ invoke_object_method (GObject         *object,
       dbus_message_unref (reply);
     }
 
-  /* Assume that if there was an error, no return values are
-   * set */
-  if (!had_error)
-    {
-      /* Be sure to free all returned STRING arguments for now;
-       * later this should be specified via method info parameter
-       * annotation; probably we want to support custom free funcs too */
-      for (i = 0; i < out_signature_len; i++)
-       {
-         DBusBasicGValue *value;
-
-         value = &(g_array_index (out_param_values, DBusBasicGValue, i));
-         if (out_signature[i] == DBUS_TYPE_STRING)
-           g_free (value->gpointer_val);
-       }
-    } 
-
   result = DBUS_HANDLER_RESULT_HANDLED;
  done:
+  g_free (in_signature);
   g_free (out_signature);
-  g_array_free (out_param_values, TRUE);
+  if (!call_only) {
+    g_array_free (out_param_values, TRUE);
+    g_value_array_free (out_param_gvalues);
+    g_value_unset (&object_value);
+    g_value_unset (&error_value);
+  }
   g_value_array_free (value_array);
-  g_value_unset (&object_value);
-  g_value_unset (&error_value);
   g_value_unset (&return_value);
   return result;
  nomem:
@@ -891,6 +941,55 @@ invoke_object_method (GObject         *object,
   goto done;
 }
 
+void
+dbus_g_method_return (DBusGMethodInvocation *context, ...)
+{
+  DBusMessage *reply;
+  DBusMessageIter iter;
+  va_list args;
+  char *out_sig;
+  GArray *argsig;
+  guint i;
+
+  reply = dbus_message_new_method_return (dbus_g_message_get_message (context->message));
+  out_sig = method_output_signature_from_object_info (context->object, context->method);
+  argsig = dbus_gtypes_from_arg_signature (out_sig, FALSE);
+
+  dbus_message_iter_init_append (reply, &iter);
+
+  va_start (args, context);
+  for (i = 0; i < argsig->len; i++) {
+    GValue value = {0,};
+    char *error;
+    g_value_init (&value, g_array_index (argsig, GType, i));
+    error = NULL;
+    G_VALUE_COLLECT (&value, args, 0, &error);
+    if (error) {
+      g_warning(error);
+      g_free (error);
+    }
+    dbus_gvalue_marshal (&iter, &value);
+  }
+  va_end (args);
+
+  dbus_connection_send (dbus_g_connection_get_connection (context->connection), reply, NULL);
+  dbus_message_unref (reply);
+
+  dbus_g_connection_unref (context->connection);
+  dbus_g_message_unref (context->message);
+  g_free (context);
+}
+
+void
+dbus_g_method_return_error (DBusGMethodInvocation *context, GError *error)
+{
+  DBusMessage *reply;
+  reply = gerror_to_dbus_error_message (context->object, dbus_g_message_get_message (context->message), error);
+  dbus_connection_send (dbus_g_connection_get_connection (context->connection), reply, NULL);
+  dbus_message_unref (reply);
+}
+
+
 static DBusHandlerResult
 gobject_message_function (DBusConnection  *connection,
                           DBusMessage     *message,
@@ -1009,6 +1108,125 @@ static DBusObjectPathVTable gobject_dbus_vtable = {
   NULL
 };
 
+typedef struct {
+  GClosure         closure;
+  DBusGConnection *connection;
+  GObject         *object;
+  char            *signame;
+} DBusGSignalClosure;
+
+static GClosure *
+dbus_g_signal_closure_new (DBusGConnection *connection,
+                          GObject         *object,
+                          const char      *signame)
+{
+  DBusGSignalClosure *closure;
+  
+  closure = (DBusGSignalClosure*) g_closure_new_simple (sizeof (DBusGSignalClosure), NULL);
+
+  closure->connection = dbus_g_connection_ref (connection);
+  closure->object = object;
+  closure->signame = g_strdup (signame);
+  return (GClosure*) closure;
+}
+
+static void
+dbus_g_signal_closure_finalize (gpointer data,
+                               GClosure *closure)
+{
+  DBusGSignalClosure *sigclosure = (DBusGSignalClosure *) closure;
+
+  dbus_g_connection_unref (sigclosure->connection);
+  g_free (sigclosure->signame);
+}
+
+static void
+signal_emitter_marshaller (GClosure        *closure,
+                          GValue          *retval,
+                          guint            n_param_values,
+                          const GValue    *param_values,
+                          gpointer         invocation_hint,
+                          gpointer         marshal_data)
+{
+  DBusGSignalClosure *sigclosure;
+  DBusMessage *signal;
+  DBusMessageIter iter;
+  guint i;
+  const char *path;
+
+  sigclosure = (DBusGSignalClosure *) closure;
+  
+  g_assert (retval == NULL);
+
+  path = _dbus_gobject_get_path (sigclosure->object);
+
+  g_assert (path != NULL);
+
+  signal = dbus_message_new_signal (path,
+                                   "org.gtk.objects",
+                                   sigclosure->signame);
+  if (!signal)
+    {
+      g_error ("out of memory");
+      return;
+    }
+
+  dbus_message_iter_init_append (signal, &iter);
+
+  /* First argument is the object itself, and we can't marshall that */
+  for (i = 1; i < n_param_values; i++)
+    {
+      if (!dbus_gvalue_marshal (&iter,
+                               (GValue *) (&(param_values[i]))))
+       {
+         g_warning ("failed to marshal parameter %d for signal %s",
+                    i, sigclosure->signame);
+         goto out;
+       }
+    }
+  dbus_connection_send (DBUS_CONNECTION_FROM_G_CONNECTION (sigclosure->connection),
+                       signal, NULL);
+ out:
+  dbus_message_unref (signal);
+}
+
+static void
+export_signals (DBusGConnection *connection, GObject *object)
+{
+  guint i;
+  guint *ids, n_ids;
+
+  ids = g_signal_list_ids (G_TYPE_FROM_INSTANCE (object), &n_ids);
+  if (!n_ids)
+    return;
+
+  /* FIXME: recurse to parent types ? */
+  for (i = 0; i < n_ids; i++)
+    {
+      GSignalQuery query;
+      GClosure *closure;
+      
+      g_signal_query (ids[i], &query);
+
+      if (query.return_type != G_TYPE_NONE) {
+       g_warning("Not exporting signal '%s' as it has a return type %s", query.signal_name, g_type_name (query.return_type));
+       continue; /* FIXME: these could be listed as methods ? */
+      }
+
+      closure = dbus_g_signal_closure_new (connection, object, query.signal_name);
+      g_closure_set_marshal (closure, signal_emitter_marshaller);
+
+      g_signal_connect_closure_by_id (object,
+                                     ids[i],
+                                     0,
+                                     closure,
+                                     FALSE);
+
+      g_closure_add_finalize_notifier (closure, NULL,
+                                      dbus_g_signal_closure_finalize);
+    }
+}
+
 /** @} */ /* end of internals */
 
 /**
@@ -1017,7 +1235,7 @@ static DBusObjectPathVTable gobject_dbus_vtable = {
  */
 
 /**
- * Install introspection information about the given object class
+ * Install introspection information about the given object GType
  * sufficient to allow methods on the object to be invoked by name.
  * The introspection information is normally generated by
  * dbus-glib-tool, then this function is called in the
@@ -1027,16 +1245,24 @@ static DBusObjectPathVTable gobject_dbus_vtable = {
  * object registered with dbus_g_connection_register_g_object() can have
  * their methods invoked remotely.
  *
- * @param object_class class struct of the object
+ * @param object_type GType for the object
  * @param info introspection data generated by dbus-glib-tool
  */
 void
-dbus_g_object_class_install_info (GObjectClass          *object_class,
-                                  const DBusGObjectInfo *info)
+dbus_g_object_type_install_info (GType                  object_type,
+                                const DBusGObjectInfo *info)
 {
+  GObjectClass *object_class;
+
+  g_return_if_fail (G_TYPE_IS_OBJECT (object_type));
+
+  dbus_g_value_types_init ();
+
+  object_class = g_type_class_peek (object_type);
+
   g_return_if_fail (G_IS_OBJECT_CLASS (object_class));
 
-  g_static_rw_lock_writer_lock (&info_hash_lock);
+  g_static_rw_lock_writer_lock (&globals_lock);
 
   if (info_hash == NULL)
     {
@@ -1045,7 +1271,7 @@ dbus_g_object_class_install_info (GObjectClass          *object_class,
 
   g_hash_table_replace (info_hash, object_class, (void*) info);
 
-  g_static_rw_lock_writer_unlock (&info_hash_lock);
+  g_static_rw_lock_writer_unlock (&globals_lock);
 }
 
 static void
@@ -1087,12 +1313,175 @@ dbus_g_connection_register_g_object (DBusGConnection       *connection,
     return;
   }
 
+  export_signals (connection, object);
+
   g_object_set_data (object, "dbus_glib_object_path", g_strdup (at_path));
   g_object_weak_ref (object, (GWeakNotify)unregister_gobject, connection);
 }
 
+GObject *
+dbus_g_connection_lookup_g_object (DBusGConnection       *connection,
+                                  const char            *at_path)
+{
+  gpointer ret;
+  if (!dbus_connection_get_object_path_data (DBUS_CONNECTION_FROM_G_CONNECTION (connection), at_path, &ret))
+    return NULL;
+  return ret;
+}
+
+typedef struct {
+  GType    rettype;
+  guint    n_params;
+  GType   *params;
+} DBusGFuncSignature;
+
+static guint
+funcsig_hash (gconstpointer key)
+{
+  const DBusGFuncSignature *sig = key;
+  GType *types;
+  guint ret;
+
+  ret = sig->rettype;
+  types = sig->params;
+
+  while (*types != G_TYPE_INVALID)
+    {
+      ret += (int) (*types);
+      types++;
+    }
+      
+  return ret;
+}
+
+static gboolean
+funcsig_equal (gconstpointer aval,
+              gconstpointer bval)
+{
+  const DBusGFuncSignature *a = aval;
+  const DBusGFuncSignature *b = bval;
+  const GType *atypes;
+  const GType *btypes;
+
+  if (a->rettype != b->rettype)
+    return FALSE;
+
+  atypes = a->params;
+  btypes = b->params;
+
+  while (*atypes != G_TYPE_INVALID)
+    {
+      if (*btypes != *atypes)
+       return FALSE;
+      atypes++;
+      btypes++;
+    }
+  if (*btypes != G_TYPE_INVALID)
+    return FALSE;
+      
+  return TRUE;
+}
+
+GClosureMarshal
+_dbus_gobject_lookup_marshaller (GType        rettype,
+                                guint        n_params,
+                                const GType *param_types)
+{
+  GClosureMarshal ret;
+  DBusGFuncSignature sig;
+
+  sig.rettype = rettype;
+  sig.n_params = n_params;
+  sig.params = (GType*) param_types;
+  
+  g_static_rw_lock_reader_lock (&globals_lock);
+
+  if (marshal_table)
+    ret = g_hash_table_lookup (marshal_table, &sig);
+  else
+    ret = NULL;
+
+  g_static_rw_lock_reader_unlock (&globals_lock);
+
+  if (ret == NULL)
+    {
+      if (rettype == G_TYPE_NONE)
+       {
+         if (n_params == 0)
+           ret = g_cclosure_marshal_VOID__VOID;
+         else if (n_params == 1)
+           {
+             switch (param_types[0])
+               {
+               case G_TYPE_BOOLEAN:
+                 ret = g_cclosure_marshal_VOID__BOOLEAN;
+               case G_TYPE_UCHAR:
+                 ret = g_cclosure_marshal_VOID__UCHAR;
+               case G_TYPE_INT:
+                 ret = g_cclosure_marshal_VOID__INT;
+               case G_TYPE_UINT:
+                 ret = g_cclosure_marshal_VOID__UINT;
+               case G_TYPE_DOUBLE:
+                 ret = g_cclosure_marshal_VOID__DOUBLE;
+               case G_TYPE_STRING:
+                 ret = g_cclosure_marshal_VOID__STRING;
+               }
+           }
+       }
+      else if (n_params == 3
+              && param_types[0] == G_TYPE_STRING
+              && param_types[1] == G_TYPE_STRING
+              && param_types[2] == G_TYPE_STRING)
+       {
+         ret = _dbus_g_marshal_NONE__STRING_STRING_STRING;
+       }
+    }
+
+  return ret;
+}
+
+/**
+ * Register a GClosureMarshal to be used for signal invocations.
+ * This function will not be needed once GLib includes libffi.
+ *
+ * @param rettype a GType for the return type of the function
+ * @param n_types number of function parameters
+ * @param param_types a C array of GTypes values
+ * @param marshaller a GClosureMarshal to be used for invocation
+ */
+void
+dbus_g_object_register_marshaller (GType            rettype,
+                                  guint            n_types,
+                                  const GType      *param_types,
+                                  GClosureMarshal   marshaller)
+{
+  DBusGFuncSignature *sig;
+  
+  g_static_rw_lock_writer_lock (&globals_lock);
+
+  if (marshal_table == NULL)
+    marshal_table = g_hash_table_new_full (funcsig_hash,
+                                          funcsig_equal,
+                                          g_free,
+                                          NULL);
+  sig = g_new0 (DBusGFuncSignature, 1);
+  sig->rettype = rettype;
+  sig->n_params = n_types;
+  sig->params = g_new (GType, n_types);
+  memcpy (sig->params, param_types, n_types * sizeof (GType));
+
+  g_hash_table_insert (marshal_table, sig, marshaller);
+
+  g_static_rw_lock_writer_unlock (&globals_lock);
+}
+
 /** @} */ /* end of public API */
 
+const char * _dbus_gobject_get_path (GObject *obj)
+{
+  return g_object_get_data (obj, "dbus_glib_object_path");
+}
+
 #ifdef DBUS_BUILD_TESTS
 #include <stdlib.h>
 
index 6c78546..fcc38cd 100644 (file)
 #define DBUS_GLIB_OBJECT_H
 
 #include <dbus/dbus.h>
+#include <dbus/dbus-signature.h>
 #include <glib.h>
+#include "dbus/dbus-glib.h"
 
 G_BEGIN_DECLS
 
-GValueArray*       _dbus_glib_marshal_dbus_message_to_gvalue_array (DBusMessage         *message);
+const char *       _dbus_gobject_get_path (GObject *obj);
+
+GClosureMarshal    _dbus_gobject_lookup_marshaller (GType        rettype,
+                                                   guint        n_params,
+                                                   const GType *param_types);
+  
+                                                   
 
 G_END_DECLS
 
index b5e977a..42bfca8 100644 (file)
@@ -24,8 +24,8 @@
 #include <dbus/dbus-glib-lowlevel.h>
 #include <dbus/dbus-signature.h>
 #include "dbus-gutils.h"
-#include "dbus-gmarshal.h"
 #include "dbus-gvalue.h"
+#include "dbus-gvalue-utils.h"
 #include "dbus-gobject.h"
 #include <string.h>
 #include <glib/gi18n.h>
@@ -737,7 +737,7 @@ dbus_g_proxy_class_init (DBusGProxyClass *klass)
                   0,
                   NULL, NULL,
                   marshal_dbus_message_to_g_marshaller,
-                  G_TYPE_NONE, 2, DBUS_TYPE_MESSAGE, G_TYPE_STRING);
+                  G_TYPE_NONE, 2, DBUS_TYPE_MESSAGE, G_TYPE_POINTER);
 }
 
 static void
@@ -816,80 +816,6 @@ create_signal_name (const char *interface,
   return g_string_free (str, FALSE);
 }
 
-static GSignalCMarshaller
-lookup_g_marshaller (DBusGProxy *proxy,
-                     const char *signature)
-{
-  /* The "proxy" arg would eventually be used if you could provide
-   * a marshaller when adding a signal to the proxy
-   */
-
-#define MATCH1(sig, t0)         ((sig)[0] == (DBUS_TYPE_##t0) && (sig)[1] == '\0')
-#define MATCH2(sig, t0, t1)     ((sig)[0] == (DBUS_TYPE_##t0) && (sig)[1] == (DBUS_TYPE_##t1) && (sig)[2] == '\0')
-#define MATCH3(sig, t0, t1, t2) ((sig)[0] == (DBUS_TYPE_##t0) && (sig)[1] == (DBUS_TYPE_##t1) && (sig)[2] == (DBUS_TYPE_##t2) && (sig)[3] == '\0')
-  
-  switch (*signature)
-    {
-    case '\0':
-      return g_cclosure_marshal_VOID__VOID;
-
-    case DBUS_TYPE_BOOLEAN:
-      if (MATCH1 (signature, BOOLEAN))
-        return g_cclosure_marshal_VOID__BOOLEAN;
-      break;
-      
-    case DBUS_TYPE_BYTE:
-      if (MATCH1 (signature, BYTE))
-        return g_cclosure_marshal_VOID__UCHAR;
-      break;
-
-    case DBUS_TYPE_INT16:
-      if (MATCH1 (signature, INT16))
-        return g_cclosure_marshal_VOID__INT;
-      break;
-
-    case DBUS_TYPE_UINT16:
-      if (MATCH1 (signature, UINT16))
-        return g_cclosure_marshal_VOID__UINT;
-      break;
-      
-    case DBUS_TYPE_INT32:
-      if (MATCH1 (signature, INT32))
-        return g_cclosure_marshal_VOID__INT;
-      break;
-
-    case DBUS_TYPE_UINT32:
-      if (MATCH1 (signature, UINT32))
-        return g_cclosure_marshal_VOID__UINT;
-      break;
-
-    case DBUS_TYPE_DOUBLE:
-      if (MATCH1 (signature, DOUBLE))
-        return g_cclosure_marshal_VOID__DOUBLE;
-      break;
-
-    case DBUS_TYPE_OBJECT_PATH:
-      if (MATCH1 (signature, OBJECT_PATH))
-        return g_cclosure_marshal_VOID__STRING;
-      break;
-
-    case DBUS_TYPE_SIGNATURE:
-      if (MATCH1 (signature, SIGNATURE))
-        return g_cclosure_marshal_VOID__STRING;
-      break;
-      
-    case DBUS_TYPE_STRING:
-      if (MATCH1 (signature, STRING))
-        return g_cclosure_marshal_VOID__STRING;
-      /* This is for NameOwnerChanged */
-      else if (MATCH3 (signature, STRING, STRING, STRING))
-        return _dbus_g_marshal_NONE__STRING_STRING_STRING;
-      break;
-    }
-
-  return NULL;
-}
-
 static void
 marshal_dbus_message_to_g_marshaller (GClosure     *closure,
                                       GValue       *return_value,
@@ -909,25 +835,36 @@ marshal_dbus_message_to_g_marshaller (GClosure     *closure,
   GSignalCMarshaller c_marshaller;
   DBusGProxy *proxy;
   DBusMessage *message;
-  const char *signature;
+  GArray *gsignature;
+  const GType *types;
 
   g_assert (n_param_values == 3);
 
   proxy = g_value_get_object (&param_values[0]);
   message = g_value_get_boxed (&param_values[1]);
-  signature = g_value_get_string (&param_values[2]);
+  gsignature = g_value_get_pointer (&param_values[2]);
 
   g_return_if_fail (DBUS_IS_G_PROXY (proxy));
   g_return_if_fail (message != NULL);
-  g_return_if_fail (signature != NULL);
-  
-  c_marshaller = lookup_g_marshaller (proxy, signature);
+  g_return_if_fail (gsignature != NULL);
+
+  c_marshaller = _dbus_gobject_lookup_marshaller (G_TYPE_NONE, gsignature->len,
+                                                 (GType*) gsignature->data);
 
   g_return_if_fail (c_marshaller != NULL);
   
-  value_array = _dbus_glib_marshal_dbus_message_to_gvalue_array (message);
+  {
+    DBusGValueMarshalCtx context;
+    context.gconnection = DBUS_G_CONNECTION_FROM_CONNECTION (proxy->manager->connection);
+    context.proxy = proxy;
 
-  g_return_if_fail (value_array != NULL);
+    types = (const GType*) gsignature->data;
+    value_array = dbus_gvalue_demarshal_message (&context, message,
+                                                gsignature->len, types, NULL);
+  }
+
+  if (value_array == NULL)
+    return;
   
   g_value_init (&value, G_TYPE_FROM_INSTANCE (proxy));
   g_value_set_instance (&value, proxy);
@@ -966,37 +903,40 @@ dbus_g_proxy_emit_remote_signal (DBusGProxy  *proxy,
 
   if (q != 0)
     {
-      const char *signature;
-
-      signature = g_datalist_id_get_data (&proxy->signal_signatures, q);
-      if (signature == NULL)
-        {
-#if 0
-          /* this should not trigger a warning, as you shouldn't have to
-           * add signals you don't care about
-           */
-          g_warning ("Signal '%s' has not been added to this proxy object\n",
-                     name);
-#endif
-        }
-      else if (!dbus_message_has_signature (message, signature))
-        {
-          g_warning ("Signature '%s' expected for signal '%s', actual signature '%s'\n",
-                     signature,
-                     name,
-                     dbus_message_get_signature (message));
-        }
-      else
-        {
-          g_signal_emit (proxy,
-                         signals[RECEIVED],
-                         q,
-                         message,
-                         signature);
-        }
+      GArray *gsignature;
+      GArray *msg_gsignature;
+      guint i;
+      
+      gsignature = g_datalist_id_get_data (&proxy->signal_signatures, q);
+      if (gsignature == NULL)
+       goto out;
+      
+      msg_gsignature = dbus_gtypes_from_arg_signature (dbus_message_get_signature (message),
+                                                      TRUE);
+      for (i = 0; i < gsignature->len; i++)
+       {
+         if (msg_gsignature->len == i
+             || g_array_index (gsignature, GType, i) != g_array_index (msg_gsignature, GType, i))
+           goto mismatch;
+       }
+      if (msg_gsignature->len != i)
+       goto mismatch;
+      
+      g_signal_emit (proxy,
+                    signals[RECEIVED],
+                    q,
+                    message,
+                    msg_gsignature);
     }
 
+ out:
   g_free (name);
+  return;
+ mismatch:
+  g_warning ("Unexpected message signature '%s' for signal '%s'\n",
+            dbus_message_get_signature (message),
+            name);
+  goto out;
 }
 
 /** @} End of DBusGLibInternals */
@@ -1204,6 +1144,33 @@ dbus_g_proxy_new_for_name_owner (DBusGConnection          *connection,
 }
 
 /**
+ * Creates a proxy using an existing proxy as a template, substituting
+ * the specified interface and path.  Either or both may be NULL.
+ *
+ * @param proxy the proxy to use as a template
+ * @param path of the object inside the peer to call methods on
+ * @param interface name of the interface to call methods on
+ * @returns new proxy object
+ * 
+ */
+DBusGProxy*
+dbus_g_proxy_new_from_proxy (DBusGProxy        *proxy,
+                            const char        *interface,
+                            const char        *path)
+{
+  g_return_val_if_fail (proxy != NULL, NULL);
+
+  if (interface == NULL)
+    interface = proxy->interface;
+  if (path == NULL)
+    path = proxy->path;
+
+  return dbus_g_proxy_new (DBUS_G_CONNECTION_FROM_CONNECTION (proxy->manager->connection),
+                          proxy->name,
+                          path, interface);
+}
+
+/**
  * Creates a proxy for an object in peer application (one
  * we're directly connected to). That is, this function is
  * intended for use when there's no message bus involved,
@@ -1255,199 +1222,63 @@ dbus_g_proxy_get_bus_name (DBusGProxy        *proxy)
 }
 
 /**
- * Invokes a method on a remote interface. This function does not
- * block; instead it returns an opaque #DBusPendingCall object that
- * tracks the pending call.  The method call will not be sent over the
- * wire until the application returns to the main loop, or blocks in
- * dbus_connection_flush() to write out pending data.  The call will
- * be completed after a timeout, or when a reply is received.
- * To collect the results of the call (which may be an error,
- * or a reply), use dbus_g_proxy_end_call().
- *
- * @todo this particular function shouldn't die on out of memory,
- * since you should be able to do a call with large arguments.
- * 
- * @param proxy a proxy for a remote interface
- * @param method the name of the method to invoke
- * @param first_arg_type type of the first argument
+ * Gets the object interface proxy is bound to (may be #NULL in some cases).
  *
- * @returns opaque pending call object
- *  */
-DBusGPendingCall*
-dbus_g_proxy_begin_call (DBusGProxy *proxy,
-                        const char *method,
-                        int         first_arg_type,
-                        ...)
+ * @param proxy the proxy
+ * @returns an object interface 
+ */
+const char*
+dbus_g_proxy_get_interface (DBusGProxy        *proxy)
 {
-  DBusPendingCall *pending;
-  DBusMessage *message;
-  va_list args;
-  
   g_return_val_if_fail (DBUS_IS_G_PROXY (proxy), NULL);
   g_return_val_if_fail (!DBUS_G_PROXY_DESTROYED (proxy), NULL);
 
-  message = dbus_message_new_method_call (proxy->name,
-                                          proxy->path,
-                                          proxy->interface,
-                                          method);
-  if (message == NULL)
-    goto oom;
-
-  va_start (args, first_arg_type);
-  if (!dbus_message_append_args_valist (message, first_arg_type,
-                                        args))
-    goto oom;
-  va_end (args);
-
-  if (!dbus_connection_send_with_reply (proxy->manager->connection,
-                                        message,
-                                        &pending,
-                                        -1))
-    goto oom;
-
-  return DBUS_G_PENDING_CALL_FROM_PENDING_CALL (pending);
-
- oom:
-  /* FIXME we should create a pending call that's
-   * immediately completed with an error status without
-   * ever going on the wire.
-   */
-  
-  g_error ("Out of memory");
-  return NULL;
+  return proxy->interface;
 }
 
 /**
- * Collects the results of a method call. The method call was normally
- * initiated with dbus_g_proxy_end_call(). This function will block if
- * the results haven't yet been received; use
- * dbus_g_pending_call_set_notify() to be notified asynchronously that a
- * pending call has been completed. If it's completed, it will not block.
- *
- * If the call results in an error, the error is set as normal for
- * GError and the function returns #FALSE.
- *
- * Otherwise, the "out" parameters and return value of the
- * method are stored in the provided varargs list.
- * The list should be terminated with #DBUS_TYPE_INVALID.
+ * Sets the object interface proxy is bound to
  *
- * This function doesn't affect the reference count of the
- * #DBusGPendingCall, the caller of dbus_g_proxy_begin_call() still owns
- * a reference.
- *
- * @todo this should be changed to make a g_malloc() copy of the
- * data returned probably; right now the data vanishes
- * when you free the PendingCall which is sort of strange.
- *
- * @param proxy a proxy for a remote interface
- * @param pending the pending call from dbus_g_proxy_begin_call()
- * @param error return location for an error
- * @param first_arg_type type of first "out" argument
- * @returns #FALSE if an error is set
+ * @param proxy the proxy
+ * @param interface_name an object interface 
  */
-gboolean
-dbus_g_proxy_end_call (DBusGProxy          *proxy,
-                       DBusGPendingCall    *pending,
-                       GError             **error,
-                       int                  first_arg_type,
-                       ...)
+void
+dbus_g_proxy_set_interface (DBusGProxy        *proxy,
+                           const char        *interface_name)
 {
-  DBusMessage *message;
-  va_list args;
-  DBusError derror;
-  
-  g_return_val_if_fail (DBUS_IS_G_PROXY (proxy), FALSE);
-  g_return_val_if_fail (!DBUS_G_PROXY_DESTROYED (proxy), FALSE);
-  g_return_val_if_fail (pending != NULL, FALSE);
-
-  dbus_pending_call_block (DBUS_PENDING_CALL_FROM_G_PENDING_CALL (pending));
-  message = dbus_pending_call_steal_reply (DBUS_PENDING_CALL_FROM_G_PENDING_CALL (pending));
-
-  g_assert (message != NULL);
-
-  dbus_error_init (&derror);
-
-  switch (dbus_message_get_type (message))
-    {
-    case DBUS_MESSAGE_TYPE_METHOD_RETURN:
-      va_start (args, first_arg_type);
-      if (!dbus_message_get_args_valist (message, &derror, first_arg_type, args))
-        {
-          va_end (args);
-          goto error;
-        }
-      va_end (args);
-
-      dbus_message_unref (message);
-      return TRUE;
-      
-    case DBUS_MESSAGE_TYPE_ERROR:
-      dbus_set_error_from_message (&derror, message);
-      goto error;
-
-    default:
-      dbus_set_error (&derror, DBUS_ERROR_FAILED,
-                      "Reply was neither a method return nor an exception");
-      goto error;
-    }
-
- error:
-  dbus_message_unref (message);
-  
-  dbus_set_g_error (error, &derror);
-  dbus_error_free (&derror);
-  return FALSE;
+  /* FIXME - need to unregister when we switch interface for now
+   * later should support idea of unset interface
+   */
+  dbus_g_proxy_manager_unregister (proxy->manager, proxy);
+  g_free (proxy->interface);
+  proxy->interface = g_strdup (interface_name);
+  dbus_g_proxy_manager_register (proxy->manager, proxy);
 }
 
 /**
- * Function for invoking a method and receiving reply values.
- * Normally this is not used directly - calls to it are generated
- * from client-side wrappers (see dbus-binding-tool).
+ * Gets the path this proxy is bound to
  *
- * This function takes two type signatures, one for method arguments,
- * and one for return values.  The remaining arguments after the
- * error parameter should be values of the input arguments,
- * followed by pointer values to storage for return values.
- *
- * @param proxy a proxy for a remote interface
- * @param method method to invoke
- * @param insig signature of input values
- * @param outsig signature of output values
- * @param error return location for an error
- * @returns #FALSE if an error is set, TRUE otherwise
+ * @param proxy the proxy
+ * @returns an object path
  */
-gboolean
-dbus_g_proxy_invoke (DBusGProxy        *proxy,
-                    const char        *method,
-                    const char        *insig,
-                    const char        *outsig,
-                    GError           **error,
-                    ...)
+const char*
+dbus_g_proxy_get_path (DBusGProxy        *proxy)
+{
+  g_return_val_if_fail (DBUS_IS_G_PROXY (proxy), NULL);
+  g_return_val_if_fail (!DBUS_G_PROXY_DESTROYED (proxy), NULL);
+
+  return proxy->path;
+}
+
+static DBusMessage *
+dbus_g_proxy_marshal_args_to_message (DBusGProxy  *proxy,
+                                     const char  *method,
+                                     GValueArray *args)
 {
-  DBusPendingCall *pending;
   DBusMessage *message;
-  DBusMessage *reply;
-  va_list args;
-  va_list args_unwind;
-  int n_retvals_processed;
   DBusMessageIter msgiter;
-  DBusSignatureIter sigiter;
-  int expected_type;
-  gboolean ret;
-  DBusError derror;
-  
-  g_return_val_if_fail (DBUS_IS_G_PROXY (proxy), FALSE);
-  g_return_val_if_fail (!DBUS_G_PROXY_DESTROYED (proxy), FALSE);
-
-  va_start (args, error);
-  /* Keep around a copy of output arguments so we
-   * can free on error. */
-  G_VA_COPY (args_unwind, args);
+  guint i;
 
-  ret = FALSE;
-  pending = NULL;
-  reply = NULL;
-  n_retvals_processed = 0;
   message = dbus_message_new_method_call (proxy->name,
                                           proxy->path,
                                           proxy->interface,
@@ -1455,47 +1286,91 @@ dbus_g_proxy_invoke (DBusGProxy        *proxy,
   if (message == NULL)
     goto oom;
 
-  dbus_signature_iter_init (&sigiter, insig);
   dbus_message_iter_init_append (message, &msgiter);
-  while ((expected_type = dbus_signature_iter_get_current_type (&sigiter)) != DBUS_TYPE_INVALID)
+  for (i = 0; i < args->n_values; i++)
     {
-      GValue gvalue = {0, };
-      char *collect_err;
+      GValue *gvalue;
 
-      if (!dbus_gvalue_init (expected_type, &gvalue))
-       {
-         g_set_error (error, DBUS_GERROR,
-                      DBUS_GERROR_INVALID_ARGS,
-                      _("Unsupported type '%c'"), expected_type);
-         goto out;
-       } 
-      
-      G_VALUE_COLLECT (&gvalue, args, G_VALUE_NOCOPY_CONTENTS, &collect_err);
+      gvalue = g_value_array_get_nth (args, i);
 
-      if (collect_err)
-       {
-         g_set_error (error, DBUS_GERROR,
-                      DBUS_GERROR_INVALID_ARGS,
-                      collect_err);
-         goto out;
-       }
-
-      /* Anything we can init must be marshallable */
-      if (!dbus_gvalue_marshal (&msgiter, &gvalue))
+      if (!dbus_gvalue_marshal (&msgiter, gvalue))
        g_assert_not_reached ();
-      g_value_unset (&gvalue);
-
-      dbus_signature_iter_next (&sigiter);
     }
+  return message;
+ oom:
+  return NULL;
+}
+
+#define DBUS_G_VALUE_ARRAY_COLLECT_ALL(VALARRAY, FIRST_ARG_TYPE, ARGS) \
+do { \
+  GType valtype; \
+  int i = 0; \
+  VALARRAY = g_value_array_new (6); \
+  valtype = FIRST_ARG_TYPE; \
+  while (valtype != G_TYPE_INVALID) \
+    { \
+      const char *collect_err; \
+      GValue *val; \
+      g_value_array_append (VALARRAY, NULL); \
+      val = g_value_array_get_nth (VALARRAY, i); \
+      g_value_init (val, valtype); \
+      collect_err = NULL; \
+      G_VALUE_COLLECT (val, ARGS, G_VALUE_NOCOPY_CONTENTS, &collect_err); \
+      valtype = va_arg (ARGS, GType); \
+      i++; \
+    } \
+} while (0)
+
+static DBusGPendingCall *
+dbus_g_proxy_begin_call_internal (DBusGProxy    *proxy,
+                                 const char    *method,
+                                 GValueArray   *args)
+{
+  DBusMessage *message;
+  DBusPendingCall *pending;
+
+  g_return_val_if_fail (DBUS_IS_G_PROXY (proxy), FALSE);
+  g_return_val_if_fail (!DBUS_G_PROXY_DESTROYED (proxy), FALSE);
+
+  pending = NULL;
 
+  message = dbus_g_proxy_marshal_args_to_message (proxy, method, args);
+  if (!message)
+    goto oom;
+  
   if (!dbus_connection_send_with_reply (proxy->manager->connection,
                                         message,
                                         &pending,
                                         -1))
     goto oom;
+  g_assert (pending != NULL);
 
-  dbus_pending_call_block (pending);
-  reply = dbus_pending_call_steal_reply (pending);
+  return DBUS_G_PENDING_CALL_FROM_PENDING_CALL (pending);
+ oom:
+  g_error ("Out of memory");
+  return NULL;
+}
+
+static gboolean
+dbus_g_proxy_end_call_internal (DBusGProxy       *proxy,
+                               DBusGPendingCall *pending,
+                               GError          **error,
+                               GType             first_arg_type,
+                               va_list           args)
+{
+  DBusMessage *reply;
+  DBusMessageIter msgiter;
+  DBusError derror;
+  va_list args_unwind;
+  int n_retvals_processed;
+  gboolean ret;
+  GType valtype;
+
+  reply = NULL;
+  ret = FALSE;
+  n_retvals_processed = 0;
+  dbus_pending_call_block (DBUS_PENDING_CALL_FROM_G_PENDING_CALL (pending));
+  reply = dbus_pending_call_steal_reply (DBUS_PENDING_CALL_FROM_G_PENDING_CALL (pending));
 
   g_assert (reply != NULL);
 
@@ -1505,53 +1380,86 @@ dbus_g_proxy_invoke (DBusGProxy        *proxy,
     {
     case DBUS_MESSAGE_TYPE_METHOD_RETURN:
 
-      dbus_signature_iter_init (&sigiter, outsig);
       dbus_message_iter_init (reply, &msgiter);
-      while ((expected_type = dbus_signature_iter_get_current_type (&sigiter)) != DBUS_TYPE_INVALID)
+      valtype = first_arg_type;
+      while (valtype != G_TYPE_INVALID)
        {
          int arg_type;
-         gpointer *value_ret;
+         gpointer return_storage;
          GValue gvalue = { 0, };
+         DBusGValueMarshalCtx context;
+
+         context.gconnection = DBUS_G_CONNECTION_FROM_CONNECTION (proxy->manager->connection);
+         context.proxy = proxy;
 
-         value_ret = va_arg (args, gpointer *);
 
          arg_type = dbus_message_iter_get_arg_type (&msgiter);
-         if (expected_type != arg_type)
+         if (arg_type == DBUS_TYPE_INVALID)
+           g_set_error (error, DBUS_GERROR,
+                        DBUS_GERROR_INVALID_ARGS,
+                        _("Too few arguments in reply"));
+
+         return_storage = va_arg (args, gpointer);
+         if (return_storage == NULL)
+           goto next;
+
+         /* We handle variants specially; the caller is expected
+          * to have already allocated storage for them.
+          */
+         if (arg_type == DBUS_TYPE_VARIANT
+             && g_type_is_a (valtype, G_TYPE_VALUE))
            {
-             if (arg_type == DBUS_TYPE_INVALID)
-                 g_set_error (error, DBUS_GERROR,
-                              DBUS_GERROR_INVALID_ARGS,
-                              _("Too few arguments in reply"));
-             else
-                 g_set_error (error, DBUS_GERROR,
+             if (!dbus_gvalue_demarshal_variant (&context, &msgiter, (GValue*) return_storage, NULL))
+               {
+                 g_set_error (error,
+                              DBUS_GERROR,
                               DBUS_GERROR_INVALID_ARGS,
-                              _("Reply argument was \"%c\", expected \"%c\""),
-                              arg_type, expected_type);  
-             goto out;
+                              _("Couldn't convert argument, expected \"%s\""),
+                              g_type_name (valtype));
+                 goto out;
+               }
            }
-         
-         if (!dbus_gvalue_demarshal (&msgiter, &gvalue))
+         else
            {
-             g_set_error (error,
-                          DBUS_GERROR,
-                          DBUS_GERROR_INVALID_ARGS,
-                          _("Couldn't convert argument type \"%c\""), expected_type);
-             goto out;
+             g_value_init (&gvalue, valtype);
+
+             /* FIXME, should use error here instead of NULL */ 
+             if (!dbus_gvalue_demarshal (&context, &msgiter, &gvalue, NULL))
+               {
+                 g_set_error (error,
+                              DBUS_GERROR,
+                              DBUS_GERROR_INVALID_ARGS,
+                              _("Couldn't convert argument, expected \"%s\""),
+                              g_type_name (valtype));
+                 goto out;
+               }
+
+             if (G_VALUE_TYPE (&gvalue) != valtype)
+               {
+                 g_set_error (error, DBUS_GERROR,
+                              DBUS_GERROR_INVALID_ARGS,
+                              _("Reply argument was \"%s\", expected \"%s\""),
+                              g_type_name (G_VALUE_TYPE (&gvalue)),
+                              g_type_name (valtype));  
+                 goto out;
+               }
+
+             /* Anything that can be demarshaled must be storable */
+             if (!dbus_gvalue_store (&gvalue, (gpointer*) return_storage))
+               g_assert_not_reached ();
+             /* Ownership of the value passes to the client, don't unset */
            }
-         /* Anything that can be demarshaled must be storable */
-         if (!dbus_gvalue_store (&gvalue, value_ret))
-           g_assert_not_reached ();
-         g_value_unset (&gvalue);
          
+       next:
          n_retvals_processed++;
-         dbus_signature_iter_next (&sigiter);
          dbus_message_iter_next (&msgiter);
+         valtype = va_arg (args, GType);
        }
       if (dbus_message_iter_get_arg_type (&msgiter) != DBUS_TYPE_INVALID)
        {
          g_set_error (error, DBUS_GERROR,
                       DBUS_GERROR_INVALID_ARGS,
-                      _("Too many arguments"));
+                      _("Too many arguments in reply"));
          goto out;
        }
       break;
@@ -1589,16 +1497,132 @@ dbus_g_proxy_invoke (DBusGProxy        *proxy,
   va_end (args_unwind);
 
   if (pending)
-    dbus_pending_call_unref (pending);
-  if (message)
-    dbus_message_unref (message);
+    dbus_g_pending_call_unref (pending);
   if (reply)
     dbus_message_unref (reply);
   return ret;
- oom:
-  g_error ("Out of memory");
-  ret = FALSE;
-  goto out;
+}
+
+
+/**
+ * Invokes a method on a remote interface. This function does not
+ * block; instead it returns an opaque #DBusPendingCall object that
+ * tracks the pending call.  The method call will not be sent over the
+ * wire until the application returns to the main loop, or blocks in
+ * dbus_connection_flush() to write out pending data.  The call will
+ * be completed after a timeout, or when a reply is received.
+ * To collect the results of the call (which may be an error,
+ * or a reply), use dbus_g_proxy_end_call().
+ *
+ * @todo this particular function shouldn't die on out of memory,
+ * since you should be able to do a call with large arguments.
+ * 
+ * @param proxy a proxy for a remote interface
+ * @param method the name of the method to invoke
+ * @param first_arg_type type of the first argument
+ *
+ * @returns opaque pending call object
+ *  */
+DBusGPendingCall*
+dbus_g_proxy_begin_call (DBusGProxy *proxy,
+                        const char  *method,
+                        GType       first_arg_type,
+                        ...)
+{
+  DBusGPendingCall *pending;
+  va_list args;
+  GValueArray *arg_values;
+  
+  va_start (args, first_arg_type);
+
+  DBUS_G_VALUE_ARRAY_COLLECT_ALL (arg_values, first_arg_type, args);
+  
+  pending = dbus_g_proxy_begin_call_internal (proxy, method, arg_values);
+
+  g_value_array_free (arg_values);
+
+  va_end (args);
+
+  return pending;
+}
+
+/**
+ * Collects the results of a method call. The method call was normally
+ * initiated with dbus_g_proxy_end_call(). This function will block if
+ * the results haven't yet been received; use
+ * dbus_g_pending_call_set_notify() to be notified asynchronously that a
+ * pending call has been completed. If it's completed, it will not block.
+ *
+ * If the call results in an error, the error is set as normal for
+ * GError and the function returns #FALSE.
+ *
+ * Otherwise, the "out" parameters and return value of the
+ * method are stored in the provided varargs list.
+ * The list should be terminated with G_TYPE_INVALID.
+ *
+ * This function unrefs the pending call.
+ *
+ * @param proxy a proxy for a remote interface
+ * @param pending the pending call from dbus_g_proxy_begin_call()
+ * @param error return location for an error
+ * @param first_arg_type type of first "out" argument
+ * @returns #FALSE if an error is set
+ */
+gboolean
+dbus_g_proxy_end_call (DBusGProxy          *proxy,
+                       DBusGPendingCall    *pending,
+                       GError             **error,
+                       GType                first_arg_type,
+                       ...)
+{
+  gboolean ret;
+  va_list args;
+
+  va_start (args, first_arg_type);
+
+  ret = dbus_g_proxy_end_call_internal (proxy, pending, error, first_arg_type, args);
+
+  va_end (args);
+  
+  return ret;
+}
+
+/**
+ * Function for invoking a method and receiving reply values.
+ * Normally this is not used directly - calls to it are generated
+ * from client-side wrappers (see dbus-binding-tool).
+ *
+ * @param proxy a proxy for a remote interface
+ * @param method method to invoke
+ * @param error return location for an error
+ * @returns #FALSE if an error is set, TRUE otherwise
+ */
+gboolean
+dbus_g_proxy_invoke (DBusGProxy        *proxy,
+                    const char        *method,
+                    GError           **error,
+                    GType              first_arg_type,
+                    ...)
+{
+  gboolean ret;
+  DBusGPendingCall *pending;
+  va_list args;
+  GValueArray *in_args;
+
+  va_start (args, first_arg_type);
+
+  DBUS_G_VALUE_ARRAY_COLLECT_ALL (in_args, first_arg_type, args);
+
+  pending = dbus_g_proxy_begin_call_internal (proxy, method, in_args);
+
+  g_value_array_free (in_args);
+
+  first_arg_type = va_arg (args, GType);
+  ret = dbus_g_proxy_end_call_internal (proxy, pending, error, first_arg_type, args);
+
+  va_end (args);
+
+  return ret;
 }
 
 /**
@@ -1614,30 +1638,29 @@ dbus_g_proxy_invoke (DBusGProxy        *proxy,
  */
 void
 dbus_g_proxy_call_no_reply (DBusGProxy               *proxy,
-                           const char               *method,
-                           int                       first_arg_type,
-                           ...)
+                           const char               *method,
+                           GType                     first_arg_type,
+                           ...)
 {
   DBusMessage *message;
   va_list args;
+  GValueArray *in_args;
   
   g_return_if_fail (DBUS_IS_G_PROXY (proxy));
   g_return_if_fail (!DBUS_G_PROXY_DESTROYED (proxy));
 
-  message = dbus_message_new_method_call (proxy->name,
-                                          proxy->path,
-                                          proxy->interface,
-                                          method);
-  if (message == NULL)
+  va_start (args, first_arg_type);
+  DBUS_G_VALUE_ARRAY_COLLECT_ALL (in_args, first_arg_type, args);
+
+  message = dbus_g_proxy_marshal_args_to_message (proxy, method, in_args);
+
+  g_value_array_free (in_args);
+  va_end (args);
+
+  if (!message)
     goto oom;
 
   dbus_message_set_no_reply (message, TRUE);
-  
-  va_start (args, first_arg_type);
-  if (!dbus_message_append_args_valist (message, first_arg_type,
-                                        args))
-    goto oom;
-  va_end (args);
 
   if (!dbus_connection_send (proxy->manager->connection,
                              message,
@@ -1696,41 +1719,63 @@ dbus_g_proxy_send (DBusGProxy          *proxy,
     g_error ("Out of memory\n");
 }
 
+static void
+array_free_all (gpointer array)
+{
+  g_array_free (array, TRUE);
+}
+
 /**
- * Specifies the signature of a signal, such that it's possible to
- * connect to the signal on this proxy.
+ * Specifies the argument signature of a signal;.only necessary
+ * if the remote object does not support introspection.  The arguments
+ * specified are the GLib types expected.
  *
  * @param proxy the proxy for a remote interface
  * @param signal_name the name of the signal
- * @param signature D-BUS signature of the signal
+ * @param first_type the first argument type, or G_TYPE_INVALID if none
  */
 void
 dbus_g_proxy_add_signal  (DBusGProxy        *proxy,
                           const char        *signal_name,
-                          const char        *signature)
+                         GType              first_type,
+                          ...)
 {
   GQuark q;
   char *name;
+  GArray *gtypesig;
+  GType gtype;
+  va_list args;
 
   g_return_if_fail (DBUS_IS_G_PROXY (proxy));
   g_return_if_fail (!DBUS_G_PROXY_DESTROYED (proxy));
   g_return_if_fail (signal_name != NULL);
-  g_return_if_fail (signature != NULL);
-#ifndef G_DISABLE_CHECKS
-  if (lookup_g_marshaller (proxy, signature) == NULL)
-    g_warning ("No marshaller for signature '%s', we need to add API for providing your own",
-               signature);
-#endif
   
   name = create_signal_name (proxy->interface, signal_name);
   
   q = g_quark_from_string (name);
   
   g_return_if_fail (g_datalist_id_get_data (&proxy->signal_signatures, q) == NULL);
+
+  gtypesig = g_array_new (FALSE, TRUE, sizeof (GType));
+
+  va_start (args, first_type);
+  gtype = first_type;
+  while (gtype != G_TYPE_INVALID)
+    {
+      g_array_append_val (gtypesig, gtype);
+      gtype = va_arg (args, GType);
+    }
+  va_end (args);
+
+#ifndef G_DISABLE_CHECKS
+  if (_dbus_gobject_lookup_marshaller (G_TYPE_NONE, gtypesig->len, (const GType*) gtypesig->data) == NULL)
+    g_warning ("No marshaller for signature of signal '%s'", signal_name);
+#endif
+
   
   g_datalist_id_set_data_full (&proxy->signal_signatures,
-                               q, g_strdup (signature),
-                               g_free);
+                               q, gtypesig,
+                               array_free_all);
 
   g_free (name);
 }
diff --git a/glib/dbus-gtype-specialized.c b/glib/dbus-gtype-specialized.c
new file mode 100644 (file)
index 0000000..b331fcc
--- /dev/null
@@ -0,0 +1,441 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-gtype-specialized.c: Non-DBus-specific functions for specialized GTypes
+ *
+ * Copyright (C) 2005 Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "dbus-gtype-specialized.h"
+#include <glib.h>
+#include <string.h>
+#include <gobject/gvaluecollector.h>
+
+typedef enum {
+  DBUS_G_SPECTYPE_COLLECTION,
+  DBUS_G_SPECTYPE_MAP
+} DBusGTypeSpecializedType;
+
+typedef struct {
+  DBusGTypeSpecializedType type;
+  const DBusGTypeSpecializedVtable     *vtable;
+} DBusGTypeSpecializedContainer;
+
+typedef struct {
+  GType types[6];
+  const DBusGTypeSpecializedContainer     *klass;
+} DBusGTypeSpecializedData;
+
+static GHashTable /* char * -> data* */ *specialized_containers;
+
+static GQuark
+specialized_type_data_quark ()
+{
+  static GQuark quark;
+  if (!quark)
+    quark = g_quark_from_static_string ("DBusGTypeSpecializedData");
+  
+  return quark;
+}
+
+void
+dbus_g_type_specialized_init (void)
+{
+  specialized_containers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
+}
+
+static gboolean
+specialized_types_is_initialized (void)
+{
+  return specialized_containers != NULL;
+}
+
+static DBusGTypeSpecializedData *
+lookup_specialization_data (GType type)
+{
+  return g_type_get_qdata (type, specialized_type_data_quark ());
+}
+
+/* Copied from gboxed.c */
+static void
+proxy_value_init (GValue *value)
+{
+  value->data[0].v_pointer = NULL;
+}
+
+/* Adapted from gboxed.c */
+static void
+proxy_value_free (GValue *value)
+{
+  if (value->data[0].v_pointer && !(value->data[1].v_uint & G_VALUE_NOCOPY_CONTENTS))
+    {
+      DBusGTypeSpecializedData *data;
+      GType type;
+
+      type = G_VALUE_TYPE (value);
+      data = lookup_specialization_data (type);
+      g_assert (data != NULL);
+
+      data->klass->vtable->free_func (type, value->data[0].v_pointer);
+    }
+}
+
+/* Adapted from gboxed.c */
+static void
+proxy_value_copy (const GValue *src_value,
+                 GValue       *dest_value)
+{
+  if (src_value->data[0].v_pointer)
+    {
+      DBusGTypeSpecializedData *data;
+      GType type;
+      type = G_VALUE_TYPE (src_value);
+      data = lookup_specialization_data (type);
+      g_assert (data != NULL);
+      dest_value->data[0].v_pointer = data->klass->vtable->copy_func (type, src_value->data[0].v_pointer);
+    }
+  else
+    dest_value->data[0].v_pointer = src_value->data[0].v_pointer;
+}
+
+/* Copied from gboxed.c */
+static gpointer
+proxy_value_peek_pointer (const GValue *value)
+{
+  return value->data[0].v_pointer;
+}
+
+/* Adapted from gboxed.c */
+static gchar*
+proxy_collect_value (GValue      *value,
+                    guint        n_collect_values,
+                    GTypeCValue *collect_values,
+                    guint        collect_flags)
+{
+  DBusGTypeSpecializedData *data;
+  GType type;
+
+  type = G_VALUE_TYPE (value);
+  data = lookup_specialization_data (type);
+
+  if (!collect_values[0].v_pointer)
+    value->data[0].v_pointer = NULL;
+  else
+    {
+      if (collect_flags & G_VALUE_NOCOPY_CONTENTS)
+       {
+         value->data[0].v_pointer = collect_values[0].v_pointer;
+         value->data[1].v_uint = G_VALUE_NOCOPY_CONTENTS;
+       }
+      else
+       value->data[0].v_pointer = data->klass->vtable->copy_func (type, collect_values[0].v_pointer);
+    }
+
+  return NULL;
+}
+
+/* Adapted from gboxed.c */
+static gchar*
+proxy_lcopy_value (const GValue *value,
+                  guint         n_collect_values,
+                  GTypeCValue  *collect_values,
+                  guint         collect_flags)
+{
+  gpointer *boxed_p = collect_values[0].v_pointer;
+
+  if (!boxed_p)
+    return g_strdup_printf ("value location for `%s' passed as NULL", G_VALUE_TYPE_NAME (value));
+
+  if (!value->data[0].v_pointer)
+    *boxed_p = NULL;
+  else if (collect_flags & G_VALUE_NOCOPY_CONTENTS)
+    *boxed_p = value->data[0].v_pointer;
+  else
+    {
+      DBusGTypeSpecializedData *data;
+      GType type;
+
+      type = G_VALUE_TYPE (value);
+      data = lookup_specialization_data (type);
+
+      *boxed_p = data->klass->vtable->copy_func (type, value->data[0].v_pointer);
+    }
+
+  return NULL;
+}
+
+static char *
+build_specialization_name (const char *prefix, GType first_type, GType second_type)
+{
+  GString *fullname;
+
+  fullname = g_string_new (prefix);
+  g_string_append_c (fullname, '+');
+  g_string_append (fullname, g_type_name (first_type));
+  if (second_type != G_TYPE_INVALID)
+    {
+      g_string_append_c (fullname, '+');
+      g_string_append (fullname, g_type_name (second_type));
+    }
+  return g_string_free (fullname, FALSE);
+}
+
+static void
+register_container (const char                         *name,
+                   DBusGTypeSpecializedType            type,
+                   const DBusGTypeSpecializedVtable   *vtable)
+{
+  DBusGTypeSpecializedContainer *klass;
+  
+  klass = g_new0 (DBusGTypeSpecializedContainer, 1);
+  klass->type = type;
+  klass->vtable = vtable;
+
+  g_hash_table_insert (specialized_containers, g_strdup (name), klass);
+}
+
+void
+dbus_g_type_register_collection (const char                                   *name,
+                                const DBusGTypeSpecializedCollectionVtable   *vtable,
+                                guint                                         flags)
+{
+  g_return_if_fail (specialized_types_is_initialized ());
+  register_container (name, DBUS_G_SPECTYPE_COLLECTION, (const DBusGTypeSpecializedVtable*) vtable);
+}
+
+void
+dbus_g_type_register_map (const char                            *name,
+                         const DBusGTypeSpecializedMapVtable   *vtable,
+                         guint                                  flags)
+{
+  g_return_if_fail (specialized_types_is_initialized ());
+  register_container (name, DBUS_G_SPECTYPE_MAP, (const DBusGTypeSpecializedVtable*) vtable);
+}
+
+static GType
+register_specialized_instance (const DBusGTypeSpecializedContainer   *klass,
+                              char                                  *name,
+                              GType                                  first_type,
+                              GType                                  second_type)
+{
+  GType ret;
+  
+  static const GTypeValueTable vtable =
+    {
+      proxy_value_init,
+      proxy_value_free,
+      proxy_value_copy,
+      proxy_value_peek_pointer,
+      "p",
+      proxy_collect_value,
+      "p",
+      proxy_lcopy_value,
+    };
+  static const GTypeInfo derived_info =
+    {
+      0,               /* class_size */
+      NULL,            /* base_init */
+      NULL,            /* base_finalize */
+      NULL,            /* class_init */
+      NULL,            /* class_finalize */
+      NULL,            /* class_data */
+      0,               /* instance_size */
+      0,               /* n_preallocs */
+      NULL,            /* instance_init */
+      &vtable,         /* value_table */
+    };
+
+  ret = g_type_register_static (G_TYPE_BOXED, name, &derived_info, 0);
+  /* install proxy functions upon successfull registration */
+  if (ret != G_TYPE_INVALID)
+    {
+      DBusGTypeSpecializedData *data;
+      data = g_new0 (DBusGTypeSpecializedData, 1);
+      data->types[0] = first_type;
+      data->types[1] = second_type;
+      data->klass = klass;
+      g_type_set_qdata (ret, specialized_type_data_quark (), data);
+    }
+
+  return ret;
+}
+
+static GType
+lookup_or_register_specialized (const char  *container,
+                               GType       first_type,
+                               GType       second_type)
+{
+  GType ret;
+  char *name;
+  const DBusGTypeSpecializedContainer *klass;
+
+  g_return_val_if_fail (specialized_types_is_initialized (), G_TYPE_INVALID);
+
+  klass = g_hash_table_lookup (specialized_containers, container);
+  g_return_val_if_fail (klass != NULL, G_TYPE_INVALID);
+
+  name = build_specialization_name (container, first_type, second_type);
+  ret = g_type_from_name (name);
+  if (ret == G_TYPE_INVALID)
+    {
+      /* Take ownership of name */
+      ret = register_specialized_instance (klass, name,
+                                          first_type,
+                                          second_type);
+    }
+  else
+    g_free (name);
+  return ret;
+}
+
+GType
+dbus_g_type_get_collection (const char *container,
+                           GType       specialization)
+{
+  return lookup_or_register_specialized (container, specialization, G_TYPE_INVALID);
+}
+
+GType
+dbus_g_type_get_map (const char   *container,
+                    GType         key_specialization,
+                    GType         value_specialization)
+{
+  return lookup_or_register_specialized (container, key_specialization, value_specialization);
+}
+
+gboolean
+dbus_g_type_is_collection (GType gtype)
+{
+  DBusGTypeSpecializedData *data;
+  data = lookup_specialization_data (gtype);
+  if (data == NULL)
+    return FALSE;
+  return data->klass->type == DBUS_G_SPECTYPE_COLLECTION;
+}
+
+gboolean
+dbus_g_type_is_map (GType gtype)
+{
+  DBusGTypeSpecializedData *data;
+  data = lookup_specialization_data (gtype);
+  if (data == NULL)
+    return FALSE;
+  return data->klass->type == DBUS_G_SPECTYPE_MAP;
+}
+
+static GType
+get_specialization_index (GType gtype, guint i)
+{
+  DBusGTypeSpecializedData *data;
+
+  data = lookup_specialization_data (gtype);
+  return data->types[i];
+}
+
+GType
+dbus_g_type_get_collection_specialization (GType gtype)
+{
+  g_return_val_if_fail (dbus_g_type_is_collection (gtype), G_TYPE_INVALID);
+  return get_specialization_index (gtype, 0);
+}
+
+GType
+dbus_g_type_get_map_key_specialization (GType gtype)
+{
+  g_return_val_if_fail (dbus_g_type_is_map (gtype), G_TYPE_INVALID);
+  return get_specialization_index (gtype, 0);
+}
+
+GType
+dbus_g_type_get_map_value_specialization (GType gtype)
+{
+  g_return_val_if_fail (dbus_g_type_is_map (gtype), G_TYPE_INVALID);
+  return get_specialization_index (gtype, 1);
+}
+
+gpointer
+dbus_g_type_specialized_construct (GType type)
+{
+  DBusGTypeSpecializedData *data;
+  g_return_val_if_fail (specialized_types_is_initialized (), FALSE);
+
+  data = lookup_specialization_data (type);
+  g_return_val_if_fail (data != NULL, FALSE);
+
+  return data->klass->vtable->constructor (type);
+}
+
+gboolean
+dbus_g_type_collection_get_fixed (GValue   *value,
+                                 gpointer *data_ret,
+                                 guint    *len_ret)
+{
+  DBusGTypeSpecializedData *data;
+  GType gtype;
+
+  g_return_val_if_fail (specialized_types_is_initialized (), FALSE);
+  g_return_val_if_fail (G_VALUE_HOLDS_BOXED (value), FALSE);
+
+  gtype = G_VALUE_TYPE (value);
+  data = lookup_specialization_data (gtype);
+  g_return_val_if_fail (data != NULL, FALSE);
+
+  return ((DBusGTypeSpecializedCollectionVtable *) (data->klass->vtable))->fixed_accessor (gtype,
+                                                                                          g_value_get_boxed (value),
+                                                                                          data_ret, len_ret);
+}
+
+void
+dbus_g_type_collection_value_iterate (GValue                                 *value,
+                                     DBusGTypeSpecializedCollectionIterator  iterator,
+                                     gpointer                                user_data)
+{
+  DBusGTypeSpecializedData *data;
+  GType gtype;
+
+  g_return_if_fail (specialized_types_is_initialized ());
+  g_return_if_fail (G_VALUE_HOLDS_BOXED (value));
+
+  gtype = G_VALUE_TYPE (value);
+  data = lookup_specialization_data (gtype);
+  g_return_if_fail (data != NULL);
+
+  ((DBusGTypeSpecializedCollectionVtable *) data->klass->vtable)->iterator (gtype,
+                                                                           g_value_get_boxed (value),
+                                                                           iterator, user_data);
+}
+
+void
+dbus_g_type_map_value_iterate (GValue                                 *value,
+                              DBusGTypeSpecializedMapIterator         iterator,
+                              gpointer                                user_data)
+{
+  DBusGTypeSpecializedData *data;
+  GType gtype;
+
+  g_return_if_fail (specialized_types_is_initialized ());
+  g_return_if_fail (G_VALUE_HOLDS_BOXED (value));
+
+  gtype = G_VALUE_TYPE (value);
+  data = lookup_specialization_data (gtype);
+  g_return_if_fail (data != NULL);
+
+  ((DBusGTypeSpecializedMapVtable *) data->klass->vtable)->iterator (gtype,
+                                                                    g_value_get_boxed (value),
+                                                                    iterator, user_data);
+}
diff --git a/glib/dbus-gtype-specialized.h b/glib/dbus-gtype-specialized.h
new file mode 100644 (file)
index 0000000..5157f33
--- /dev/null
@@ -0,0 +1,104 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-gtype-specialized.h: Non-DBus-specific functions for specialized GTypes
+ *
+ * Copyright (C) 2005 Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef DBUS_GOBJECT_TYPE_SPECIALIZED_H
+#define DBUS_GOBJECT_TYPE_SPECIALIZED_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+GType          dbus_g_type_get_collection                   (const char *container,
+                                                            GType       specialization);
+GType          dbus_g_type_get_map                          (const char *container,
+                                                            GType       key_specialization,
+                                                            GType       value_specialization);
+gboolean       dbus_g_type_is_collection                    (GType       gtype);
+gboolean       dbus_g_type_is_map                           (GType       gtype);
+GType          dbus_g_type_get_collection_specialization    (GType       gtype);
+GType          dbus_g_type_get_map_key_specialization       (GType       gtype);
+GType          dbus_g_type_get_map_value_specialization     (GType       gtype);
+
+typedef void   (*DBusGTypeSpecializedCollectionIterator)    (const GValue *val,
+                                                            gpointer      user_data);
+typedef void   (*DBusGTypeSpecializedMapIterator)           (const GValue *key_val,
+                                                            const GValue *value_val,
+                                                            gpointer      user_data);
+
+gpointer       dbus_g_type_specialized_construct            (GType type);
+
+gboolean       dbus_g_type_collection_get_fixed             (GValue                                 *value,
+                                                            gpointer                               *data,
+                                                            guint                                  *len);
+
+void           dbus_g_type_collection_value_iterate         (GValue                                 *value,
+                                                            DBusGTypeSpecializedCollectionIterator  iterator,
+                                                            gpointer                                user_data);
+
+void           dbus_g_type_map_value_iterate                (GValue                                 *value,
+                                                            DBusGTypeSpecializedMapIterator         iterator,
+                                                            gpointer                                user_data);
+
+typedef gpointer (*DBusGTypeSpecializedConstructor) (GType type);
+typedef void     (*DBusGTypeSpecializedFreeFunc)    (GType type, gpointer val);
+typedef gpointer (*DBusGTypeSpecializedCopyFunc)    (GType type, gpointer src);
+
+typedef struct {
+  DBusGTypeSpecializedConstructor    constructor;
+  DBusGTypeSpecializedFreeFunc       free_func;
+  DBusGTypeSpecializedCopyFunc       copy_func;
+  gpointer                           padding1;
+  gpointer                           padding2;
+  gpointer                           padding3;
+} DBusGTypeSpecializedVtable;
+
+typedef gboolean (*DBusGTypeSpecializedCollectionFixedAccessorFunc) (GType type, gpointer instance, gpointer *values, guint *len);
+typedef void (*DBusGTypeSpecializedCollectionIteratorFunc) (GType type, gpointer instance, DBusGTypeSpecializedCollectionIterator iterator, gpointer user_data);
+
+typedef struct {
+  DBusGTypeSpecializedVtable                        base_vtable;
+  DBusGTypeSpecializedCollectionFixedAccessorFunc   fixed_accessor;
+  DBusGTypeSpecializedCollectionIteratorFunc        iterator;
+} DBusGTypeSpecializedCollectionVtable;
+
+typedef void (*DBusGTypeSpecializedMapIteratorFunc) (GType type, gpointer instance, DBusGTypeSpecializedMapIterator iterator, gpointer user_data);
+
+typedef struct {
+  DBusGTypeSpecializedVtable                        base_vtable;
+  DBusGTypeSpecializedMapIteratorFunc               iterator;
+} DBusGTypeSpecializedMapVtable;
+
+void           dbus_g_type_specialized_init           (void);
+
+void           dbus_g_type_register_collection        (const char                                   *name,
+                                                      const DBusGTypeSpecializedCollectionVtable   *vtable,
+                                                      guint                                         flags);
+  
+void           dbus_g_type_register_map               (const char                                   *name,
+                                                      const DBusGTypeSpecializedMapVtable          *vtable,
+                                                      guint                                         flags);
+
+G_END_DECLS
+
+#endif
diff --git a/glib/dbus-gvalue-utils.c b/glib/dbus-gvalue-utils.c
new file mode 100644 (file)
index 0000000..9eea4bf
--- /dev/null
@@ -0,0 +1,715 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-gvalue-utils.c: Non-DBus-specific functions related to GType/GValue 
+ *
+ * Copyright (C) 2005 Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "dbus/dbus-glib.h"
+#include "dbus-gvalue-utils.h"
+#include <glib.h>
+#include <string.h>
+#include <gobject/gvaluecollector.h>
+
+
+static guint
+fixed_type_get_size (GType type)
+{
+  switch (type)
+    {
+    case G_TYPE_CHAR:
+    case G_TYPE_UCHAR:
+      return sizeof (gchar);
+    case G_TYPE_BOOLEAN:
+      return sizeof (gboolean);
+    case G_TYPE_LONG:
+    case G_TYPE_ULONG:
+      return sizeof (glong);
+    case G_TYPE_INT:
+    case G_TYPE_UINT:
+      return sizeof (gint);
+    case G_TYPE_INT64:
+    case G_TYPE_UINT64:
+      return sizeof (gint64);
+    case G_TYPE_FLOAT:
+      return sizeof (gfloat);
+    case G_TYPE_DOUBLE:
+      return sizeof (gdouble);
+    default:
+      return 0;
+    }
+}
+
+gboolean
+dbus_g_type_is_fixed (GType type)
+{
+  return fixed_type_get_size (type) > 0;
+}
+
+guint
+dbus_g_type_fixed_get_size (GType type)
+{
+  g_assert (dbus_g_type_is_fixed (type));
+  return fixed_type_get_size (type);
+}
+
+gboolean
+dbus_gvalue_store (GValue          *value,
+                  gpointer        storage)
+{
+  /* FIXME - can we use the GValue lcopy_value method
+   * to do this in a cleaner way?
+   */
+  switch (g_type_fundamental (G_VALUE_TYPE (value)))
+    {
+    case G_TYPE_CHAR:
+      *((gchar *) storage) = g_value_get_char (value);
+      return TRUE;
+    case G_TYPE_UCHAR:
+      *((guchar *) storage) = g_value_get_uchar (value);
+      return TRUE;
+    case G_TYPE_BOOLEAN:
+      *((gboolean *) storage) = g_value_get_boolean (value);
+      return TRUE;
+    case G_TYPE_LONG:
+      *((glong *) storage) = g_value_get_long (value);
+      return TRUE;
+    case G_TYPE_ULONG:
+      *((gulong *) storage) = g_value_get_ulong (value);
+      return TRUE;
+    case G_TYPE_INT:
+      *((gint *) storage) = g_value_get_int (value);
+      return TRUE;
+    case G_TYPE_UINT:
+      *((guint *) storage) = g_value_get_uint (value);
+      return TRUE;
+    case G_TYPE_INT64:
+      *((gint64 *) storage) = g_value_get_int64 (value);
+      return TRUE;
+    case G_TYPE_UINT64:
+      *((guint64 *) storage) = g_value_get_uint64 (value);
+      return TRUE;
+    case G_TYPE_DOUBLE:
+      *((gdouble *) storage) = g_value_get_double (value);
+      return TRUE;
+    case G_TYPE_STRING:
+      *((gchar **) storage) = (char*) g_value_get_string (value);
+      return TRUE;
+    case G_TYPE_POINTER:
+      *((gpointer *) storage) = g_value_get_pointer (value);
+      return TRUE;
+    case G_TYPE_OBJECT:
+      *((gpointer *) storage) = g_value_get_object (value);
+      return TRUE;
+    case G_TYPE_BOXED:
+      *((gpointer *) storage) = g_value_get_boxed (value);
+      return TRUE;
+    default:
+      return FALSE;
+    }
+}
+
+gboolean
+dbus_gvalue_set_from_pointer (GValue          *value,
+                             gconstpointer    storage)
+{
+  /* FIXME - is there a better way to do this? */
+  switch (g_type_fundamental (G_VALUE_TYPE (value)))
+    {
+    case G_TYPE_CHAR:
+      g_value_set_char (value, *((gchar *) storage));
+      return TRUE;
+    case G_TYPE_UCHAR:
+      g_value_set_uchar (value, *((guchar *) storage));
+      return TRUE;
+    case G_TYPE_BOOLEAN:
+      g_value_set_boolean (value, *((gboolean *) storage));
+      return TRUE;
+    case G_TYPE_LONG:
+      g_value_set_long (value, *((glong *) storage));
+      return TRUE;
+    case G_TYPE_ULONG:
+      g_value_set_ulong (value, *((gulong *) storage));
+      return TRUE;
+    case G_TYPE_INT:
+      g_value_set_int (value, *((gint *) storage));
+      return TRUE;
+    case G_TYPE_UINT:
+      g_value_set_uint (value, *((guint *) storage));
+      return TRUE;
+    case G_TYPE_INT64:
+      g_value_set_int64 (value, *((gint64 *) storage));
+      return TRUE;
+    case G_TYPE_UINT64:
+      g_value_set_uint64 (value, *((guint64 *) storage));
+      return TRUE;
+    case G_TYPE_DOUBLE:
+      g_value_set_double (value, *((gdouble *) storage));
+      return TRUE;
+    case G_TYPE_STRING:
+      g_value_set_string (value, *((gchar **) storage));
+      return TRUE;
+    case G_TYPE_POINTER:
+      g_value_set_pointer (value, *((gpointer *) storage));
+      return TRUE;
+    case G_TYPE_OBJECT:
+      g_value_set_object (value, *((gpointer *) storage));
+      return TRUE;
+    case G_TYPE_BOXED:
+      g_value_set_boxed (value, *((gpointer *) storage));
+      return TRUE;
+    default:
+      return FALSE;
+    }
+}
+
+gboolean
+dbus_gvalue_take (GValue          *value,
+                 GTypeCValue     *cvalue)
+{
+  GType g_type;
+  GTypeValueTable *value_table;
+  char *error_msg;
+
+  g_type = G_VALUE_TYPE (value);
+  value_table = g_type_value_table_peek (g_type);
+
+  error_msg = value_table->collect_value (value, 1, cvalue, G_VALUE_NOCOPY_CONTENTS);
+  if (error_msg)
+    {
+      g_warning ("%s: %s", G_STRLOC, error_msg);
+      g_free (error_msg);
+      return FALSE;
+    }
+  /* Clear the NOCOPY_CONTENTS flag; we want to take ownership
+   * of the value.
+   */
+  value->data[1].v_uint &= ~(G_VALUE_NOCOPY_CONTENTS);
+  return TRUE;
+}
+
+static gboolean
+hash_func_from_gtype (GType gtype, GHashFunc *func)
+{
+  switch (gtype)
+    {
+    case G_TYPE_CHAR:
+    case G_TYPE_UCHAR:
+    case G_TYPE_BOOLEAN:
+    case G_TYPE_INT:
+    case G_TYPE_UINT:
+      *func = NULL;
+      return TRUE;
+    case G_TYPE_STRING:
+      *func = g_str_hash;
+      return TRUE;
+    default:
+      return FALSE;
+    }
+}
+
+static gboolean
+hash_free_from_gtype (GType gtype, GDestroyNotify *func)
+{
+  switch (gtype)
+    {
+    case G_TYPE_CHAR:
+    case G_TYPE_UCHAR:
+    case G_TYPE_BOOLEAN:
+    case G_TYPE_INT:
+    case G_TYPE_UINT:
+      *func = NULL;
+      return TRUE;
+    case G_TYPE_STRING:
+      *func = g_free;
+      return TRUE;
+    default:
+      if (gtype == G_TYPE_VALUE)
+       {
+         *func = (GDestroyNotify) g_value_unset;
+         return TRUE;
+       }
+      return FALSE;
+    }
+}
+
+gboolean
+dbus_gtype_is_valid_hash_key (GType type)
+{
+  GHashFunc func;
+  return hash_func_from_gtype (type, &func);
+}
+
+gboolean
+dbus_gtype_is_valid_hash_value (GType type)
+{
+  GDestroyNotify func;
+  return hash_free_from_gtype (type, &func);
+}
+
+GHashFunc
+dbus_g_hash_func_from_gtype (GType gtype)
+{
+  GHashFunc func;
+  gboolean ret;
+  ret = hash_func_from_gtype (gtype, &func);
+  g_assert (ret != FALSE);
+  return func;
+}
+
+GEqualFunc
+dbus_g_hash_equal_from_gtype (GType gtype)
+{
+  g_assert (dbus_gtype_is_valid_hash_key (gtype));
+
+  switch (gtype)
+    {
+    case G_TYPE_CHAR:
+    case G_TYPE_UCHAR:
+    case G_TYPE_BOOLEAN:
+    case G_TYPE_INT:
+    case G_TYPE_UINT:
+      return NULL;
+    case G_TYPE_STRING:
+      return g_str_equal;
+    default:
+      g_assert_not_reached ();
+      return NULL;
+    }
+}
+
+GDestroyNotify
+dbus_g_hash_free_from_gtype (GType gtype)
+{
+  GDestroyNotify func;
+  gboolean ret;
+  ret = hash_free_from_gtype (gtype, &func);
+  g_assert (ret != FALSE);
+  return func;
+}
+
+static void
+gvalue_from_hash_value (GValue *value, gpointer instance)
+{
+  switch (g_type_fundamental (G_VALUE_TYPE (value)))
+    {
+    case G_TYPE_CHAR:
+      g_value_set_char (value, (gchar) GPOINTER_TO_INT (instance));
+      break;
+    case G_TYPE_UCHAR:
+      g_value_set_uchar (value, (guchar) GPOINTER_TO_UINT (instance));
+      break;
+    case G_TYPE_BOOLEAN:
+      g_value_set_boolean (value, (gboolean) GPOINTER_TO_UINT (instance));
+      break;
+    case G_TYPE_INT:
+      g_value_set_int (value, GPOINTER_TO_INT (instance));
+      break;
+    case G_TYPE_UINT:
+      g_value_set_uint (value, GPOINTER_TO_UINT (instance));
+      break;
+    case G_TYPE_STRING:
+      g_value_set_static_string (value, instance);
+      break;
+    case G_TYPE_POINTER:
+      g_value_set_pointer (value, instance);
+      break;
+    case G_TYPE_BOXED:
+      g_value_set_static_boxed (value, instance);
+      break;
+    case G_TYPE_OBJECT:
+      g_value_set_object (value, instance);
+      g_object_unref (g_value_get_object (value));
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+}
+
+static gpointer
+hash_value_from_gvalue (GValue *value)
+{
+  switch (g_type_fundamental (G_VALUE_TYPE (value)))
+    {
+    case G_TYPE_CHAR:
+      return GINT_TO_POINTER ((int) g_value_get_char (value));
+      break;
+    case G_TYPE_UCHAR:
+      return GUINT_TO_POINTER ((guint) g_value_get_uchar (value));
+      break;
+    case G_TYPE_BOOLEAN:
+      return GUINT_TO_POINTER ((guint) g_value_get_boolean (value));
+      break;
+    case G_TYPE_INT:
+      return GINT_TO_POINTER (g_value_get_int (value));
+      break;
+    case G_TYPE_UINT:
+      return GUINT_TO_POINTER (g_value_get_uint (value));
+      break;
+    case G_TYPE_STRING:
+      return (gpointer) g_value_get_string (value);
+      break;
+    case G_TYPE_POINTER:
+      return g_value_get_pointer (value);
+      break;
+    case G_TYPE_BOXED:
+      return g_value_get_boxed (value);
+      break;
+    case G_TYPE_OBJECT:
+      return g_value_get_object (value);
+      break;
+    default:
+      g_assert_not_reached ();
+      return NULL;
+    }
+}
+
+struct DBusGHashTableValueForeachData
+{
+  DBusGTypeSpecializedMapIterator func;
+  GType key_type;
+  GType value_type;
+  gpointer data;
+};
+
+static void
+hashtable_foreach_with_values (gpointer key, gpointer value, gpointer user_data)
+{
+  GValue key_val = {0, };
+  GValue value_val = {0, };
+  struct DBusGHashTableValueForeachData *data = user_data;
+  
+  g_value_init (&key_val, data->key_type);
+  g_value_init (&value_val, data->value_type);
+  gvalue_from_hash_value (&key_val, key);
+  gvalue_from_hash_value (&value_val, value);
+
+  data->func (&key_val, &value_val, data->data);
+}
+
+
+static void
+hashtable_iterator (GType                           hash_type,
+                   gpointer                        instance,
+                   DBusGTypeSpecializedMapIterator iterator,
+                   gpointer                        user_data)
+{
+  struct DBusGHashTableValueForeachData data;
+  GType key_gtype;
+  GType value_gtype;
+
+  key_gtype = dbus_g_type_get_map_key_specialization (hash_type);
+  value_gtype = dbus_g_type_get_map_value_specialization (hash_type);
+
+  data.func = iterator;
+  data.key_type = key_gtype;
+  data.value_type = value_gtype;
+  data.data = user_data;
+
+  g_hash_table_foreach (instance, hashtable_foreach_with_values, &data);
+}
+
+void
+dbus_g_hash_table_insert_steal_values (GHashTable *table,
+                                      GValue     *key_val,
+                                      GValue     *value_val)
+{
+  gpointer key, val;
+  
+  key = hash_value_from_gvalue (key_val);
+  val = hash_value_from_gvalue (value_val);
+
+  g_hash_table_insert (table, key, val);
+}
+
+static gpointer
+hashtable_constructor (GType type)
+{
+  GHashTable *ret;
+  GType key_gtype;
+  GType value_gtype;
+
+  key_gtype = dbus_g_type_get_map_key_specialization (type);
+  value_gtype = dbus_g_type_get_map_value_specialization (type);
+
+  ret = g_hash_table_new_full (dbus_g_hash_func_from_gtype (key_gtype),
+                              dbus_g_hash_equal_from_gtype (key_gtype),
+                              dbus_g_hash_free_from_gtype (key_gtype),
+                              dbus_g_hash_free_from_gtype (value_gtype));
+  return ret;
+}
+
+static void
+hashtable_insert_values (GHashTable       *table,
+                        const GValue     *key_val,
+                        const GValue     *value_val)
+{
+  GValue key_copy = {0, };
+  GValue value_copy = {0, };
+
+  g_value_init (&key_copy, G_VALUE_TYPE (key_val));
+  g_value_copy (key_val, &key_copy);
+  g_value_init (&value_copy, G_VALUE_TYPE (value_val));
+  g_value_copy (value_val, &value_copy);
+  
+  dbus_g_hash_table_insert_steal_values (table, &key_copy, &value_copy);
+}
+
+static void
+hashtable_foreach_copy (const GValue *key, const GValue *val, gpointer data)
+{
+  hashtable_insert_values ((GHashTable *) data, key, val);
+}
+
+static gpointer
+hashtable_copy (GType type, gpointer src)
+{
+  GHashTable *ghash;
+  GHashTable *ret;
+  GValue hashval = {0,};
+
+  ghash = src;
+
+  ret = hashtable_constructor (type);
+
+  g_value_init (&hashval, type);
+  g_value_set_static_boxed (&hashval, ghash); 
+  dbus_g_type_map_value_iterate (&hashval, hashtable_foreach_copy, ret);
+  return ret;
+}
+
+static void
+hashtable_free (GType type, gpointer val)
+{
+  g_hash_table_destroy (val);
+}
+
+static gpointer
+array_constructor (GType type)
+{
+  GArray *array;
+  guint elt_size;
+  GType elt_type;
+  gboolean zero_terminated;
+  gboolean clear;
+
+  elt_type = dbus_g_type_get_collection_specialization (type);
+  g_assert (elt_type != G_TYPE_INVALID);
+
+  elt_size = dbus_g_type_fixed_get_size (elt_type);
+
+  /* These are "safe" defaults */ 
+  zero_terminated = TRUE; /* ((struct _DBusGRealArray*) garray)->zero_terminated; */
+  clear = TRUE; /* ((struct _DBusGRealArray*) garray)->clear; */
+
+  array = g_array_new (zero_terminated, clear, elt_size);
+  return array;
+}
+
+static gpointer
+array_copy (GType type, gpointer src)
+{
+  GArray *garray;
+  GArray *new;
+
+  garray = src;
+
+  new = array_constructor (type);
+  g_array_append_vals (new, garray->data, garray->len);
+
+  return new;
+}
+
+static void
+array_free (GType type, gpointer val)
+{
+  GArray *array;
+  array = val;
+  g_array_free (array, TRUE);
+}
+
+static gboolean
+array_fixed_accessor (GType type, gpointer instance, gpointer *values, guint *len)
+{
+  GType elt_type;
+  GArray *array = instance;
+
+  elt_type = dbus_g_type_get_collection_specialization (type);
+  if (!dbus_g_type_is_fixed (elt_type))
+    return FALSE;
+
+  *values = array->data;
+  *len = array->len;
+  return TRUE;
+}
+
+static gpointer
+ptrarray_constructor (GType type)
+{
+  /* Later we should determine a destructor, need g_ptr_array_destroy */
+  return g_ptr_array_new ();
+}
+
+static void
+gvalue_from_ptrarray_value (GValue *value, gpointer instance)
+{
+  switch (g_type_fundamental (G_VALUE_TYPE (value)))
+    {
+    case G_TYPE_POINTER:
+      g_value_set_pointer (value, instance);
+      break;
+    case G_TYPE_BOXED:
+      g_value_set_static_boxed (value, instance);
+      break;
+    case G_TYPE_OBJECT:
+      g_value_set_object (value, instance);
+      g_object_unref (g_value_get_object (value));
+      break;
+    default:
+      g_assert_not_reached ();
+      break;
+    }
+}
+
+static gpointer
+ptrarray_value_from_gvalue (const GValue *value)
+{
+  switch (g_type_fundamental (G_VALUE_TYPE (value)))
+    {
+    case G_TYPE_POINTER:
+      return g_value_get_pointer (value);
+      break;
+    case G_TYPE_BOXED:
+      return g_value_get_boxed (value);
+      break;
+    case G_TYPE_OBJECT:
+      return g_value_get_object (value);
+      break;
+    default:
+      g_assert_not_reached ();
+      return NULL;
+    }
+}
+
+static void
+ptrarray_iterator (GType                                   hash_type,
+                  gpointer                                instance,
+                  DBusGTypeSpecializedCollectionIterator  iterator,
+                  gpointer                                user_data)
+{
+  GPtrArray *ptrarray;
+  GType elt_gtype;
+  guint i;
+
+  ptrarray = instance;
+
+  elt_gtype = dbus_g_type_get_collection_specialization (hash_type);
+
+  for (i = 0; i < ptrarray->len; i++)
+    {
+      GValue val = {0, };
+      g_value_init (&val, elt_gtype);
+      gvalue_from_ptrarray_value (&val, g_ptr_array_index (ptrarray, i));
+      iterator (&val, user_data);
+    }
+}
+
+static void
+ptrarray_copy_elt (const GValue *val, gpointer user_data)
+{
+  GPtrArray *dest = user_data;
+  GValue val_copy = {0, }; 
+  
+  g_value_init (&val_copy, G_VALUE_TYPE (val));
+  g_value_copy (val, &val_copy);
+
+  g_ptr_array_add (dest, ptrarray_value_from_gvalue (&val_copy));
+}
+
+static gpointer
+ptrarray_copy (GType type, gpointer src)
+{
+  GPtrArray *new;
+  GValue array_val = {0, };
+
+  g_value_init (&array_val, type);
+  g_value_set_static_boxed (&array_val, src);
+
+  new = ptrarray_constructor (type);
+  dbus_g_type_collection_value_iterate (&array_val, ptrarray_copy_elt, new);
+
+  return new;
+}
+
+static void
+ptrarray_free (GType type, gpointer val)
+{
+  GArray *array;
+  array = val;
+  g_array_free (array, TRUE);
+}
+
+void
+dbus_g_type_specialized_builtins_init (void)
+{
+  static const DBusGTypeSpecializedCollectionVtable array_vtable = {
+    {
+      array_constructor,
+      array_free,
+      array_copy,
+      NULL,
+      NULL,
+      NULL
+    },
+    array_fixed_accessor,
+    NULL
+  };
+
+  dbus_g_type_register_collection ("GArray", &array_vtable, 0);
+
+  static const DBusGTypeSpecializedCollectionVtable ptrarray_vtable = {
+    {
+      ptrarray_constructor,
+      ptrarray_free,
+      ptrarray_copy,
+      NULL,
+      NULL,
+      NULL
+    },
+    NULL,
+    ptrarray_iterator
+  };
+
+  dbus_g_type_register_collection ("GPtrArray", &ptrarray_vtable, 0);
+
+  static const DBusGTypeSpecializedMapVtable hashtable_vtable = {
+    {
+      hashtable_constructor,
+      hashtable_free,
+      hashtable_copy,
+      NULL,
+      NULL,
+      NULL
+    },
+    hashtable_iterator
+  };
+
+  dbus_g_type_register_map ("GHashTable", &hashtable_vtable, 0);
+}
diff --git a/glib/dbus-gvalue-utils.h b/glib/dbus-gvalue-utils.h
new file mode 100644 (file)
index 0000000..781569f
--- /dev/null
@@ -0,0 +1,70 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* dbus-gvalue-utils.h: Non-DBus-specific functions related to GType/GValue
+ *
+ * Copyright (C) 2005 Red Hat, Inc.
+ *
+ * Licensed under the Academic Free License version 2.1
+ * 
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef DBUS_GOBJECT_VALUE_UTILS_H
+#define DBUS_GOBJECT_VALUE_UTILS_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+void           dbus_g_type_specialized_builtins_init (void);
+
+gboolean       dbus_g_type_is_fixed                  (GType gtype); 
+guint          dbus_g_type_fixed_get_size            (GType gtype); 
+
+gboolean       dbus_gvalue_set_from_pointer          (GValue *value,
+                                                     gconstpointer storage);
+
+typedef void (*DBusGHashValueForeachFunc) (GValue * key, GValue *val, gpointer data);
+
+void           dbus_g_hash_table_value_foreach       (GHashTable                *table,
+                                                     GType                      hash_type,
+                                                     DBusGHashValueForeachFunc  func,
+                                                     gpointer                   data);
+
+void           dbus_g_hash_table_insert_values       (GHashTable                *table,
+                                                     GValue                    *key_val,
+                                                     GValue                    *value_val);
+void           dbus_g_hash_table_insert_steal_values (GHashTable *table,
+                                                     GValue     *key_val,
+                                                     GValue     *value_val);
+
+gboolean       dbus_gtype_is_valid_hash_key          (GType type);
+gboolean       dbus_gtype_is_valid_hash_value        (GType type);
+
+GHashFunc      dbus_g_hash_func_from_gtype           (GType gtype);
+GEqualFunc     dbus_g_hash_equal_from_gtype          (GType gtype);
+GDestroyNotify dbus_g_hash_free_from_gtype           (GType gtype);
+
+gboolean       dbus_gvalue_store                     (GValue          *value,
+                                                     gpointer         storage);
+
+gboolean       dbus_gvalue_take                      (GValue          *value,
+                                                     GTypeCValue     *cvalue);
+
+
+G_END_DECLS
+
+#endif
index eae5a45..8506330 100644 (file)
  *
  */
 
-#include <dbus-gvalue.h>
+#include "dbus-gvalue.h"
+#include "dbus-gobject.h"
+#include "dbus-gvalue-utils.h"
+#include "dbus/dbus-glib.h"
+#include <string.h>
+#include <glib.h>
+#include <glib/gi18n.h>
 #include "dbus/dbus-signature.h"
 
-/* This is slightly evil, we don't use g_value_set_foo() functions */
+static gboolean demarshal_static_variant (DBusGValueMarshalCtx    *context,
+                                         DBusMessageIter         *iter,
+                                         GValue                  *value,
+                                         GError                 **error);
+static gpointer dbus_g_value_copy (gpointer value);
+
+
+struct DBusGValue
+{
+  enum {
+    DBUS_G_VALUE_TYPE_TOPLEVEL,
+    DBUS_G_VALUE_TYPE_ITERATOR
+  } type;
+  union {
+    struct {
+      DBusGConnection        *connection;
+      DBusGProxy             *proxy;
+      DBusMessage            *message;
+      char                   *signature;
+    } toplevel;
+    struct {
+      DBusGValue             *toplevel;
+      DBusMessageIter         iterator;
+    } recurse;
+  } value;
+};
+
+static gboolean marshal_basic                   (DBusMessageIter           *iter,
+                                                GValue                    *value);
+static gboolean demarshal_basic                 (DBusGValueMarshalCtx      *context,
+                                                DBusMessageIter           *iter,
+                                                GValue                    *value,
+                                                GError                   **error);
+static gboolean marshal_strv                    (DBusMessageIter           *iter,
+                                                GValue                    *value);
+static gboolean demarshal_strv                  (DBusGValueMarshalCtx      *context,
+                                                DBusMessageIter           *iter,
+                                                GValue                    *value,
+                                                GError                   **error);
+static gboolean marshal_variant                 (DBusMessageIter           *iter,
+                                                GValue                    *value);
+static gboolean demarshal_variant               (DBusGValueMarshalCtx      *context,
+                                                DBusMessageIter           *iter,
+                                                GValue                    *value,
+                                                GError                   **error);
+static gboolean marshal_garray_basic            (DBusMessageIter           *iter,
+                                                GValue                    *value);
+static gboolean demarshal_garray_basic          (DBusGValueMarshalCtx      *context,
+                                                DBusMessageIter           *iter,
+                                                GValue                    *value,
+                                                GError                   **error);
+static gboolean marshal_proxy                   (DBusMessageIter           *iter,
+                                                GValue                    *value);
+static gboolean demarshal_proxy                 (DBusGValueMarshalCtx      *context,
+                                                DBusMessageIter           *iter,
+                                                GValue                    *value,
+                                                GError                   **error);
+static gboolean marshal_object                  (DBusMessageIter           *iter,
+                                                GValue                    *value);
+static gboolean demarshal_object                (DBusGValueMarshalCtx      *context,
+                                                DBusMessageIter           *iter,
+                                                GValue                    *value,
+                                                GError                   **error);
+static gboolean marshal_proxy_array             (DBusMessageIter           *iter,
+                                                GValue                    *value);
+static gboolean demarshal_proxy_array           (DBusGValueMarshalCtx      *context,
+                                                DBusMessageIter           *iter,
+                                                GValue                    *value,
+                                                GError                   **error);
+static gboolean marshal_map                     (DBusMessageIter           *iter,
+                                                GValue                    *value);
+static gboolean demarshal_ghashtable            (DBusGValueMarshalCtx      *context,
+                                                DBusMessageIter           *iter,
+                                                GValue                    *value,
+                                                GError                   **error);
+
+typedef gboolean (*DBusGValueMarshalFunc)       (DBusMessageIter           *iter,
+                                                GValue                    *value);
+typedef gboolean (*DBusGValueDemarshalFunc)     (DBusGValueMarshalCtx      *context,
+                                                DBusMessageIter           *iter,
+                                                GValue                    *value,
+                                                GError                   **error);
+
+typedef struct {
+  DBusGValueMarshalFunc       marshaller;
+  DBusGValueDemarshalFunc     demarshaller;
+} DBusGTypeMarshalVtable;
+
+typedef struct {
+  const char                       *sig;
+  const DBusGTypeMarshalVtable     *vtable;
+} DBusGTypeMarshalData;
+
+static GQuark
+dbus_g_type_metadata_data_quark ()
+{
+  static GQuark quark;
+  if (!quark)
+    quark = g_quark_from_static_string ("DBusGTypeMetaData");
+  
+  return quark;
+}
+
+static void
+set_type_metadata (GType type, const DBusGTypeMarshalData *data)
+{
+  g_type_set_qdata (type, dbus_g_type_metadata_data_quark (), (gpointer) data);
+}
+
 #define MAP_BASIC(d_t, g_t)                     \
     case DBUS_TYPE_##d_t:                       \
       return G_TYPE_##g_t;
-
 static GType
-dbus_dbus_type_to_gtype (int type)
+typecode_to_gtype (int type)
 {
   switch (type)
     {
       MAP_BASIC (BOOLEAN, BOOLEAN);
       MAP_BASIC (BYTE,    UCHAR);
+      MAP_BASIC (INT16,   INT);
       MAP_BASIC (INT32,   INT);
+      MAP_BASIC (UINT16,  UINT);
       MAP_BASIC (UINT32,  UINT);
       MAP_BASIC (INT64,   INT64);
       MAP_BASIC (UINT64,  UINT64);
       MAP_BASIC (DOUBLE,  DOUBLE);
-    case DBUS_TYPE_INT16:
-      return G_TYPE_INT;
-    case DBUS_TYPE_UINT16:
-      return G_TYPE_UINT;
-    case DBUS_TYPE_STRING:
-    case DBUS_TYPE_OBJECT_PATH:
-    case DBUS_TYPE_SIGNATURE:
-      return G_TYPE_STRING;
-    case DBUS_TYPE_STRUCT:
-    case DBUS_TYPE_ARRAY:
-    case DBUS_TYPE_VARIANT:
+      MAP_BASIC (STRING,  STRING);
     default:
       return G_TYPE_INVALID;
     }
 }
-
 #undef MAP_BASIC
 
-gboolean
-dbus_gvalue_init (int     type,
-                 GValue *value)
+static gboolean
+dbus_typecode_maps_to_basic (int typecode)
+{
+  return typecode_to_gtype (typecode) != G_TYPE_INVALID;
+}
+
+static gboolean
+basic_typecode_to_gtype (int typecode)
+{
+  g_assert (dbus_type_is_basic (typecode));
+  g_assert (dbus_typecode_maps_to_basic (typecode));
+  return typecode_to_gtype (typecode);
+}
+
+static void
+register_basic (int typecode, const DBusGTypeMarshalData *typedata)
 {
+  set_type_metadata (basic_typecode_to_gtype (typecode), typedata);
+}
+
+static void
+register_array (int typecode, const DBusGTypeMarshalData *typedata)
+{
+  GType elt_gtype;
   GType gtype;
 
-  gtype = dbus_dbus_type_to_gtype (type);
-  if (gtype == G_TYPE_INVALID)
-    return FALSE;
-  g_value_init (value, gtype);
-  return TRUE;
+  elt_gtype = basic_typecode_to_gtype (typecode); 
+  gtype = dbus_g_type_get_collection ("GArray", elt_gtype);
+  set_type_metadata (gtype, typedata); 
+}
+
+static void
+register_dict (int key_type, int value_type, const DBusGTypeMarshalData *typedata)
+{
+  GType key_gtype;
+  GType value_gtype;
+  GType gtype;
+
+  key_gtype = basic_typecode_to_gtype (key_type); 
+  value_gtype = basic_typecode_to_gtype (value_type); 
+  gtype = dbus_g_type_get_map ("GHashTable", key_gtype, value_gtype);
+  set_type_metadata (gtype, typedata); 
+}
+
+void
+dbus_g_value_types_init (void)
+{
+  static gboolean types_initialized;
+
+
+  if (types_initialized)
+    return;
+
+  g_assert (sizeof (DBusGValueIterator) >= sizeof (DBusMessageIter));
+
+  dbus_g_type_specialized_init ();
+  dbus_g_type_specialized_builtins_init ();
+  
+  static const DBusGTypeMarshalVtable basic_vtable = {
+    marshal_basic,
+    demarshal_basic
+  };
+  static const DBusGTypeMarshalVtable garray_basic_vtable = {
+    marshal_garray_basic,
+    demarshal_garray_basic
+  };
+  static const DBusGTypeMarshalVtable ghashtable_vtable = {
+    marshal_map,
+    demarshal_ghashtable
+  };
+
+  /* Register basic types */
+  {
+    static const DBusGTypeMarshalData typedata = {
+      DBUS_TYPE_BOOLEAN_AS_STRING,
+      &basic_vtable,
+    };
+    register_basic (DBUS_TYPE_BOOLEAN, &typedata);
+  }
+  {
+    static const DBusGTypeMarshalData typedata = {
+      DBUS_TYPE_BYTE_AS_STRING,
+      &basic_vtable,
+    };
+    register_basic (DBUS_TYPE_BYTE, &typedata);
+  }
+  {
+    static const DBusGTypeMarshalData typedata = {
+      DBUS_TYPE_INT16_AS_STRING,
+      &basic_vtable,
+    };
+    register_basic (DBUS_TYPE_INT16, &typedata);
+  }
+  {
+    static const DBusGTypeMarshalData typedata = {
+      DBUS_TYPE_UINT16_AS_STRING,
+      &basic_vtable,
+    };
+    register_basic (DBUS_TYPE_UINT16, &typedata);
+  }
+  {
+    static const DBusGTypeMarshalData typedata = {
+      DBUS_TYPE_UINT32_AS_STRING,
+      &basic_vtable,
+    };
+    register_basic (DBUS_TYPE_UINT32, &typedata);
+  }
+  {
+    static const DBusGTypeMarshalData typedata = {
+      DBUS_TYPE_INT32_AS_STRING,
+      &basic_vtable,
+    };
+    register_basic (DBUS_TYPE_INT32, &typedata);
+  }
+  {
+    static const DBusGTypeMarshalData typedata = {
+      DBUS_TYPE_UINT64_AS_STRING,
+      &basic_vtable,
+    };
+    register_basic (DBUS_TYPE_UINT64, &typedata);
+  }
+  {
+    static const DBusGTypeMarshalData typedata = {
+      DBUS_TYPE_INT64_AS_STRING,
+      &basic_vtable,
+    };
+    register_basic (DBUS_TYPE_INT64, &typedata);
+  }
+  {
+    static const DBusGTypeMarshalData typedata = {
+      DBUS_TYPE_DOUBLE_AS_STRING,
+      &basic_vtable,
+    };
+    register_basic (DBUS_TYPE_DOUBLE, &typedata);
+  }
+  {
+    static const DBusGTypeMarshalData typedata = {
+      DBUS_TYPE_STRING_AS_STRING,
+      &basic_vtable,
+    };
+    register_basic (DBUS_TYPE_STRING, &typedata);
+  }
+  
+  /* Register complex types with builtin GType mappings */
+  {
+    static const DBusGTypeMarshalVtable vtable = {
+      marshal_variant,
+      demarshal_variant
+    };
+    static const DBusGTypeMarshalData typedata = {
+      DBUS_TYPE_VARIANT_AS_STRING,
+      &vtable
+    };
+    set_type_metadata (G_TYPE_VALUE, &typedata);
+  };
+  {
+    static const DBusGTypeMarshalVtable vtable = {
+      marshal_strv,
+      demarshal_strv
+    };
+    static const DBusGTypeMarshalData typedata = {
+      DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_STRING_AS_STRING,
+      &vtable
+    };
+    set_type_metadata (G_TYPE_STRV, &typedata);
+  };
+
+  { 
+    static const DBusGTypeMarshalData typedata = {
+      DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BOOLEAN_AS_STRING,
+      &garray_basic_vtable
+    };
+    register_array (DBUS_TYPE_BOOLEAN, &typedata);
+  }
+  { 
+    static const DBusGTypeMarshalData typedata = {
+      DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_BYTE_AS_STRING,
+      &garray_basic_vtable
+    };
+    register_array (DBUS_TYPE_BYTE, &typedata);
+  }
+  { 
+    static const DBusGTypeMarshalData typedata = {
+      DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_UINT32_AS_STRING,
+      &garray_basic_vtable
+    };
+    register_array (DBUS_TYPE_UINT32, &typedata);
+  }
+  { 
+    static const DBusGTypeMarshalData typedata = {
+      DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_INT32_AS_STRING,
+      &garray_basic_vtable
+    };
+    register_array (DBUS_TYPE_INT32, &typedata);
+  }
+  { 
+    static const DBusGTypeMarshalData typedata = {
+      DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_UINT64_AS_STRING,
+      &garray_basic_vtable
+    };
+    register_array (DBUS_TYPE_UINT64, &typedata);
+  }
+  { 
+    static const DBusGTypeMarshalData typedata = {
+      DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_INT64_AS_STRING,
+      &garray_basic_vtable
+    };
+    register_array (DBUS_TYPE_INT64, &typedata);
+  }
+  { 
+    static const DBusGTypeMarshalData typedata = {
+      DBUS_TYPE_ARRAY_AS_STRING DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+      &ghashtable_vtable,
+    };
+    register_dict (DBUS_TYPE_STRING, DBUS_TYPE_STRING, &typedata);
+  }
+
+  {
+    static const DBusGTypeMarshalVtable vtable = {
+      marshal_proxy,
+      demarshal_proxy
+    };
+    static const DBusGTypeMarshalData typedata = {
+      DBUS_TYPE_OBJECT_PATH_AS_STRING,
+      &vtable
+    };
+    set_type_metadata (DBUS_TYPE_G_PROXY, &typedata);
+  }
+
+  {
+    static const DBusGTypeMarshalVtable vtable = {
+      marshal_object,
+      demarshal_object
+    };
+    static const DBusGTypeMarshalData typedata = {
+      DBUS_TYPE_OBJECT_PATH_AS_STRING,
+      &vtable
+    };
+    set_type_metadata (G_TYPE_OBJECT, &typedata);
+  }
+
+  {
+    static const DBusGTypeMarshalVtable vtable = {
+      marshal_proxy_array,
+      demarshal_proxy_array
+    };
+    static const DBusGTypeMarshalData typedata = {
+      DBUS_TYPE_ARRAY_AS_STRING DBUS_TYPE_OBJECT_PATH_AS_STRING,
+      &vtable
+    };
+    set_type_metadata (DBUS_TYPE_G_PROXY_ARRAY, &typedata);
+  }
+
+  types_initialized = TRUE;
 }
 
-/* FIXME - broken for containers
+/**
+ * Get the GLib type ID for a DBusGValue boxed type.
+ *
+ * @returns GLib type
  */
-static int
-base_type_from_signature  (const char *signature)
+GType
+dbus_g_value_get_g_type (void)
 {
-  DBusSignatureIter iter;
+  static GType type_id = 0;
 
-  dbus_signature_iter_init (&iter, signature);
+  if (!type_id)
+    type_id = g_boxed_type_register_static ("DBusGValue",
+                                           dbus_g_value_copy,
+                                           (GBoxedFreeFunc) dbus_g_value_free);
+  return type_id;
+}
+
+void
+dbus_g_value_open (DBusGValue          *value,
+                  DBusGValueIterator  *iter)
+{
+  DBusGValue *real;
+
+  g_return_if_fail (value->type == DBUS_G_VALUE_TYPE_TOPLEVEL);
+
+  real = (DBusGValue*) iter;
+  real->type = DBUS_G_VALUE_TYPE_ITERATOR;
+  real->value.recurse.toplevel = value;
 
-  return dbus_signature_iter_get_current_type (&iter);
+  dbus_message_iter_init (value->value.toplevel.message,
+                         &(real->value.recurse.iterator));
+  value->value.recurse.toplevel = value;
 }
 
-const char *
-dbus_gvalue_genmarshal_name_from_type (const char *signature)
+gboolean
+dbus_g_value_iterator_get_values (DBusGValueIterator *iter,
+                                 GError            **error,
+                                 GValue             *first_val,
+                                 ...)
 {
-  int type;
+  GValue *value;
+  va_list args;
+  DBusGValue *iterval;
 
-  type = base_type_from_signature (signature);
-  switch (type)
+  va_start (args, first_val);
+
+  iterval = (DBusGValue *) iter;
+
+  value = first_val;
+  do
     {
-    case DBUS_TYPE_BOOLEAN:
-      return "BOOLEAN";
-    case DBUS_TYPE_BYTE:
-      return "UCHAR";
-    case DBUS_TYPE_INT32:
-      return "INT";
-    case DBUS_TYPE_UINT32:
-      return "UINT";
-    case DBUS_TYPE_INT64:
-      return "INT64";
-    case DBUS_TYPE_UINT64:
-      return "UINT64";
-    case DBUS_TYPE_DOUBLE:
-      return "DOUBLE";
-    case DBUS_TYPE_INT16:
-      return "INT";
-      break;
-    case DBUS_TYPE_UINT16:
-      return "UINT";
-    case DBUS_TYPE_STRING:
-    case DBUS_TYPE_OBJECT_PATH:
-    case DBUS_TYPE_SIGNATURE:
-      return "STRING";
-      
-    case DBUS_TYPE_STRUCT:
-    case DBUS_TYPE_ARRAY:
-    case DBUS_TYPE_VARIANT:
-      return NULL;
-    }
+      DBusGValueMarshalCtx context;
+
+      context.gconnection = (iterval->value.recurse.toplevel)->value.toplevel.connection;
+      context.proxy = (iterval->value.recurse.toplevel)->value.toplevel.proxy;
+      if (!dbus_gvalue_demarshal (&context,
+                                 &(iterval->value.recurse.iterator),
+                                 value,
+                                 error))
+       return FALSE;
+    } while ((value = va_arg (args, GValue *)) != NULL);
+  
+  return TRUE;
+}
+
+static char *
+dbus_g_value_get_signature (DBusGValue *value)
+{
+  return value->value.toplevel.signature;
+}
+
+static gpointer
+dbus_g_value_copy (gpointer value)
+{
+  /* FIXME */
   return NULL;
 }
 
-const char *
-dbus_gvalue_ctype_from_type (const char *signature, gboolean in)
+void
+dbus_g_value_free (DBusGValue *value)
+{
+  if (value->type == DBUS_G_VALUE_TYPE_TOPLEVEL)
+    {
+      dbus_message_unref (value->value.toplevel.message);
+      g_free (value->value.toplevel.signature);
+    }
+}
+
+static GType
+signature_iter_to_g_type_dict (const DBusSignatureIter *subiter, gboolean is_client)
 {
-  int type;
+  DBusSignatureIter iter;
+  GType key_gtype;
+  GType value_gtype;
 
-  type = base_type_from_signature (signature);
+  g_assert (dbus_signature_iter_get_current_type (subiter) == DBUS_TYPE_DICT_ENTRY);
 
-  switch (type)
+  dbus_signature_iter_recurse (subiter, &iter);
+
+  key_gtype = dbus_gtype_from_signature_iter (&iter, is_client); 
+  if (key_gtype == G_TYPE_INVALID)
+    return G_TYPE_INVALID;
+
+  dbus_signature_iter_next (&iter);
+  value_gtype = dbus_gtype_from_signature_iter (&iter, is_client);
+  if (value_gtype == G_TYPE_INVALID)
+    return G_TYPE_INVALID;
+
+  if (!dbus_gtype_is_valid_hash_key (key_gtype)
+      || !dbus_gtype_is_valid_hash_value (value_gtype))
+    /* Later we need to return DBUS_TYPE_G_VALUE */
+    return G_TYPE_INVALID; 
+
+  return dbus_g_type_get_map ("GHashTable", key_gtype, value_gtype);
+}
+
+static GType
+signature_iter_to_g_type_array (DBusSignatureIter *iter, gboolean is_client)
+{
+  GType elt_gtype;
+  DBusGTypeMarshalData *typedata;
+
+  elt_gtype = dbus_gtype_from_signature_iter (iter, is_client);
+  if (elt_gtype == G_TYPE_INVALID)
+    return G_TYPE_INVALID;
+
+  typedata = g_type_get_qdata (elt_gtype, dbus_g_type_metadata_data_quark ());
+  if (typedata == NULL)
+    return G_TYPE_INVALID;
+  
+  if (elt_gtype == G_TYPE_OBJECT)
+    return DBUS_TYPE_G_OBJECT_ARRAY;
+  if (elt_gtype == G_TYPE_STRING)
+    return G_TYPE_STRV;
+  if (dbus_g_type_is_fixed (elt_gtype))
+    return dbus_g_type_get_collection ("GArray", elt_gtype);
+
+  /* Later we need to return DBUS_TYPE_G_VALUE */
+  return G_TYPE_INVALID; 
+}
+
+static gboolean
+signature_iter_to_g_type_struct (DBusSignatureIter *origiter, gboolean is_client)
+{
+  /* FIXME allow structures */
+  return G_TYPE_INVALID;
+#if 0
+  DBusSignatureIter iter;
+  int current_type;
+
+  iter = *origiter;
+
+  while ((current_type = dbus_signature_iter_get_current_type (&iter)) != DBUS_TYPE_INVALID) {
+    subtype = dbus_gtype_from_signature_iter (&iter, is_client);
+    if (subtype == G_TYPE_INVALID)
+      return G_TYPE_INVALID;
+  }
+  return DBUS_TYPE_G_VALUE ();
+#endif
+}
+
+GType
+dbus_gtype_from_signature_iter (DBusSignatureIter *iter, gboolean is_client)
+{
+  int current_type;
+
+  current_type = dbus_signature_iter_get_current_type (iter);
+  /* TODO: handle type 0? */
+  if (dbus_typecode_maps_to_basic (current_type))
+    return basic_typecode_to_gtype (current_type);
+  else if (current_type == DBUS_TYPE_OBJECT_PATH)
+    return is_client ? DBUS_TYPE_G_PROXY : G_TYPE_OBJECT;
+  else
     {
-    case DBUS_TYPE_BOOLEAN:
-      return "gboolean";
-    case DBUS_TYPE_BYTE:
-      return "guchar";
-    case DBUS_TYPE_INT32:
-      return "gint32";
-    case DBUS_TYPE_UINT32:
-      return "guint32";
-    case DBUS_TYPE_INT64:
-      return "gint64";
-    case DBUS_TYPE_UINT64:
-      return "guint64";
-    case DBUS_TYPE_DOUBLE:
-      return "gdouble";
-    case DBUS_TYPE_INT16:
-      return "gint";
-      break;
-    case DBUS_TYPE_UINT16:
-      return "guint";
-    case DBUS_TYPE_STRING:
-    case DBUS_TYPE_OBJECT_PATH:
-    case DBUS_TYPE_SIGNATURE:
-      /* FIXME - kind of a hack */
-      if (in)
-       return "const char *";
+      DBusSignatureIter subiter;
+
+      g_assert (dbus_type_is_container (current_type));
+      dbus_signature_iter_recurse (iter, &subiter);
+
+      if (current_type == DBUS_TYPE_ARRAY)
+       {
+         int elt_type = dbus_signature_iter_get_current_type (&subiter);
+         if (elt_type == DBUS_TYPE_DICT_ENTRY)
+           return signature_iter_to_g_type_dict (&subiter, is_client);
+         else 
+           return signature_iter_to_g_type_array (&subiter, is_client);
+       }
+      else if (current_type == DBUS_TYPE_STRUCT)
+       return signature_iter_to_g_type_struct (&subiter, is_client);
+      else if (current_type == DBUS_TYPE_VARIANT)
+       return g_value_get_type ();
+      else if (current_type == DBUS_TYPE_DICT_ENTRY)
+       return G_TYPE_INVALID;
       else
-       return "char *";
-    case DBUS_TYPE_STRUCT:
-    case DBUS_TYPE_ARRAY:
-    case DBUS_TYPE_VARIANT:
-      return NULL;
+       {
+         g_assert_not_reached ();
+         return G_TYPE_INVALID;
+       }
     }
-  return NULL;
 }
 
-const char *
-dbus_gtype_to_dbus_type (GType type)
+GType
+dbus_gtype_from_signature (const char *signature, gboolean is_client)
 {
-  switch (type)
+  DBusSignatureIter iter;
+
+  dbus_signature_iter_init (&iter, signature);
+
+  return dbus_gtype_from_signature_iter (&iter, is_client);
+}
+
+static char *
+dbus_gvalue_to_signature (GValue *value)
+{
+  const char *ret;
+
+  ret = dbus_gtype_to_signature (G_VALUE_TYPE (value));
+  if (ret)
+    return g_strdup (ret);
+  else
     {
-    case G_TYPE_CHAR:
-    case G_TYPE_UCHAR:
-      return DBUS_TYPE_BYTE_AS_STRING;
+      DBusGValue *val;
       
-    case G_TYPE_BOOLEAN:
-      return DBUS_TYPE_BOOLEAN_AS_STRING;
+      g_assert (G_VALUE_TYPE (value) == DBUS_TYPE_G_VALUE);
 
-      /* long gets cut to 32 bits so the remote API is consistent
-       * on all architectures
-       */
+      val = g_value_get_boxed (value);
       
-    case G_TYPE_LONG:
-    case G_TYPE_INT:
-      return DBUS_TYPE_INT32_AS_STRING;
-    case G_TYPE_ULONG:
-    case G_TYPE_UINT:
-      return DBUS_TYPE_UINT32_AS_STRING;
+      return dbus_g_value_get_signature (val);
+    }
+}
 
-    case G_TYPE_INT64:
-      return DBUS_TYPE_INT64_AS_STRING;
+const char *
+dbus_gtype_to_signature (GType gtype)
+{
+  DBusGTypeMarshalData *typedata;
 
-    case G_TYPE_UINT64:
-      return DBUS_TYPE_UINT64_AS_STRING;
-      
-    case G_TYPE_FLOAT:
-    case G_TYPE_DOUBLE:
-      return DBUS_TYPE_DOUBLE_AS_STRING;
+  typedata = g_type_get_qdata (gtype, dbus_g_type_metadata_data_quark ());
+  if (typedata == NULL)
+    return NULL;
+  return typedata->sig;
+}
 
-    case G_TYPE_STRING:
-      return DBUS_TYPE_STRING_AS_STRING;
+GArray *
+dbus_gtypes_from_arg_signature (const char *argsig, gboolean is_client)
+{
+  GArray *ret;
+  int current_type;
+  DBusSignatureIter sigiter;
 
-    default:
-      return NULL;
+  ret = g_array_new (FALSE, FALSE, sizeof (GType));
+
+  dbus_signature_iter_init (&sigiter, argsig);
+  while ((current_type = dbus_signature_iter_get_current_type (&sigiter)) != DBUS_TYPE_INVALID)
+    {
+      GType curtype;
+
+      curtype = dbus_gtype_from_signature_iter (&sigiter, is_client);
+      g_array_append_val (ret, curtype);
+      dbus_signature_iter_next (&sigiter);
     }
+  return ret;
 }
 
-gboolean
-dbus_gvalue_demarshal (DBusMessageIter *iter, GValue *value)
-{
-  g_assert (sizeof (dbus_bool_t) == sizeof (value->data[0].v_int));
 
-  dbus_gvalue_init (dbus_message_iter_get_arg_type (iter), value);
+static gboolean
+demarshal_basic (DBusGValueMarshalCtx      *context,
+                DBusMessageIter           *iter,
+                GValue                    *value,
+                GError                   **error)
+{
+  int current_type;
   
-  switch (dbus_message_iter_get_arg_type (iter))
+  current_type = dbus_message_iter_get_arg_type (iter);
+  g_assert (dbus_type_is_basic (current_type));
+
+  switch (current_type)
     {
     case DBUS_TYPE_BOOLEAN:
       {
@@ -283,24 +761,407 @@ dbus_gvalue_demarshal (DBusMessageIter *iter, GValue *value)
        return TRUE;
       }
     case DBUS_TYPE_STRING:
-    case DBUS_TYPE_OBJECT_PATH:
-    case DBUS_TYPE_SIGNATURE:
       {
         const char *s;
         dbus_message_iter_get_basic (iter, &s);
-        g_value_set_string (value, s);
+       g_value_set_string (value, s);
        return TRUE;
       }
-    case DBUS_TYPE_STRUCT:
-    case DBUS_TYPE_ARRAY:
-    case DBUS_TYPE_VARIANT:
     default:
+      g_assert_not_reached ();
+      return FALSE;
+    }
+}
+
+static gboolean
+demarshal_static_variant (DBusGValueMarshalCtx    *context,
+                         DBusMessageIter         *iter,
+                         GValue                  *value,
+                         GError                 **error)
+{
+  char *sig;
+  int current_type;
+  DBusMessageIter subiter;
+  GType variant_type;
+
+  current_type = dbus_message_iter_get_arg_type (iter);
+  dbus_message_iter_recurse (iter, &subiter);
+  sig = dbus_message_iter_get_signature (&subiter);
+
+  variant_type = dbus_gtype_from_signature (sig, context->proxy != NULL);
+  if (variant_type != G_TYPE_INVALID)
+    {
+      g_value_init (value, variant_type);
+
+      if (!dbus_gvalue_demarshal (context, &subiter, value, error))
+       {
+         dbus_free (sig);
+         return FALSE;
+       }
+    }
+  dbus_free (sig);
+  return TRUE;
+}
+
+static gboolean
+demarshal_variant (DBusGValueMarshalCtx    *context,
+                  DBusMessageIter         *iter,
+                  GValue                  *value,
+                  GError                 **error)
+
+{
+  GValue *variant_val;
+  variant_val = g_new0 (GValue, 1);
+
+  if (!demarshal_static_variant (context, iter, variant_val, error))
+    return FALSE;
+  
+  g_value_set_boxed_take_ownership (value, variant_val);
+  return TRUE;
+}
+
+static gboolean
+demarshal_proxy (DBusGValueMarshalCtx    *context,
+                DBusMessageIter         *iter,
+                GValue                  *value,
+                GError                 **error)
+{
+  const char *name;
+  DBusGProxy *new_proxy;
+  const char *objpath;
+  int current_type;
+
+  current_type = dbus_message_iter_get_arg_type (iter);
+  g_assert (current_type == DBUS_TYPE_OBJECT_PATH);
+
+  g_assert (context->proxy != NULL);
+  
+  name = dbus_g_proxy_get_bus_name (context->proxy);
+      
+  dbus_message_iter_get_basic (iter, &objpath);
+
+  new_proxy = dbus_g_proxy_new_from_proxy (context->proxy, NULL, objpath);
+  g_value_set_object_take_ownership (value, new_proxy);
+
+  return TRUE;
+}
+
+static gboolean
+demarshal_object (DBusGValueMarshalCtx    *context,
+                 DBusMessageIter         *iter,
+                 GValue                  *value,
+                 GError                 **error)
+{
+  const char *objpath;
+  int current_type;
+  GObject *obj;
+
+  current_type = dbus_message_iter_get_arg_type (iter);
+  g_assert (current_type == DBUS_TYPE_OBJECT_PATH);
+  g_assert (context->proxy == NULL);
+
+  dbus_message_iter_get_basic (iter, &objpath);
+
+  obj = dbus_g_connection_lookup_g_object (context->gconnection, objpath);
+  if (obj == NULL)
+    {
+      g_set_error (error,
+                  DBUS_GERROR,
+                  DBUS_GERROR_INVALID_ARGS,
+                  _("Unregistered object at path '%s'"),
+                  objpath);
       return FALSE;
     }
+  g_value_set_object (value, obj);
+
+  return TRUE;
 }
-    
+
+static gboolean
+demarshal_strv (DBusGValueMarshalCtx    *context,
+               DBusMessageIter         *iter,
+               GValue                  *value,
+               GError                 **error)
+{
+  DBusMessageIter subiter;
+  int current_type;
+  char **ret;
+  int len;
+  int i;
+
+  dbus_message_iter_recurse (iter, &subiter);
+
+  len = dbus_message_iter_get_array_len (&subiter);
+  g_assert (len >= 0);
+  ret = g_malloc (sizeof (char *) * (len + 1));
+  
+  i = 0;
+  while ((current_type = dbus_message_iter_get_arg_type (&subiter)) != DBUS_TYPE_INVALID)
+    {
+      g_assert (i < len);
+      g_assert (current_type == DBUS_TYPE_STRING);
+      
+      dbus_message_iter_get_basic (&subiter, &(ret[i]));
+      ret[i] = g_strdup (ret[i]);
+
+      dbus_message_iter_next (&subiter);
+      i++;
+    }
+  ret[i] = NULL; 
+  g_value_set_boxed_take_ownership (value, ret);
+  
+  return TRUE;
+}
+
+static gboolean
+demarshal_garray_basic (DBusGValueMarshalCtx    *context,
+                       DBusMessageIter         *iter,
+                       GValue                  *value,
+                       GError                 **error)
+{
+  DBusMessageIter subiter;
+  GArray *ret;
+  GType elt_gtype;
+  int elt_size;
+  void *msgarray;
+  int msgarray_len;
+
+  dbus_message_iter_recurse (iter, &subiter);
+
+  elt_gtype = dbus_g_type_get_collection_specialization (G_VALUE_TYPE (value));
+  g_assert (elt_gtype != G_TYPE_INVALID);
+  g_assert (dbus_g_type_is_fixed (elt_gtype));
+
+  elt_size = dbus_g_type_fixed_get_size (elt_gtype);
+  
+  ret = g_array_new (FALSE, TRUE, elt_size);
+
+  msgarray = NULL;
+  dbus_message_iter_get_fixed_array (&subiter,
+                                    &msgarray,
+                                    &msgarray_len);
+  g_assert (msgarray != NULL);
+  g_assert (msgarray_len >= 0);
+  g_array_append_vals (ret, msgarray, (guint) msgarray_len);
+
+  g_value_set_boxed_take_ownership (value, ret);
+  
+  return TRUE;
+}
+
+static gboolean
+demarshal_proxy_array (DBusGValueMarshalCtx    *context,
+                      DBusMessageIter         *iter,
+                      GValue                  *value,
+                      GError                 **error)
+{
+  DBusMessageIter subiter;
+  GPtrArray *ret;
+  guint len;
+  guint i;
+  int current_type;
+
+  g_assert (dbus_message_iter_get_arg_type (iter) == DBUS_TYPE_ARRAY);
+
+  dbus_message_iter_recurse (iter, &subiter);
+
+  len = dbus_message_iter_get_array_len (&subiter);
+  g_assert (len >= 0);
+  ret = g_ptr_array_sized_new (len);
+  
+  i = 0;
+  while ((current_type = dbus_message_iter_get_arg_type (&subiter)) != DBUS_TYPE_INVALID)
+    {
+      GValue subval = {0, };
+      g_assert (i < len);
+
+      if (!demarshal_proxy (context, &subiter, &subval, error))
+       {
+         for (i = 0; i < ret->len; i++)
+           g_object_unref (g_ptr_array_index (ret, i));
+         g_ptr_array_free (ret, TRUE);
+         return FALSE;
+       }
+
+      g_ptr_array_index (ret, i) = g_value_get_boxed (&subval);
+      /* Don't unset value, now owned by ret */
+
+      i++;
+    }
+  g_value_set_boxed_take_ownership (value, ret);
+  
+  return TRUE;
+}
+
+static gboolean
+demarshal_ghashtable (DBusGValueMarshalCtx    *context,
+                     DBusMessageIter         *iter,
+                     GValue                  *value,
+                     GError                 **error)
+{
+  GType gtype;
+  DBusMessageIter subiter;
+  int current_type;
+  GHashTable *ret;
+  GType key_gtype;
+  GType value_gtype;
+
+  current_type = dbus_message_iter_get_arg_type (iter);
+  g_assert (current_type == DBUS_TYPE_ARRAY);
+
+  gtype = G_VALUE_TYPE (value);
+
+  dbus_message_iter_recurse (iter, &subiter);
+
+  g_assert (dbus_message_iter_get_arg_type (&subiter) == DBUS_TYPE_DICT_ENTRY);
+
+  key_gtype = dbus_g_type_get_map_key_specialization (gtype);
+  g_assert (dbus_gtype_is_valid_hash_key (key_gtype));
+  value_gtype = dbus_g_type_get_map_value_specialization (gtype);
+  g_assert (dbus_gtype_is_valid_hash_value (value_gtype));
+
+  ret = g_hash_table_new_full (dbus_g_hash_func_from_gtype (key_gtype),
+                              dbus_g_hash_equal_from_gtype (key_gtype),
+                              dbus_g_hash_free_from_gtype (key_gtype),
+                              dbus_g_hash_free_from_gtype (value_gtype));
+
+  while ((current_type = dbus_message_iter_get_arg_type (&subiter)) != DBUS_TYPE_INVALID)
+    {
+      DBusMessageIter entry_iter;
+      GValue key_value = {0,};
+      GValue value_value = {0,};
+
+      current_type = dbus_message_iter_get_arg_type (&subiter);
+      g_assert (current_type == DBUS_TYPE_DICT_ENTRY);
+
+      dbus_message_iter_recurse (&subiter, &entry_iter);
+
+      g_value_init (&key_value, key_gtype);
+      if (!dbus_gvalue_demarshal (context,
+                                 &entry_iter,
+                                 &key_value,
+                                 error))
+       return FALSE;
+
+      dbus_message_iter_next (&entry_iter);
+
+      g_value_init (&value_value, value_gtype);
+      if (!dbus_gvalue_demarshal (context,
+                                 &entry_iter,
+                                 &value_value,
+                                 error))
+       return FALSE;
+
+      dbus_g_hash_table_insert_steal_values (ret,
+                                            &key_value,
+                                            &value_value);
+      /* Ownership of values passes to hash table, don't unset */
+
+      dbus_message_iter_next (&subiter);
+    }
+  g_value_set_boxed_take_ownership (value, ret);
+  
+  return TRUE;
+}
+
+static gboolean
+demarshal_recurse (DBusGValueMarshalCtx    *context,
+                  DBusMessageIter         *iter,
+                  GValue                  *value,
+                  GError                 **error)
+{
+  return FALSE;
+}
+
+gboolean
+dbus_gvalue_demarshal (DBusGValueMarshalCtx    *context,
+                      DBusMessageIter         *iter,
+                      GValue                  *value,
+                      GError                 **error)
+{
+  GType gtype;
+  DBusGTypeMarshalData *typedata;
+
+  gtype = G_VALUE_TYPE (value);
+
+  typedata = g_type_get_qdata (gtype, dbus_g_type_metadata_data_quark ());
+  g_return_val_if_fail (typedata != NULL || g_type_is_a (gtype, DBUS_TYPE_G_VALUE), FALSE);
+
+  if (typedata == NULL)
+    {
+      if (g_type_is_a (gtype, DBUS_TYPE_G_VALUE))
+       return demarshal_recurse (context, iter, value, error);
+      g_assert_not_reached ();
+    }
+  g_assert (typedata->vtable);
+  return typedata->vtable->demarshaller (context, iter, value, error);
+}
+
 gboolean
-dbus_gvalue_marshal (DBusMessageIter *iter, GValue *value)
+dbus_gvalue_demarshal_variant (DBusGValueMarshalCtx    *context,
+                              DBusMessageIter         *iter,
+                              GValue                  *value,
+                              GError                 **error)
+{
+  return demarshal_static_variant (context, iter, value, error);
+}
+
+GValueArray *
+dbus_gvalue_demarshal_message  (DBusGValueMarshalCtx    *context,
+                               DBusMessage             *message,
+                               guint                    n_types,
+                               const GType             *types,
+                               GError                 **error)
+{
+  GValueArray *ret;
+  DBusMessageIter iter;
+  int current_type;
+  guint index;
+  
+  ret = g_value_array_new (6);  /* 6 is a typical maximum for arguments */
+
+  dbus_message_iter_init (message, &iter);
+  index = 0;
+  while ((current_type = dbus_message_iter_get_arg_type (&iter)) != DBUS_TYPE_INVALID)
+    {
+      GValue *value;
+      GType gtype;
+
+      if (index >= n_types)
+       {
+         g_set_error (error, DBUS_GERROR,
+                      DBUS_GERROR_INVALID_ARGS,
+                      _("Too many arguments in message"));
+         goto lose;
+       }
+      
+      g_value_array_append (ret, NULL);
+      value = g_value_array_get_nth (ret, index);
+
+      gtype = types[index]; 
+      g_value_init (value, gtype);
+
+      if (!dbus_gvalue_demarshal (context, &iter, value, error))
+       goto lose;
+      dbus_message_iter_next (&iter);
+      index++;
+    }
+  if (index < n_types)
+    {
+      g_set_error (error, DBUS_GERROR,
+                  DBUS_GERROR_INVALID_ARGS,
+                  _("Too few arguments in message"));
+      goto lose;
+    }
+
+  return ret;
+ lose:
+  g_value_array_free (ret);
+  return NULL;
+}
+
+static gboolean
+marshal_basic (DBusMessageIter *iter, GValue *value)
 {
   GType value_type;
 
@@ -353,9 +1214,6 @@ dbus_gvalue_marshal (DBusMessageIter *iter, GValue *value)
           goto nomem;
       }
       return TRUE;
-      /* long gets cut to 32 bits so the remote API is consistent
-       * on all architectures
-       */
     case G_TYPE_LONG:
       {
         dbus_int32_t v = g_value_get_long (value);
@@ -424,9 +1282,10 @@ dbus_gvalue_marshal (DBusMessageIter *iter, GValue *value)
       return TRUE;
       
     default:
-      /* FIXME: we need to define custom boxed types for arrays
-        etc. so we can map them transparently / pleasantly */
-      return FALSE;
+      {
+       g_assert_not_reached ();
+       return FALSE;
+      }
     }
 
  nomem:
@@ -434,49 +1293,320 @@ dbus_gvalue_marshal (DBusMessageIter *iter, GValue *value)
   return FALSE;
 }
 
-/* FIXME is there a better way to do this? */
+static gboolean
+marshal_strv (DBusMessageIter   *iter,
+             GValue             *value)
+{
+  DBusMessageIter subiter;
+  char **array;
+  char **elt;
+  gboolean ret;
+
+  g_assert (G_VALUE_TYPE (value) == g_strv_get_type ());
+
+  array = g_value_get_boxed (value);
+
+  if (!dbus_message_iter_open_container (iter,
+                                        DBUS_TYPE_ARRAY,
+                                        "s",
+                                        &subiter))
+    goto out;
+
+  for (elt = array; *elt; elt++)
+    {
+      if (!dbus_message_iter_append_basic (&subiter,
+                                          DBUS_TYPE_STRING,
+                                          elt))
+       goto out;
+    }
+
+  if (!dbus_message_iter_close_container (iter, &subiter))
+    goto out;
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+static gboolean
+marshal_garray_basic (DBusMessageIter   *iter,
+                     GValue            *value)
+{
+  GType elt_gtype;
+  DBusMessageIter subiter;
+  GArray *array;
+  guint elt_size;
+  const char *subsignature_str;
+  gboolean ret;
+
+  elt_gtype = dbus_g_type_get_collection_specialization (G_VALUE_TYPE (value));
+  /* FIXME - this means we can't send an array of DBusGValue right now... */
+  subsignature_str = dbus_gtype_to_signature (elt_gtype);
+  g_assert (subsignature_str != NULL);
+  
+  elt_size = dbus_g_type_fixed_get_size (elt_gtype);
+
+  array = g_value_get_boxed (value);
+
+  if (!dbus_message_iter_open_container (iter,
+                                        DBUS_TYPE_ARRAY,
+                                        subsignature_str,
+                                        &subiter))
+    goto out;
+
+  /* TODO - This assumes that basic values are the same size
+   * is this always true?  If it is we can probably avoid
+   * a lot of the overhead in _marshal_basic_instance...
+   */
+  if (!dbus_message_iter_append_fixed_array (&subiter,
+                                            subsignature_str[0],
+                                            &(array->data),
+                                            array->len))
+    goto out;
+
+  if (!dbus_message_iter_close_container (iter, &subiter))
+    goto out;
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+static gboolean
+marshal_proxy (DBusMessageIter         *iter,
+              GValue                  *value)
+{
+  const char *path;
+  DBusGProxy *proxy;
+
+  g_assert (G_VALUE_TYPE (value) == dbus_g_proxy_get_type ());
+
+  proxy = g_value_get_object (value);
+  path = dbus_g_proxy_get_path (proxy);
+  
+  if (!dbus_message_iter_append_basic (iter,
+                                      DBUS_TYPE_OBJECT_PATH,
+                                      &path))
+    return FALSE;
+  return TRUE;
+}
+
+static gboolean
+marshal_object (DBusMessageIter         *iter,
+               GValue                  *value)
+{
+  const char *path;
+  GObject *obj;
+
+  obj = g_value_get_object (value);
+  path = _dbus_gobject_get_path (obj);
+
+  if (path == NULL)
+    /* FIXME should throw error */
+    return FALSE;
+  
+  if (!dbus_message_iter_append_basic (iter,
+                                      DBUS_TYPE_OBJECT_PATH,
+                                      &path))
+    return FALSE;
+  return TRUE;
+}
+
+static gboolean
+marshal_proxy_array (DBusMessageIter   *iter,
+                    GValue            *value)
+{
+  DBusMessageIter subiter;
+  GPtrArray *array;
+  const char *subsignature_str;
+  gboolean ret;
+  guint i;
+
+  subsignature_str = dbus_gtype_to_signature (DBUS_TYPE_G_PROXY);
+  g_assert (subsignature_str != NULL);
+
+  array = g_value_get_boxed (value);
+
+  if (!dbus_message_iter_open_container (iter,
+                                        DBUS_TYPE_ARRAY,
+                                        subsignature_str,
+                                        &subiter))
+    goto out;
+
+  for (i = 0; i < array->len; i++)
+    {
+      GValue val = {0, };
+
+      g_value_init (&val, DBUS_TYPE_G_PROXY);
+      g_value_set_static_boxed (&val, g_ptr_array_index (array, i));
+
+      marshal_proxy (&subiter, &val);
+
+      g_value_unset (&val);
+    }
+
+  if (!dbus_message_iter_close_container (iter, &subiter))
+    goto out;
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+struct DBusGLibHashMarshalData
+{
+  const char *entry_sig;
+  DBusMessageIter *iter;
+  gboolean err;
+};
+
+static void
+marshal_map_entry (const GValue *key,
+                  const GValue *value,
+                  gpointer data)
+{
+  struct DBusGLibHashMarshalData *hashdata = data;
+  DBusMessageIter subiter;
+
+  if (hashdata->err)
+    return;
+
+  if (!dbus_message_iter_open_container (hashdata->iter,
+                                        DBUS_TYPE_DICT_ENTRY,
+                                        NULL,
+                                        &subiter))
+    goto lose;
+
+  if (!dbus_gvalue_marshal (&subiter, (GValue*) key))
+    goto lose;
+
+  if (!dbus_gvalue_marshal (&subiter, (GValue*) value))
+    goto lose;
+
+  if (!dbus_message_iter_close_container (hashdata->iter, &subiter))
+    goto lose;
+  
+  return;
+ lose:
+  hashdata->err = TRUE;
+}
+
+static gboolean
+marshal_map (DBusMessageIter   *iter,
+            GValue            *value)
+{
+  GType gtype;
+  DBusMessageIter arr_iter;
+  gboolean ret;
+  struct DBusGLibHashMarshalData hashdata;
+  GType key_type;
+  GType value_type;
+  const char *key_sig;
+  const char *value_sig;
+  char *entry_sig;
+  char *array_sig;
+
+  gtype = G_VALUE_TYPE (value);
+
+  ret = FALSE;
+
+  key_type = dbus_g_type_get_map_key_specialization (gtype);
+  g_assert (dbus_gtype_is_valid_hash_key (key_type));
+  value_type = dbus_g_type_get_map_value_specialization (gtype);
+  g_assert (dbus_gtype_is_valid_hash_value (value_type));
+
+  key_sig = dbus_gtype_to_signature (key_type);
+  value_sig = dbus_gtype_to_signature (value_type);
+  entry_sig = g_strdup_printf ("%s%s", key_sig, value_sig);
+  array_sig = g_strdup_printf ("%c%s%c",
+                              DBUS_DICT_ENTRY_BEGIN_CHAR,
+                              entry_sig,
+                              DBUS_DICT_ENTRY_END_CHAR);
+  if (!dbus_message_iter_open_container (iter,
+                                        DBUS_TYPE_ARRAY,
+                                        array_sig,
+                                        &arr_iter))
+    goto lose;
+
+  hashdata.iter = &arr_iter;
+  hashdata.err = FALSE;
+  hashdata.entry_sig = entry_sig;
+
+  dbus_g_type_map_value_iterate (value,
+                                marshal_map_entry,
+                                &hashdata);
+
+  if (!dbus_message_iter_close_container (iter, &arr_iter))
+    goto lose;
+
+ out:
+  g_free (entry_sig);
+  g_free (array_sig);
+  return !hashdata.err;
+ lose:
+  hashdata.err = TRUE;
+  goto out;
+}
+
+static gboolean
+marshal_variant (DBusMessageIter          *iter,
+                GValue                   *value)
+{
+  GType value_gtype;
+  DBusMessageIter subiter;
+  char *variant_sig;
+  GValue *real_value;
+  gboolean ret;
+
+  real_value = g_value_get_boxed (value);
+  value_gtype = G_VALUE_TYPE (real_value);
+
+  variant_sig = dbus_gvalue_to_signature (real_value);
+  if (variant_sig == NULL)
+    {
+      g_warning ("Unsupported value type \"%s\"",
+                g_type_name (value_gtype));
+      return FALSE;
+    }
+
+  if (!dbus_message_iter_open_container (iter,
+                                        DBUS_TYPE_VARIANT,
+                                        variant_sig,
+                                        &subiter))
+    goto out;
+
+  if (!marshal_basic (&subiter, real_value))
+    goto out;
+
+  if (!dbus_message_iter_close_container (iter, &subiter))
+    goto out;
+
+  ret = TRUE;
+ out:
+  g_free (variant_sig);
+  return ret;
+}
+
+static gboolean
+marshal_recurse (DBusMessageIter         *iter,
+                GValue                  *value)
+{
+  return FALSE;
+}
+
 gboolean
-dbus_gvalue_store (GValue          *value,
-                  gpointer        storage)
+dbus_gvalue_marshal (DBusMessageIter         *iter,
+                    GValue                  *value)
 {
-  switch (G_VALUE_TYPE (value))
+  GType gtype;
+  DBusGTypeMarshalData *typedata;
+
+  gtype = G_VALUE_TYPE (value);
+
+  typedata = g_type_get_qdata (gtype, dbus_g_type_metadata_data_quark ());
+  if (typedata == NULL)
     {
-    case G_TYPE_CHAR:
-      *((gchar *) storage) = g_value_get_char (value);
-      return TRUE;
-    case G_TYPE_UCHAR:
-      *((guchar *) storage) = g_value_get_uchar (value);
-      return TRUE;
-    case G_TYPE_BOOLEAN:
-      *((gboolean *) storage) = g_value_get_boolean (value);
-      return TRUE;
-    case G_TYPE_LONG:
-      *((glong *) storage) = g_value_get_long (value);
-      return TRUE;
-    case G_TYPE_ULONG:
-      *((gulong *) storage) = g_value_get_ulong (value);
-      return TRUE;
-    case G_TYPE_INT:
-      *((gint *) storage) = g_value_get_int (value);
-      return TRUE;
-    case G_TYPE_UINT:
-      *((guint *) storage) = g_value_get_uint (value);
-      return TRUE;
-    case G_TYPE_INT64:
-      *((gint64 *) storage) = g_value_get_int64 (value);
-      return TRUE;
-    case G_TYPE_UINT64:
-      *((guint64 *) storage) = g_value_get_uint64 (value);
-      return TRUE;
-    case G_TYPE_FLOAT:
-    case G_TYPE_DOUBLE:
-      *((gdouble *) storage) = g_value_get_double (value);
-      return TRUE;
-    case G_TYPE_STRING:
-      /* FIXME - should optimize by not duping string twice */
-      *((gchar **) storage) = g_value_dup_string (value);
-      return TRUE;
-    default:
+      if (g_type_is_a (gtype, DBUS_TYPE_G_VALUE))
+       return marshal_recurse (iter, value);
       return FALSE;
     }
+  g_assert (typedata->vtable);
+  return typedata->vtable->marshaller (iter, value);
 }
index 4caa688..54ef780 100644 (file)
@@ -2,40 +2,49 @@
 #define DBUS_GOBJECT_VALUE_H
 
 #include <dbus/dbus.h>
+#include <dbus/dbus-signature.h>
 #include <glib.h>
 #include <glib-object.h>
+#include "dbus/dbus-glib.h"
 
 G_BEGIN_DECLS
 
-/* Used for return value storage */
-typedef union
-{
-  gboolean gboolean_val;
-  guchar guchar_val;
-  gint int_val;
-  gint64 gint64_val;
-  guint64 guint64_val;
-  double double_val;
-  gpointer gpointer_val;
-  char * chararray_val;
-} DBusBasicGValue;
+typedef struct {
+  DBusGConnection    *gconnection;
+  DBusGProxy         *proxy;
+} DBusGValueMarshalCtx;
 
-const char *   dbus_gvalue_genmarshal_name_from_type (const char *type);
+void           dbus_g_value_types_init        (void);
 
-const char *   dbus_gvalue_ctype_from_type           (const char *type, gboolean in);
+GType          dbus_gtype_from_signature      (const char              *signature,
+                                              gboolean                 is_client);
 
-const char *   dbus_gtype_to_dbus_type    (GType            type);
+GType          dbus_gtype_from_signature_iter (DBusSignatureIter       *sigiter,
+                                              gboolean                 is_client);
 
-gboolean       dbus_gvalue_init           (int              type,
-                                          GValue          *value);
+const char *   dbus_gtype_to_signature        (GType                    type);
 
-gboolean       dbus_gvalue_demarshal      (DBusMessageIter *iter,
-                                          GValue          *value);
-gboolean       dbus_gvalue_marshal        (DBusMessageIter *iter,
-                                          GValue          *value);
+GArray *       dbus_gtypes_from_arg_signature (const char              *signature,
+                                              gboolean                 is_client);
 
-gboolean       dbus_gvalue_store          (GValue          *value,
-                                          gpointer         storage);
+gboolean       dbus_gvalue_demarshal          (DBusGValueMarshalCtx    *context,
+                                              DBusMessageIter         *iter,
+                                              GValue                  *value,
+                                              GError                 **error);
+
+gboolean       dbus_gvalue_demarshal_variant  (DBusGValueMarshalCtx    *context,
+                                              DBusMessageIter         *iter,
+                                              GValue                  *value,
+                                              GError                 **error);
+
+GValueArray *  dbus_gvalue_demarshal_message  (DBusGValueMarshalCtx    *context,
+                                              DBusMessage             *message,
+                                              guint                    n_params,
+                                              const GType             *types, 
+                                              GError                 **error);
+
+gboolean       dbus_gvalue_marshal            (DBusMessageIter         *iter,
+                                              GValue                  *value);
 
 G_END_DECLS
 
index 23e9cea..b5ee424 100644 (file)
@@ -1,6 +1,6 @@
 SUBDIRS=examples
 
-INCLUDES=-I$(top_builddir) -I$(top_builddir)/dbus $(DBUS_CLIENT_CFLAGS) $(DBUS_GLIB_CFLAGS) $(DBUS_GLIB_TOOL_CFLAGS) $(PYTHON_INCLUDES)
+INCLUDES=-I$(top_builddir) -I$(top_builddir)/dbus $(DBUS_CLIENT_CFLAGS) $(DBUS_GLIB_CFLAGS) $(DBUS_GLIB_TOOL_CFLAGS) $(PYTHON_INCLUDES) -DDBUS_COMPILATION=1
 
 dbusdir = $(pythondir)/dbus
 dbus_PYTHON = __init__.py _dbus.py decorators.py exceptions.py services.py proxies.py _util.py types.py matchrules.py
index b0b8361..fffad08 100644 (file)
@@ -1,4 +1,4 @@
-INCLUDES=-I$(top_srcdir) $(DBUS_CLIENT_CFLAGS) $(DBUS_GLIB_CFLAGS)
+INCLUDES=-I$(top_srcdir) $(DBUS_CLIENT_CFLAGS) $(DBUS_GLIB_CFLAGS) -DDBUS_COMPILATION
 
 ## note that TESTS has special meaning (stuff to use in make check)
 ## so if adding tests not to be run in make check, don't add them to 
@@ -45,10 +45,10 @@ test_service_glib_SOURCES=                          \
 BUILT_SOURCES = test-service-glib-glue.h test-service-glib-bindings.h
 
 test-service-glib-glue.h: test-service-glib.xml $(top_builddir)/glib/dbus-binding-tool
-       $(top_builddir)/glib/dbus-binding-tool --mode=glib-server --output=test-service-glib-glue.h test-service-glib.xml
+       $(top_builddir)/glib/dbus-binding-tool --prefix=my_object --mode=glib-server --output=test-service-glib-glue.h test-service-glib.xml
 
 test-service-glib-bindings.h: test-service-glib.xml $(top_builddir)/glib/dbus-binding-tool
-       $(top_builddir)/glib/dbus-binding-tool --mode=glib-client --output=test-service-glib-bindings.h test-service-glib.xml
+       $(top_builddir)/glib/dbus-binding-tool --prefix=my_object --mode=glib-client --output=test-service-glib-bindings.h test-service-glib.xml
 
 CLEANFILES = test-service-glib-glue.h test-service-glib-bindings.h
 
index b0dc9ef..2fc8665 100644 (file)
@@ -6,9 +6,11 @@
 #include "test-service-glib-bindings.h"
 #include <glib/dbus-gidl.h>
 #include <glib/dbus-gparser.h>
+#include <glib-object.h>
 
 static GMainLoop *loop = NULL;
 static int n_times_foo_received = 0;
+static int n_times_frobnicate_received = 0;
 
 static gboolean
 timed_exit (gpointer loop)
@@ -27,6 +29,18 @@ foo_signal_handler (DBusGProxy  *proxy,
   g_main_loop_quit (loop);
 }
 
+static void
+frobnicate_signal_handler (DBusGProxy  *proxy,
+                          int          val,
+                          void        *user_data)
+{
+  n_times_frobnicate_received += 1;
+
+  g_assert (val == 42);
+
+  g_main_loop_quit (loop);
+}
+
 static void lose (const char *fmt, ...) G_GNUC_NORETURN G_GNUC_PRINTF (1, 2);
 static void lose_gerror (const char *prefix, GError *error) G_GNUC_NORETURN;
 
@@ -60,8 +74,8 @@ main (int argc, char **argv)
   DBusGProxy *proxy;
   DBusGPendingCall *call;
   char **name_list;
-  int name_list_len;
-  int i;
+  guint name_list_len;
+  guint i;
   guint32 result;
   const char *v_STRING;
   char *v_STRING_2;
@@ -71,6 +85,8 @@ main (int argc, char **argv)
   double v_DOUBLE_2;
     
   g_type_init ();
+
+  g_log_set_always_fatal (G_LOG_LEVEL_WARNING);
   
   loop = g_main_loop_new (NULL, FALSE);
 
@@ -94,17 +110,17 @@ main (int argc, char **argv)
 
   /* Call ListNames method */
   
-  call = dbus_g_proxy_begin_call (driver, "ListNames", DBUS_TYPE_INVALID);
+  call = dbus_g_proxy_begin_call (driver, "ListNames", G_TYPE_INVALID);
 
   error = NULL;
   if (!dbus_g_proxy_end_call (driver, call, &error,
-                              DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
-                              &name_list, &name_list_len,
-                              DBUS_TYPE_INVALID))
+                              G_TYPE_STRV, &name_list,
+                              G_TYPE_INVALID))
     lose_gerror ("Failed to complete ListNames call", error);
 
   g_print ("Names on the message bus:\n");
   i = 0;
+  name_list_len = g_strv_length (name_list);
   while (i < name_list_len)
     {
       g_assert (name_list[i] != NULL);
@@ -116,55 +132,49 @@ main (int argc, char **argv)
   g_strfreev (name_list);
 
   /* Test handling of unknown method */
-  v_STRING = "blah blah blah blah blah";
-  v_UINT32 = 10;
   call = dbus_g_proxy_begin_call (driver, "ThisMethodDoesNotExist",
-                                  DBUS_TYPE_STRING,
-                                  &v_STRING,
-                                  DBUS_TYPE_INT32,
-                                  &v_UINT32,
-                                  DBUS_TYPE_INVALID);
+                                  G_TYPE_STRING,
+                                  "blah blah blah blah blah",
+                                  G_TYPE_INT,
+                                  10,
+                                  G_TYPE_INVALID);
 
   error = NULL;
   if (dbus_g_proxy_end_call (driver, call, &error,
-                            DBUS_TYPE_INVALID))
+                            G_TYPE_INVALID))
     lose ("Calling nonexistent method succeeded!");
 
   g_print ("Got EXPECTED error from calling unknown method: %s\n", error->message);
   g_error_free (error);
   
   /* Activate a service */
-  v_STRING = "org.freedesktop.DBus.TestSuiteEchoService";
-  v_UINT32 = 0;
   call = dbus_g_proxy_begin_call (driver, "StartServiceByName",
-                                  DBUS_TYPE_STRING,
-                                  &v_STRING,
-                                  DBUS_TYPE_UINT32,
-                                  &v_UINT32,
-                                  DBUS_TYPE_INVALID);
+                                  G_TYPE_STRING,
+                                  "org.freedesktop.DBus.TestSuiteEchoService",
+                                  G_TYPE_UINT,
+                                  0,
+                                  G_TYPE_INVALID);
 
   error = NULL;
   if (!dbus_g_proxy_end_call (driver, call, &error,
-                              DBUS_TYPE_UINT32, &result,
-                              DBUS_TYPE_INVALID))
+                              G_TYPE_UINT, &result,
+                              G_TYPE_INVALID))
     lose_gerror ("Failed to complete Activate call", error);
 
   g_print ("Starting echo service result = 0x%x\n", result);
 
   /* Activate a service again */
-  v_STRING = "org.freedesktop.DBus.TestSuiteEchoService";
-  v_UINT32 = 0;
   call = dbus_g_proxy_begin_call (driver, "StartServiceByName",
-                                  DBUS_TYPE_STRING,
-                                  &v_STRING,
-                                  DBUS_TYPE_UINT32,
-                                  &v_UINT32,
+                                  G_TYPE_STRING,
+                                  "org.freedesktop.DBus.TestSuiteEchoService",
+                                  G_TYPE_UINT,
+                                  0,
                                   DBUS_TYPE_INVALID);
 
   error = NULL;
   if (!dbus_g_proxy_end_call (driver, call, &error,
-                             DBUS_TYPE_UINT32, &result,
-                             DBUS_TYPE_INVALID))
+                             G_TYPE_UINT, &result,
+                             G_TYPE_INVALID))
     lose_gerror ("Failed to complete Activate call", error);
 
   g_print ("Duplicate start of echo service = 0x%x\n", result);
@@ -180,30 +190,30 @@ main (int argc, char **argv)
   if (proxy == NULL)
     lose_gerror ("Failed to create proxy for name owner", error);
 
-  v_STRING = "my string hello";
   call = dbus_g_proxy_begin_call (proxy, "Echo",
-                                  DBUS_TYPE_STRING,
-                                  &v_STRING,
-                                  DBUS_TYPE_INVALID);
+                                  G_TYPE_STRING,
+                                  "my string hello",
+                                  G_TYPE_INVALID);
 
   error = NULL;
   if (!dbus_g_proxy_end_call (proxy, call, &error,
-                              DBUS_TYPE_STRING, &v_STRING,
-                              DBUS_TYPE_INVALID))
+                              G_TYPE_STRING, &v_STRING_2,
+                              G_TYPE_INVALID))
     lose_gerror ("Failed to complete Echo call", error);
 
-  g_print ("String echoed = \"%s\"\n", v_STRING);
+  g_print ("String echoed = \"%s\"\n", v_STRING_2);
+  g_free (v_STRING_2);
 
   /* Test oneway call and signal handling */
 
-  dbus_g_proxy_add_signal (proxy, "Foo", DBUS_TYPE_DOUBLE_AS_STRING);
+  dbus_g_proxy_add_signal (proxy, "Foo", G_TYPE_DOUBLE, G_TYPE_INVALID);
   
   dbus_g_proxy_connect_signal (proxy, "Foo",
                                G_CALLBACK (foo_signal_handler),
                                NULL, NULL);
   
   dbus_g_proxy_call_no_reply (proxy, "EmitFoo",
-                              DBUS_TYPE_INVALID);
+                              G_TYPE_INVALID);
   
   dbus_g_connection_flush (connection);
   
@@ -216,20 +226,22 @@ main (int argc, char **argv)
   
   /* Activate test servie */ 
   g_print ("Activating TestSuiteGLibService\n");
-  v_STRING = "org.freedesktop.DBus.TestSuiteGLibService";
-  v_UINT32 = 0;
-  call = dbus_g_proxy_begin_call (driver, "StartServiceByName",
-                                  DBUS_TYPE_STRING,
-                                  &v_STRING,
-                                  DBUS_TYPE_UINT32,
-                                  &v_UINT32,
-                                  DBUS_TYPE_INVALID);
-
   error = NULL;
-  if (!dbus_g_proxy_end_call (driver, call, &error,
-                             DBUS_TYPE_UINT32, &result,
-                             DBUS_TYPE_INVALID))
+  if (!dbus_g_proxy_invoke (driver, "StartServiceByName", &error,
+                           G_TYPE_STRING,
+                           "org.freedesktop.DBus.TestSuiteGLibService",
+                           G_TYPE_UINT,
+                           0,
+                           G_TYPE_INVALID,
+                           G_TYPE_UINT, &result,
+                           G_TYPE_INVALID)) {
     lose_gerror ("Failed to complete Activate call", error);
+  }
+
+  g_print ("TestSuiteGLibService activated\n");
+
+  if (getenv ("DBUS_GLIB_TEST_SLEEP_AFTER_ACTIVATION"))
+    g_usleep (8 * G_USEC_PER_SEC);
 
   g_object_unref (G_OBJECT (proxy));
 
@@ -242,63 +254,64 @@ main (int argc, char **argv)
   if (proxy == NULL)
     lose_gerror ("Failed to create proxy for name owner", error);
 
+  g_print ("Beginning method calls\n");
+
   call = dbus_g_proxy_begin_call (proxy, "DoNothing",
-                                  DBUS_TYPE_INVALID);
+                                  G_TYPE_INVALID);
   error = NULL;
-  if (!dbus_g_proxy_end_call (proxy, call, &error, DBUS_TYPE_INVALID))
+  if (!dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID))
     lose_gerror ("Failed to complete DoNothing call", error);
 
-  v_UINT32 = 42;
-  call = dbus_g_proxy_begin_call (proxy, "Increment",
-                                 DBUS_TYPE_UINT32, &v_UINT32,
-                                  DBUS_TYPE_INVALID);
   error = NULL;
-  if (!dbus_g_proxy_end_call (proxy, call, &error,
-                             DBUS_TYPE_UINT32, &v_UINT32_2,
-                             DBUS_TYPE_INVALID))
+  if (!dbus_g_proxy_invoke (proxy, "Increment", &error,
+                           G_TYPE_UINT, 42,
+                           G_TYPE_INVALID,
+                           G_TYPE_UINT, &v_UINT32_2,
+                           G_TYPE_INVALID))
     lose_gerror ("Failed to complete Increment call", error);
 
-  if (v_UINT32_2 != v_UINT32 + 1)
+  if (v_UINT32_2 != 43)
     lose ("Increment call returned %d, should be 43", v_UINT32_2);
 
-  call = dbus_g_proxy_begin_call (proxy, "ThrowError", DBUS_TYPE_INVALID);
+  call = dbus_g_proxy_begin_call (proxy, "ThrowError", G_TYPE_INVALID);
   error = NULL;
-  if (dbus_g_proxy_end_call (proxy, call, &error, DBUS_TYPE_INVALID) != FALSE)
+  if (dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID) != FALSE)
     lose ("ThrowError call unexpectedly succeeded!");
 
   g_print ("ThrowError failed (as expected) returned error: %s\n", error->message);
   g_clear_error (&error);
 
-  v_STRING = "foobar";
   call = dbus_g_proxy_begin_call (proxy, "Uppercase",
-                                 DBUS_TYPE_STRING, &v_STRING,
-                                 DBUS_TYPE_INVALID);
+                                 G_TYPE_STRING, "foobar",
+                                 G_TYPE_INVALID);
   error = NULL;
   if (!dbus_g_proxy_end_call (proxy, call, &error,
-                             DBUS_TYPE_STRING, &v_STRING_2,
-                             DBUS_TYPE_INVALID))
+                             G_TYPE_STRING, &v_STRING_2,
+                             G_TYPE_INVALID))
     lose_gerror ("Failed to complete Uppercase call", error);
   if (strcmp ("FOOBAR", v_STRING_2) != 0)
     lose ("Uppercase call returned unexpected string %s", v_STRING_2);
+  g_free (v_STRING_2);
 
   v_STRING = "bazwhee";
   v_UINT32 = 26;
   v_DOUBLE = G_PI;
   call = dbus_g_proxy_begin_call (proxy, "ManyArgs",
-                                 DBUS_TYPE_UINT32, &v_UINT32,
-                                 DBUS_TYPE_STRING, &v_STRING,
-                                 DBUS_TYPE_DOUBLE, &v_DOUBLE,
-                                 DBUS_TYPE_INVALID);
+                                 G_TYPE_UINT, 26,
+                                 G_TYPE_STRING, "bazwhee",
+                                 G_TYPE_DOUBLE, G_PI,
+                                 G_TYPE_INVALID);
   error = NULL;
   if (!dbus_g_proxy_end_call (proxy, call, &error,
-                             DBUS_TYPE_DOUBLE, &v_DOUBLE_2,
-                             DBUS_TYPE_STRING, &v_STRING_2,
-                             DBUS_TYPE_INVALID))
+                             G_TYPE_DOUBLE, &v_DOUBLE_2,
+                             G_TYPE_STRING, &v_STRING_2,
+                             G_TYPE_INVALID))
     lose_gerror ("Failed to complete ManyArgs call", error);
   if (v_DOUBLE_2 < 55 || v_DOUBLE_2 > 56)
     lose ("ManyArgs call returned unexpected double value %f", v_DOUBLE_2);
   if (strcmp ("BAZWHEE", v_STRING_2) != 0)
     lose ("ManyArgs call returned unexpected string %s", v_STRING_2);
+  g_free (v_STRING_2);
 
   if (!org_freedesktop_DBus_Tests_MyObject_do_nothing (proxy, &error))
     lose_gerror ("Failed to complete (wrapped) DoNothing call", error);
@@ -333,6 +346,313 @@ main (int argc, char **argv)
     lose ("(wrapped) ManyArgs call returned unexpected string %s", v_STRING_2);
   g_free (v_STRING_2);
 
+  {
+    guint32 arg0;
+    char *arg1;
+    gint32 arg2;
+    guint32 arg3;
+    guint32 arg4;
+    char *arg5;
+    
+    if (!org_freedesktop_DBus_Tests_MyObject_many_return (proxy, &arg0, &arg1, &arg2, &arg3, &arg4, &arg5, &error))
+      lose_gerror ("Failed to complete (wrapped) ManyReturn call", error);
+
+    if (arg0 != 42)
+      lose ("(wrapped) ManyReturn call returned unexpected guint32 value %u", arg0);
+
+    if (strcmp ("42", arg1) != 0)
+      lose ("(wrapped) ManyReturn call returned unexpected string %s", arg1);
+    g_free (arg1);
+
+    if (arg2 != -67)
+      lose ("(wrapped) ManyReturn call returned unexpected gint32 value %u", arg2);
+
+    if (arg3 != 2)
+      lose ("(wrapped) ManyReturn call returned unexpected guint32 value %u", arg3);
+
+    if (arg4 != 26)
+      lose ("(wrapped) ManyReturn call returned unexpected guint32 value %u", arg4);
+
+    if (strcmp ("hello world", arg5))
+      lose ("(wrapped) ManyReturn call returned unexpected string %s", arg5);
+    g_free (arg5);
+  }
+
+  {
+    GValue value = {0, };
+
+    g_value_init (&value, G_TYPE_STRING);
+    g_value_set_string (&value, "foo");
+
+    if (!org_freedesktop_DBus_Tests_MyObject_stringify (proxy,
+                                                       &value,
+                                                       &v_STRING_2,
+                                                       &error))
+      lose_gerror ("Failed to complete (wrapped) stringify call", error);
+    if (strcmp ("foo", v_STRING_2) != 0)
+      lose ("(wrapped) stringify call returned unexpected string %s", v_STRING_2);
+    g_free (v_STRING_2);
+
+    g_value_unset (&value);
+    g_value_init (&value, G_TYPE_INT);
+    g_value_set_int (&value, 42);
+
+    if (!org_freedesktop_DBus_Tests_MyObject_stringify (proxy,
+                                                       &value,
+                                                       &v_STRING_2,
+                                                       &error))
+      lose_gerror ("Failed to complete (wrapped) stringify call 2", error);
+    if (strcmp ("42", v_STRING_2) != 0)
+      lose ("(wrapped) stringify call 2 returned unexpected string %s", v_STRING_2);
+    g_value_unset (&value);
+    g_free (v_STRING_2);
+
+    g_value_init (&value, G_TYPE_INT);
+    g_value_set_int (&value, 88);
+    if (!org_freedesktop_DBus_Tests_MyObject_stringify (proxy,
+                                                       &value,
+                                                       NULL,
+                                                       &error))
+      lose_gerror ("Failed to complete (wrapped) stringify call 3", error);
+    g_value_unset (&value);
+
+    if (!org_freedesktop_DBus_Tests_MyObject_unstringify (proxy,
+                                                         "foo",
+                                                         &value,
+                                                         &error))
+      lose_gerror ("Failed to complete (wrapped) unstringify call", error);
+    if (!G_VALUE_HOLDS_STRING (&value))
+      lose ("(wrapped) unstringify call returned unexpected value type %d", (int) G_VALUE_TYPE (&value));
+    if (strcmp (g_value_get_string (&value), "foo"))
+      lose ("(wrapped) unstringify call returned unexpected string %s",
+           g_value_get_string (&value));
+       
+    g_value_unset (&value);
+
+    if (!org_freedesktop_DBus_Tests_MyObject_unstringify (proxy,
+                                                         "10",
+                                                         &value,
+                                                         &error))
+      lose_gerror ("Failed to complete (wrapped) unstringify call", error);
+    if (!G_VALUE_HOLDS_INT (&value))
+      lose ("(wrapped) unstringify call returned unexpected value type %d", (int) G_VALUE_TYPE (&value));
+    if (g_value_get_int (&value) != 10)
+      lose ("(wrapped) unstringify call returned unexpected integer %d",
+           g_value_get_int (&value));
+
+    g_value_unset (&value);
+  }
+
+  {
+    GArray *array;
+    guint32 val;
+    guint32 arraylen;
+
+    array = g_array_new (FALSE, TRUE, sizeof (guint32));
+    val = 42;
+    g_array_append_val (array, val);
+    val = 69;
+    g_array_append_val (array, val);
+    val = 88;
+    g_array_append_val (array, val);
+    val = 26;
+    g_array_append_val (array, val);
+    val = 2;
+    g_array_append_val (array, val);
+
+    arraylen = 0;
+    if (!org_freedesktop_DBus_Tests_MyObject_recursive1 (proxy, array,
+                                                        &arraylen, &error))
+      lose_gerror ("Failed to complete (wrapped) recursive1 call", error);
+    if (arraylen != 5)
+      lose ("(wrapped) recursive1 call returned invalid length %u", arraylen);
+  }
+
+  {
+    GArray *array = NULL;
+    guint32 *arrayvals;
+    
+    if (!org_freedesktop_DBus_Tests_MyObject_recursive2 (proxy, 2, &array, &error))
+      lose_gerror ("Failed to complete (wrapped) Recursive2 call", error);
+
+    if (array == NULL)
+      lose ("(wrapped) Recursive2 call returned NULL");
+    if (array->len != 5)
+      lose ("(wrapped) Recursive2 call returned unexpected array length %u", array->len);
+
+    arrayvals = (guint32*) array->data;
+    if (arrayvals[0] != 42)
+      lose ("(wrapped) Recursive2 call returned unexpected value %d in position 0", arrayvals[0]);
+    if (arrayvals[1] != 26)
+      lose ("(wrapped) Recursive2 call returned unexpected value %d in position 1", arrayvals[1]);
+    if (arrayvals[4] != 2)
+      lose ("(wrapped) Recursive2 call returned unexpected value %d in position 4", arrayvals[4]);
+
+    g_array_free (array, TRUE);
+  }
+
+  {
+    char **strs;
+    char **strs_ret;
+
+    strs = g_new0 (char *, 4);
+    strs[0] = "hello";
+    strs[1] = "HellO";
+    strs[2] = "HELLO";
+    strs[3] = NULL;
+
+    strs_ret = NULL;
+    if (!org_freedesktop_DBus_Tests_MyObject_many_uppercase (proxy, strs, &strs_ret, &error)) 
+      lose_gerror ("Failed to complete (wrapped) ManyUppercase call", error);
+    g_assert (strs_ret != NULL);
+    if (strcmp ("HELLO", strs_ret[0]) != 0)
+      lose ("(wrapped) ManyUppercase call returned unexpected string %s", strs_ret[0]);
+    if (strcmp ("HELLO", strs_ret[1]) != 0)
+      lose ("(wrapped) ManyUppercase call returned unexpected string %s", strs_ret[1]);
+    if (strcmp ("HELLO", strs_ret[2]) != 0)
+      lose ("(wrapped) ManyUppercase call returned unexpected string %s", strs_ret[2]);
+
+    g_strfreev (strs_ret);
+  }
+
+  {
+    GHashTable *table;
+    guint len;
+
+    table = g_hash_table_new (g_str_hash, g_str_equal);
+    g_hash_table_insert (table, "moooo", "b");
+    g_hash_table_insert (table, "xxx", "cow!");
+
+    len = 0;
+    if (!org_freedesktop_DBus_Tests_MyObject_str_hash_len (proxy, table, &len, &error))
+      lose_gerror ("(wrapped) StrHashLen call failed", error);
+    if (len != 13) 
+      lose ("(wrapped) StrHashLen returned unexpected length %u", len);
+    g_hash_table_destroy (table);
+  }
+
+  {
+    GHashTable *table;
+    const char *val;
+
+    if (!org_freedesktop_DBus_Tests_MyObject_get_hash (proxy, &table, &error))
+      lose_gerror ("(wrapped) GetHash call failed", error);
+    val = g_hash_table_lookup (table, "foo");
+    if (val == NULL || strcmp ("bar", val))
+      lose ("(wrapped) StrHashLen returned invalid value %s for key \"foo\"",
+           val ? val : "(null)");
+    val = g_hash_table_lookup (table, "baz");
+    if (val == NULL || strcmp ("whee", val))
+      lose ("(wrapped) StrHashLen returned invalid value %s for key \"whee\"",
+           val ? val : "(null)");
+    val = g_hash_table_lookup (table, "cow");
+    if (val == NULL || strcmp ("crack", val))
+      lose ("(wrapped) StrHashLen returned invalid value %s for key \"cow\"",
+           val ? val : "(null)");
+    if (g_hash_table_size (table) != 3)
+      lose ("(wrapped) StrHashLen returned unexpected hash size %u",
+           g_hash_table_size (table));
+
+    g_hash_table_destroy (table);
+  }
+
+  {
+    guint val;
+    DBusGProxy *ret_proxy;
+
+    if (!org_freedesktop_DBus_Tests_MyObject_objpath (proxy, proxy, &ret_proxy, &error))
+      lose_gerror ("Failed to complete (wrapped) Objpath call", error);
+    if (strcmp ("/org/freedesktop/DBus/Tests/MyTestObject2",
+               dbus_g_proxy_get_path (ret_proxy)) != 0)
+      lose ("(wrapped) objpath call returned unexpected proxy %s",
+           dbus_g_proxy_get_path (ret_proxy));
+
+    val = 1;
+    if (!org_freedesktop_DBus_Tests_MyObject_get_val (ret_proxy, &val, &error))
+      lose_gerror ("Failed to complete (wrapped) GetVal call", error);
+    if (val != 0)
+      lose ("(wrapped) GetVal returned invalid value %d", val);
+
+    if (!org_freedesktop_DBus_Tests_MyObject_increment_val (ret_proxy, &error))
+      lose_gerror ("Failed to complete (wrapped) IncrementVal call", error);
+
+    if (!org_freedesktop_DBus_Tests_MyObject_increment_val (ret_proxy, &error))
+      lose_gerror ("Failed to complete (wrapped) IncrementVal call", error);
+
+    if (!org_freedesktop_DBus_Tests_MyObject_increment_val (ret_proxy, &error))
+      lose_gerror ("Failed to complete (wrapped) IncrementVal call", error);
+
+    if (!org_freedesktop_DBus_Tests_MyObject_get_val (ret_proxy, &val, &error))
+      lose_gerror ("Failed to complete (wrapped) GetVal call", error);
+    if (val != 3)
+      lose ("(wrapped) GetVal returned invalid value %d", val);
+
+    if (!org_freedesktop_DBus_Tests_MyObject_get_val (proxy, &val, &error))
+      lose_gerror ("Failed to complete (wrapped) GetVal call", error);
+    if (val != 0)
+      lose ("(wrapped) GetVal returned invalid value %d", val);
+
+    if (!org_freedesktop_DBus_Tests_MyObject_increment_val (proxy, &error))
+      lose_gerror ("Failed to complete (wrapped) IncrementVal call", error);
+
+    if (!org_freedesktop_DBus_Tests_MyObject_get_val (proxy, &val, &error))
+      lose_gerror ("Failed to complete (wrapped) GetVal call", error);
+    if (val != 1)
+      lose ("(wrapped) GetVal returned invalid value %d", val);
+
+    if (!org_freedesktop_DBus_Tests_MyObject_get_val (ret_proxy, &val, &error))
+      lose_gerror ("Failed to complete (wrapped) GetVal call", error);
+    if (val != 3)
+      lose ("(wrapped) GetVal returned invalid value %d", val);
+
+    g_object_unref (G_OBJECT (ret_proxy));
+
+    ret_proxy = NULL;
+    if (!org_freedesktop_DBus_Tests_MyObject_objpath (proxy, proxy, &ret_proxy, &error))
+      lose_gerror ("Failed to complete (wrapped) Objpath call 2", error);
+    if (strcmp ("/org/freedesktop/DBus/Tests/MyTestObject2",
+               dbus_g_proxy_get_path (ret_proxy)) != 0)
+      lose ("(wrapped) objpath call 2 returned unexpected proxy %s",
+           dbus_g_proxy_get_path (ret_proxy));
+    {
+      const char *iface = dbus_g_proxy_get_interface (ret_proxy);
+      g_print ("returned proxy has interface \"%s\"\n",
+              iface ? iface : "(NULL)");
+    }
+
+    dbus_g_proxy_set_interface (ret_proxy, "org.freedesktop.DBus.Tests.FooObject");
+
+    val = 0;
+    if (!org_freedesktop_DBus_Tests_FooObject_get_value (ret_proxy, &val, &error))
+      lose_gerror ("Failed to complete (wrapped) GetValue call", error);
+    if (val != 3)
+      lose ("(wrapped) GetValue returned invalid value %d", val);
+  }
+
+  /* Signal handling tests */
+  
+  dbus_g_proxy_add_signal (proxy, "Frobnicate", G_TYPE_INT, G_TYPE_INVALID);
+  
+  dbus_g_proxy_connect_signal (proxy, "Frobnicate",
+                               G_CALLBACK (frobnicate_signal_handler),
+                               NULL, NULL);
+  
+  if (!dbus_g_proxy_invoke (proxy, "EmitFrobnicate", &error,
+                           G_TYPE_INVALID, G_TYPE_INVALID))
+    lose_gerror ("Failed to complete EmitFrobnicate call", error);
+
+  
+  dbus_g_connection_flush (connection);
+  
+#if 0
+  g_timeout_add (5000, timed_exit, loop);
+
+  g_main_loop_run (loop);
+
+  if (n_times_frobnicate_received != 1)
+    lose ("Frobnicate signal received %d times, should have been 1", n_times_frobnicate_received);
+#endif
+
   g_object_unref (G_OBJECT (proxy));
 
   proxy = dbus_g_proxy_new_for_name_owner (connection,
@@ -345,11 +665,11 @@ main (int argc, char **argv)
     lose_gerror ("Failed to create proxy for name owner", error);
 
   call = dbus_g_proxy_begin_call (proxy, "Introspect",
-                                 DBUS_TYPE_INVALID);
+                                 G_TYPE_INVALID);
   error = NULL;
   if (!dbus_g_proxy_end_call (proxy, call, &error,
-                             DBUS_TYPE_STRING, &v_STRING,
-                             DBUS_TYPE_INVALID))
+                             G_TYPE_STRING, &v_STRING_2,
+                             G_TYPE_INVALID))
     lose_gerror ("Failed to complete Introspect call", error);
 
   /* Could just do strcmp(), but that seems more fragile */
@@ -359,9 +679,10 @@ main (int argc, char **argv)
     gboolean found_introspectable;
     gboolean found_properties;
     gboolean found_myobject;
+    gboolean found_fooobject;
     gboolean found_gtk_myobject;
 
-    node = description_load_from_string (v_STRING, strlen (v_STRING), &error);
+    node = description_load_from_string (v_STRING_2, strlen (v_STRING_2), &error);
     if (!node)
       lose_gerror ("Failed to parse introspection data: %s", error);
 
@@ -369,6 +690,7 @@ main (int argc, char **argv)
     found_properties = FALSE;
     found_gtk_myobject = FALSE;
     found_myobject = FALSE;
+    found_fooobject = FALSE;
     for (elt = node_info_get_interfaces (node); elt ; elt = elt->next)
       {
        InterfaceInfo *iface = elt->data;
@@ -377,7 +699,7 @@ main (int argc, char **argv)
          found_introspectable = TRUE;
        else if (!found_properties && strcmp (interface_info_get_name (iface), "org.freedesktop.DBus.Properties") == 0)
          found_properties = TRUE;
-       else if (!found_gtk_myobject && strcmp (interface_info_get_name (iface), "org.gtk.objects.MyObject") == 0)
+       else if (strcmp (interface_info_get_name (iface), "org.gtk.objects.MyObject") == 0)
          found_gtk_myobject = TRUE;
        else if (!found_myobject && strcmp (interface_info_get_name (iface), "org.freedesktop.DBus.Tests.MyObject") == 0)
          {
@@ -401,6 +723,8 @@ main (int argc, char **argv)
            if (!found_manyargs)
              lose ("Missing method org.freedesktop.DBus.Tests.MyObject.ManyArgs");
          }
+       else if (!found_fooobject && strcmp (interface_info_get_name (iface), "org.freedesktop.DBus.Tests.FooObject") == 0)
+         found_fooobject = TRUE;
        else
          lose ("Unexpected or duplicate interface %s", interface_info_get_name (iface));
       }
@@ -408,6 +732,7 @@ main (int argc, char **argv)
     if (!(found_introspectable && found_gtk_myobject && found_myobject && found_properties))
       lose ("Missing interface"); 
   }
+  g_free (v_STRING_2);
   
   g_object_unref (G_OBJECT (driver));
 
index d929999..208bfb2 100644 (file)
@@ -20,6 +20,7 @@ struct MyObject
 {
   GObject parent;
   char *this_is_a_string;
+  guint val;
 };
 
 struct MyObjectClass
@@ -46,7 +47,7 @@ typedef enum
 
 gboolean my_object_do_nothing (MyObject *obj, GError **error);
 
-gboolean my_object_increment (MyObject *obj, gint32 x, int *ret, GError **error);
+gboolean my_object_increment (MyObject *obj, gint32 x, gint32 *ret, GError **error);
 
 gboolean my_object_throw_error (MyObject *obj, GError **error);
 
@@ -54,6 +55,30 @@ gboolean my_object_uppercase (MyObject *obj, const char *str, char **ret, GError
 
 gboolean my_object_many_args (MyObject *obj, guint32 x, const char *str, double trouble, double *d_ret, char **str_ret, GError **error);
 
+gboolean my_object_many_return (MyObject *obj, guint32 *arg0, char **arg1, gint32 *arg2, guint32 *arg3, guint32 *arg4, char **arg5, GError **error);
+
+gboolean my_object_recursive1 (MyObject *obj, GArray *array, guint32 *len_ret, GError **error);
+gboolean my_object_recursive2 (MyObject *obj, guint32 reqlen, GArray **array, GError **error);
+
+gboolean my_object_objpath (MyObject *obj, GObject *in, GObject **arg1, GError **error);
+
+gboolean my_object_stringify (MyObject *obj, GValue *value, char **ret, GError **error);
+gboolean my_object_unstringify (MyObject *obj, const char *str, GValue *value, GError **error);
+
+gboolean my_object_many_uppercase (MyObject *obj, const char * const *in, char ***out, GError **error);
+
+gboolean my_object_str_hash_len (MyObject *obj, GHashTable *table, guint *len, GError **error);
+
+gboolean my_object_get_hash (MyObject *obj, GHashTable **table, GError **error);
+
+gboolean my_object_increment_val (MyObject *obj, GError **error);
+
+gboolean my_object_get_val (MyObject *obj, guint *ret, GError **error);
+
+gboolean my_object_get_value (MyObject *obj, guint *ret, GError **error);
+
+gboolean my_object_emit_frobnicate (MyObject *obj, GError **error);
+
 #include "test-service-glib-glue.h"
 
 GQuark my_object_error_quark (void);
@@ -65,6 +90,15 @@ enum
   PROP_THIS_IS_A_STRING
 };
 
+enum
+{
+  FROBNICATE,
+  LAST_SIGNAL
+};
+
+static void *parent_class;
+static guint signals[LAST_SIGNAL] = { 0 };
+
 static void
 my_object_finalize (GObject *object)
 {
@@ -123,7 +157,7 @@ my_object_get_property (GObject      *object,
 static void
 my_object_init (MyObject *obj)
 {
-  
+  obj->val = 0;
 }
 
 static void
@@ -142,6 +176,15 @@ my_object_class_init (MyObjectClass *mobject_class)
                                                         _("Example of a string property"),
                                                         "default value",
                                                         G_PARAM_READWRITE));
+  signals[FROBNICATE] =
+    g_signal_new ("frobnicate",
+                 G_OBJECT_CLASS_TYPE (mobject_class),
+                  G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED,
+                  0,
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__INT,
+                  G_TYPE_NONE, 1, G_TYPE_INT);
+
 }
 
 GQuark
@@ -154,6 +197,9 @@ my_object_error_quark (void)
   return quark;
 }
 
+static GObject *obj;
+static GObject *obj2;
+
 gboolean
 my_object_do_nothing (MyObject *obj, GError **error)
 {
@@ -161,7 +207,7 @@ my_object_do_nothing (MyObject *obj, GError **error)
 }
 
 gboolean
-my_object_increment (MyObject *obj, gint32 x, int *ret, GError **error)
+my_object_increment (MyObject *obj, gint32 x, gint32 *ret, GError **error)
 {
   *ret = x +1;
   return TRUE;
@@ -191,7 +237,172 @@ my_object_many_args (MyObject *obj, guint32 x, const char *str, double trouble,
   *str_ret = g_ascii_strup (str, -1);
   return TRUE;
 }
-     
+
+gboolean
+my_object_many_return (MyObject *obj, guint32 *arg0, char **arg1, gint32 *arg2, guint32 *arg3, guint32 *arg4, char **arg5, GError **error)
+{
+  *arg0 = 42;
+  *arg1 = g_strdup ("42");
+  *arg2 = -67;
+  *arg3 = 2;
+  *arg4 = 26;
+  *arg5 = g_strdup ("hello world");
+  return TRUE;
+}
+
+gboolean
+my_object_stringify (MyObject *obj, GValue *value, char **ret, GError **error)
+{
+  GValue valstr = {0, };
+
+  g_value_init (&valstr, G_TYPE_STRING);
+  if (!g_value_transform (value, &valstr))
+    {
+      g_set_error (error,
+                  MY_OBJECT_ERROR,
+                  MY_OBJECT_ERROR_FOO,
+                  "couldn't transform value");
+      return FALSE;
+    }
+  *ret = g_value_dup_string (&valstr);
+  g_value_unset (&valstr);
+  return TRUE;
+}
+
+gboolean
+my_object_unstringify (MyObject *obj, const char *str, GValue *value, GError **error)
+{
+  if (str[0] == '\0' || !g_ascii_isdigit (str[0])) {
+    g_value_init (value, G_TYPE_STRING);
+    g_value_set_string (value, str);
+  } else {
+    g_value_init (value, G_TYPE_INT);
+    g_value_set_int (value, (int) g_ascii_strtoull (str, NULL, 10));
+  } 
+  return TRUE;
+}
+
+gboolean
+my_object_recursive1 (MyObject *obj, GArray *array, guint32 *len_ret, GError **error)
+{
+  *len_ret = array->len;
+  return TRUE;
+}
+
+gboolean
+my_object_recursive2 (MyObject *obj, guint32 reqlen, GArray **ret, GError **error)
+{
+  guint32 val;
+  GArray *array;
+  
+  array = g_array_new (FALSE, TRUE, sizeof (guint32));
+
+  while (reqlen > 0) {
+    val = 42;
+    g_array_append_val (array, val);
+    val = 26;
+    g_array_append_val (array, val);
+    reqlen--;
+  }
+  val = 2;
+  g_array_append_val (array, val);
+  *ret = array;
+  return TRUE;
+}
+
+gboolean
+my_object_many_uppercase (MyObject *obj, const char * const *in, char ***out, GError **error)
+{
+  int len;
+  int i;
+
+  len = g_strv_length ((char**) in);
+
+  *out = g_new0 (char *, len + 1);
+  for (i = 0; i < len; i++)
+    {
+      (*out)[i] = g_ascii_strup (in[i], -1);
+    }
+  (*out)[i] = NULL;
+  
+  return TRUE;
+}
+
+gboolean
+my_object_objpath (MyObject *obj, GObject *incoming, GObject **outgoing, GError **error)
+{
+  if ((GObject*) obj != incoming)
+    {
+      g_set_error (error,
+                  MY_OBJECT_ERROR,
+                  MY_OBJECT_ERROR_FOO,
+                  "invalid incoming object");
+      return FALSE;
+    }
+  *outgoing = g_object_ref (obj2);
+  return TRUE;
+}
+
+static void
+hash_foreach (gpointer key, gpointer val, gpointer user_data)
+{
+  const char *keystr = key;
+  const char *valstr = val;
+  guint *count = user_data;
+
+  *count += (strlen (keystr) + strlen (valstr));
+  g_print ("%s -> %s\n", keystr, valstr);
+}
+
+gboolean
+my_object_str_hash_len (MyObject *obj, GHashTable *table, guint *len, GError **error)
+{
+  *len = 0;
+  g_hash_table_foreach (table, hash_foreach, len);
+  return TRUE;
+}
+
+gboolean
+my_object_get_hash (MyObject *obj, GHashTable **ret, GError **error)
+{
+  GHashTable *table;
+
+  table = g_hash_table_new (g_str_hash, g_str_equal);
+  g_hash_table_insert (table, "foo", "bar");
+  g_hash_table_insert (table, "baz", "whee");
+  g_hash_table_insert (table, "cow", "crack");
+  *ret = table;
+  return TRUE;
+}
+
+gboolean
+my_object_increment_val (MyObject *obj, GError **error)
+{
+  obj->val++;
+  return TRUE;
+}
+
+gboolean
+my_object_get_val (MyObject *obj, guint *ret, GError **error)
+{
+  *ret = obj->val;
+  return TRUE;
+}
+
+gboolean
+my_object_get_value (MyObject *obj, guint *ret, GError **error)
+{
+  *ret = obj->val;
+  return TRUE;
+}
+
+gboolean
+my_object_emit_frobnicate (MyObject *obj, GError **error)
+{
+  g_signal_emit (obj, signals[FROBNICATE], 0, 42);
+  return TRUE;
+}
+
 static GMainLoop *loop;
 
 #define TEST_SERVICE_NAME "org.freedesktop.DBus.TestSuiteGLibService"
@@ -201,13 +412,15 @@ main (int argc, char **argv)
 {
   DBusGConnection *connection;
   GError *error;
-  GObject *obj;
   DBusGProxy *driver_proxy;
   guint32 request_name_ret;
 
   g_type_init ();
 
   g_printerr ("Launching test-service-glib\n");
+
+  g_log_set_always_fatal (G_LOG_LEVEL_CRITICAL);
+  g_log_set_always_fatal (G_LOG_LEVEL_WARNING);
   
   loop = g_main_loop_new (NULL, FALSE);
 
@@ -223,12 +436,16 @@ main (int argc, char **argv)
     }
 
   obj = g_object_new (MY_TYPE_OBJECT, NULL);
+  obj2 = g_object_new (MY_TYPE_OBJECT, NULL);
 
-  dbus_g_object_class_install_info (G_OBJECT_GET_CLASS (obj),
-                                   &dbus_glib_my_object_object_info);
+  dbus_g_object_type_install_info (MY_TYPE_OBJECT,
+                                  &dbus_glib_my_object_object_info);
   dbus_g_connection_register_g_object (connection,
                                        "/org/freedesktop/DBus/Tests/MyTestObject",
                                        obj);
+  dbus_g_connection_register_g_object (connection,
+                                       "/org/freedesktop/DBus/Tests/MyTestObject2",
+                                       obj2);
 
   driver_proxy = dbus_g_proxy_new_for_name (connection,
                                             DBUS_SERVICE_DBUS,
index 80a815f..f976572 100644 (file)
@@ -1,32 +1,24 @@
 <?xml version="1.0" encoding="UTF-8" ?>
 
 <node name="/org/freedesktop/DBus/Tests/MyTestObject">
-
   <interface name="org.freedesktop.DBus.Tests.MyObject">
-    <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="my_object"/>
-
     <method name="DoNothing">
-      <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="my_object_do_nothing"/>
     </method>
 
     <method name="Increment">
-      <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="my_object_increment"/>
       <arg type="u" name="x" />
       <arg type="u" direction="out" />
     </method>
 
     <method name="ThrowError">
-      <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="my_object_throw_error"/>
     </method>
 
     <method name="Uppercase">
-      <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="my_object_uppercase"/>
       <arg type="s" direction="in" />
       <arg type="s" direction="out" />
     </method>
 
     <method name="ManyArgs">
-      <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="my_object_many_args"/>
       <arg type="u" name="x" direction="in" />
       <arg type="s" name="str" direction="in" />
       <arg type="d" name="trouble" direction="in" />
       <arg type="s" name="str_ret" direction="out" />
     </method>
 
+    <method name="ManyReturn">
+      <arg type="u" direction="out" />
+      <arg type="s" direction="out" />
+      <arg type="i" direction="out" />
+      <arg type="u" direction="out" />
+      <arg type="u" direction="out" />
+      <arg type="s" direction="out" />
+    </method>
+
+    <method name="Stringify">
+      <arg type="v" name="val" direction="in"/>
+      <arg type="s" direction="out"/>
+    </method>
+
+    <method name="Unstringify">
+      <arg type="s" name="val" direction="in"/>
+      <arg type="v" direction="out"/>
+    </method>
+
+    <method name="Recursive1">
+      <arg type="au" direction="in"/>
+      <arg type="u" direction="out"/>
+    </method>
+
+    <method name="Recursive2">
+      <arg type="u" direction="in"/>
+      <arg type="au" direction="out"/>
+    </method>
+
+    <method name="ManyUppercase">
+      <arg type="as" direction="in"/>
+      <arg type="as" direction="out"/>
+    </method>
+
+    <method name="StrHashLen">
+      <arg type="a{ss}" direction="in"/>
+      <arg type="u" direction="out"/>
+    </method>
+
+    <method name="GetHash">
+      <arg type="a{ss}" direction="out"/>
+    </method>
+
+    <method name="Objpath">
+      <arg type="o" direction="in"/>
+      <arg type="o" direction="out"/>
+    </method>
+
+    <method name="IncrementVal">
+    </method>
+
+    <method name="GetVal">
+      <arg type="u" direction="out" />
+    </method>
+
+    <method name="EmitFrobnicate">
+    </method>
+
+  </interface>
+
+  <interface name="org.freedesktop.DBus.Tests.FooObject">
+    <method name="GetValue">
+      <arg type="u" direction="out" />
+    </method>
+
   </interface>
 
 </node>
index 333d89d..71db520 100644 (file)
@@ -1,4 +1,4 @@
-INCLUDES=-I$(top_srcdir) $(DBUS_CLIENT_CFLAGS) $(DBUS_GLIB_CFLAGS) $(DBUS_X_CFLAGS) $(DBUS_GTK_THREADS_CFLAGS) -DDBUS_LOCALEDIR=\"$(prefix)/@DATADIRNAME@/locale\"
+INCLUDES=-I$(top_srcdir) $(DBUS_CLIENT_CFLAGS) $(DBUS_GLIB_CFLAGS) $(DBUS_X_CFLAGS) $(DBUS_GTK_THREADS_CFLAGS) -DDBUS_LOCALEDIR=\"$(prefix)/@DATADIRNAME@/locale\" -DDBUS_COMPILATION
 
 if HAVE_GLIB
 GLIB_TOOLS=dbus-monitor
@@ -7,7 +7,7 @@ nodist_libdbus_glib_HEADERS = dbus-glib-bindings.h
 libdbus_glibdir = $(includedir)/dbus-1.0/dbus
 
 dbus-glib-bindings.h: dbus-bus-introspect.xml $(top_builddir)/glib/dbus-binding-tool
-       $(top_builddir)/glib/dbus-binding-tool --ignore-unsupported --mode=glib-client --output=dbus-glib-bindings.h dbus-bus-introspect.xml # FIXME - remove --ignore-unsupported when we can do arrays
+       $(top_builddir)/glib/dbus-binding-tool --mode=glib-client --output=dbus-glib-bindings.h dbus-bus-introspect.xml
 
 BUILT_SOURCES = dbus-glib-bindings.h dbus-bus-introspect.xml
 
index ab83e5f..01f7c4e 100644 (file)
@@ -23,6 +23,7 @@
 #include "dbus-names-model.h"
 #include <glib/gi18n.h>
 #include <string.h>
+#include <dbus/dbus-protocol.h>
 
 enum
 {
@@ -76,8 +77,8 @@ have_names_notify (DBusGPendingCall *call,
   error = NULL;
   if (!dbus_g_proxy_end_call (names_model->driver_proxy,
                               names_model->pending_list_names,
-                              &error, DBUS_TYPE_ARRAY, DBUS_TYPE_STRING,
-                              &names, &n_elements, DBUS_TYPE_INVALID))
+                              &error, G_TYPE_STRV,
+                              &names, &n_elements, G_TYPE_INVALID))
     {
       g_assert (names == NULL);
       g_assert (error != NULL);
@@ -210,7 +211,7 @@ names_model_reload (NamesModel *names_model)
   names_model->pending_list_names =
     dbus_g_proxy_begin_call (names_model->driver_proxy,
                              "ListNames",
-                             DBUS_TYPE_INVALID);
+                             G_TYPE_INVALID);
 
   dbus_g_pending_call_set_notify (names_model->pending_list_names,
                                   have_names_notify, names_model, NULL);
@@ -252,9 +253,10 @@ names_model_set_connection (NamesModel      *names_model,
 
       dbus_g_proxy_add_signal (names_model->driver_proxy,
                                "NameOwnerChanged",
-                               DBUS_TYPE_STRING_AS_STRING
-                               DBUS_TYPE_STRING_AS_STRING
-                               DBUS_TYPE_STRING_AS_STRING);
+                               G_TYPE_STRING,
+                               G_TYPE_STRING,
+                               G_TYPE_STRING,
+                              G_TYPE_INVALID);
       
       dbus_g_proxy_connect_signal (names_model->driver_proxy,
                                    "NameOwnerChanged", 
index b031c7d..95d00c1 100644 (file)
@@ -148,7 +148,7 @@ load_child_nodes (const char *service_name,
     {
       DBusGProxy *proxy;
       DBusGPendingCall *call;
-      const char *data;
+      char *data;
       NodeInfo *child;
       NodeInfo *complete_child;
       int save_len;
@@ -184,14 +184,19 @@ load_child_nodes (const char *service_name,
         }
   
       call = dbus_g_proxy_begin_call (proxy, "Introspect",
-                                      DBUS_TYPE_INVALID);
+                                      G_TYPE_INVALID);
       
       data = NULL;
-      if (!dbus_g_proxy_end_call (proxy, call, error, DBUS_TYPE_STRING, &data,
-                                  DBUS_TYPE_INVALID))
-        goto done;
+      if (!dbus_g_proxy_end_call (proxy, call, error, G_TYPE_STRING, &data,
+                                  G_TYPE_INVALID))
+       {
+         call = NULL;
+         goto done;
+       }
+      call = NULL;
       
       complete_child = description_load_from_string (data, -1, error);
+      g_free (data);
       if (complete_child == NULL)
         {
           g_printerr ("%s\n", data);
@@ -300,16 +305,18 @@ load_from_service_thread_func (void *thread_data)
 #endif
   
   call = dbus_g_proxy_begin_call (root_proxy, "Introspect",
-                                  DBUS_TYPE_INVALID);
+                                  G_TYPE_INVALID);
 
   data = NULL;
-  if (!dbus_g_proxy_end_call (root_proxy, call, &lfsd->error, DBUS_TYPE_STRING, &data,
-                              DBUS_TYPE_INVALID))
+  if (!dbus_g_proxy_end_call (root_proxy, call, &lfsd->error, G_TYPE_STRING, &data,
+                              G_TYPE_INVALID))
     {
+      call = NULL;
       g_printerr ("Failed to Introspect() %s\n",
                   dbus_g_proxy_get_bus_name (root_proxy));
       goto out;
     }
+  call = NULL;
 
   node = description_load_from_string (data, -1, &lfsd->error);
 
index 9784af0..e9fe3ce 100644 (file)
@@ -41,7 +41,7 @@ main (int argc, char *argv[])
   GError *error;
   const char *service;
   const char *path;
-  const char *introspect_data;
+  char *introspect_data;
   
   if (argc != 3)
     usage (argv[0], 1);
@@ -64,9 +64,9 @@ main (int argc, char *argv[])
   proxy = dbus_g_proxy_new_for_name (connection,
                                     service, path,
                                     DBUS_INTERFACE_INTROSPECTABLE);
-  call = dbus_g_proxy_begin_call (proxy, "Introspect", DBUS_TYPE_INVALID);
-  if (!dbus_g_proxy_end_call (proxy, call, &error, DBUS_TYPE_STRING,
-                             &introspect_data, DBUS_TYPE_INVALID))
+  call = dbus_g_proxy_begin_call (proxy, "Introspect", G_TYPE_INVALID);
+  if (!dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_STRING,
+                             &introspect_data, G_TYPE_INVALID))
     {
       fprintf (stderr, "Failed to get introspection data: %s\n",
                error->message);
@@ -75,8 +75,8 @@ main (int argc, char *argv[])
     }
       
   printf ("%s", introspect_data);
+  g_free (introspect_data);
 
-  dbus_g_pending_call_unref (call);
   g_object_unref (proxy);
 
   exit (0);