From beb9cd2eb219e04f9872c6a4dd743d5d1c36b4b1 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Mon, 13 Jun 2005 03:01:30 +0000 Subject: 2005-06-12 Colin Walters Async signals and various bugfixes and testing by Ross Burton . * 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. --- ChangeLog | 184 +++++ dbus/dbus-glib.h | 126 +++- doc/TODO | 9 +- doc/dbus-tutorial.xml | 680 ++++++++++++++++-- glib/Makefile.am | 6 +- glib/dbus-binding-tool-glib.c | 642 ++++++++++++++--- glib/dbus-binding-tool-glib.h | 1 + glib/dbus-gmain.c | 29 + glib/dbus-gobject.c | 599 +++++++++++++--- glib/dbus-gobject.h | 10 +- glib/dbus-gproxy.c | 789 ++++++++++---------- glib/dbus-gtype-specialized.c | 441 ++++++++++++ glib/dbus-gtype-specialized.h | 104 +++ glib/dbus-gvalue-utils.c | 715 +++++++++++++++++++ glib/dbus-gvalue-utils.h | 70 ++ glib/dbus-gvalue.c | 1508 ++++++++++++++++++++++++++++++++++----- glib/dbus-gvalue.h | 55 +- python/Makefile.am | 2 +- test/glib/Makefile.am | 6 +- test/glib/test-dbus-glib.c | 487 ++++++++++--- test/glib/test-service-glib.c | 231 +++++- test/glib/test-service-glib.xml | 73 +- tools/Makefile.am | 4 +- tools/dbus-names-model.c | 14 +- tools/dbus-viewer.c | 23 +- tools/print-introspect.c | 10 +- 26 files changed, 5825 insertions(+), 993 deletions(-) create mode 100644 glib/dbus-gtype-specialized.c create mode 100644 glib/dbus-gtype-specialized.h create mode 100644 glib/dbus-gvalue-utils.c create mode 100644 glib/dbus-gvalue-utils.h diff --git a/ChangeLog b/ChangeLog index bf71a3b8..305d7673 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,187 @@ +2005-06-12 Colin Walters + + Async signals and various bugfixes and testing by + Ross Burton . + + * 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 * doc/TODO: Add item about need to remove deprecated functions. diff --git a/dbus/dbus-glib.h b/dbus/dbus-glib.h index e1b4a704..7921c917 100644 --- a/dbus/dbus-glib.h +++ b/dbus/dbus-glib.h @@ -25,7 +25,6 @@ #define DBUS_GLIB_H #include -#include #include 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 +#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 diff --git a/doc/TODO b/doc/TODO index 28a772d5..659dde15 100644 --- 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 diff --git a/doc/dbus-tutorial.xml b/doc/dbus-tutorial.xml index 22906ac9..6ee32927 100644 --- a/doc/dbus-tutorial.xml +++ b/doc/dbus-tutorial.xml @@ -449,20 +449,294 @@ GLib API: Using Remote Objects - The GLib binding is defined in the header file - <dbus/dbus-glib.h>. The API is very small, in sharp contrast to the - low-level <dbus/dbus.h>. - + <dbus/dbus-glib.h>. - - The GLib bindings are incomplete, see the TODO file and comments in the - source code. - - - -Here is a D-BUS program using the GLib bindings. + + D-BUS - GLib type mappings + + The heart of the GLib bindings for D-BUS is the mapping it + provides between D-BUS "type signatures" and GLib types + (GType). The D-BUS type system is composed of + a number of "basic" types, along with several "container" types. + + + Basic type mappings + + Below is a list of the basic types, along with their associated + mapping to a GType. + + + + + D-BUS basic type + GType + Free function + Notes + + + + + BYTE + G_TYPE_UCHAR + + + + BOOLEAN + G_TYPE_BOOLEAN + + + + INT16 + G_TYPE_INT + + Will be changed to a G_TYPE_INT16 once GLib has it + + UINT16 + G_TYPE_UINT + + Will be changed to a G_TYPE_UINT16 once GLib has it + + INT32 + G_TYPE_INT + + Will be changed to a G_TYPE_INT32 once GLib has it + + UINT32 + G_TYPE_UINT + + Will be changed to a G_TYPE_UINT32 once GLib has it + + INT64 + G_TYPE_GINT64 + + + + UINT64 + G_TYPE_GUINT64 + + + + DOUBLE + G_TYPE_DOUBLE + + + + STRING + G_TYPE_STRING + g_free + + + OBJECT_PATH + DBUS_TYPE_G_PROXY + g_object_unref + The returned proxy does not have an interface set; use dbus_g_proxy_set_interface to invoke methods + + + + + As you can see, the basic mapping is fairly straightforward. + + + + Container type mappings + + The D-BUS type system also has a number of "container" + types, such as DBUS_TYPE_ARRAY and + DBUS_TYPE_STRUCT. The D-BUS type system + is fully recursive, so one can for example have an array of + array of strings (i.e. type signature + aas). + + + 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 DBUS_TYPE_STRUCT, or a + DBUS_TYPE_ARRAY 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". + + + First, D-BUS type signatures which have an "obvious" + corresponding builtin GLib type are mapped using that type: + + + + + D-BUS type signature + Description + GType + C typedef + Free function + Notes + + + + + as + Array of strings + G_TYPE_STRV + char ** + g_strfreev + + + v + Generic value container + G_TYPE_VALUE + GValue * + g_value_unset + The calling conventions for values expect that method callers have allocated return values; see below. + + + + + + + The next most common recursive type signatures are arrays of + basic values. The most obvious mapping for arrays of basic + types is a GArray. Now, GLib does not + provide a builtin GType for + GArray. 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. + + + 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. + + + + + D-BUS type signature + Description + GType + C typedef + Free function + Notes + + + + + ay + Array of bytes + DBUS_TYPE_G_BYTE_ARRAY + GArray * + g_array_free + + + + au + Array of uint + DBUS_TYPE_G_UINT_ARRAY + GArray * + g_array_free + + + + ai + Array of int + DBUS_TYPE_G_INT_ARRAY + GArray * + g_array_free + + + + ax + Array of int64 + DBUS_TYPE_G_INT64_ARRAY + GArray * + g_array_free + + + + at + Array of uint64 + DBUS_TYPE_G_UINT64_ARRAY + GArray * + g_array_free + + + + ad + Array of double + DBUS_TYPE_G_DOUBLE_ARRAY + GArray * + g_array_free + + + + ab + Array of boolean + DBUS_TYPE_G_BOOLEAN_ARRAY + GArray * + g_array_free + + + + + + + + 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 GType 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. + + + At present, only strings are supported. Work is in progress to + include more types. + + + + + D-BUS type signature + Description + GType + C typedef + Free function + Notes + + + + + a{ss} + Dictionary mapping strings to strings + DBUS_TYPE_G_STRING_STRING_HASHTABLE + GHashTable * + g_hash_table_destroy + + + + + + + + + Arbitrarily recursive type mappings + + 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 + DBusGValue which acts as a kind of special + variant value which may be iterated over manually. The + GType associated is + DBUS_TYPE_G_VALUE. + + + TODO insert usage of DBUS_TYPE_G_VALUE here. + + + + + A sample program + Here is a D-BUS program using the GLib bindings. 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, &error, - DBUS_TYPE_ARRAY, DBUS_TYPE_STRING, - &name_list, &name_list_len, - DBUS_TYPE_INVALID)) + if (!dbus_g_proxy_invoke (proxy, "ListNames", &error, G_TYPE_INVALID, + G_TYPE_STRV, &name_list, G_TYPE_INVALID)) { g_printerr ("Failed to complete ListNames call: %s\n", error->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 < 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; } + + + Program initalization + + A connection to the bus is acquired using + dbus_g_bus_get. Next, a proxy + is created for the object "/org/freedesktop/DBus" with + interface org.freedesktop.DBus + on the service org.freedesktop.DBus. + This is a proxy for the message bus itself. + + + + Understanding method invocation + + You have a number of choices for method invocation. First, as + used above, dbus_g_proxy_invoke 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 G_TYPE_INVALID. + Next, pointers to return values are specified, followed again + by G_TYPE_INVALID. + + + To invoke a method asynchronously, use + dbus_g_proxy_begin_call. This returns a + DBusGPendingCall object; you may then set a + notification function using + dbus_g_pending_call_set_notify. + + + + Connecting to object signals + + You may connect to signals using + dbus_g_proxy_add_signal and + dbus_g_proxy_connect_signal. At the + moment, dbus_g_proxy_add_signal requires + the D-BUS types of the remote object; this will likely be + changed later. + + + + More examples of method invocation + + Sending an integer and string, receiving an array of bytes + + + GArray *arr; + + error = NULL; + if (!dbus_g_proxy_invoke (proxy, "Foobar", &error, + G_TYPE_INT, 42, G_TYPE_STRING, "hello", + G_TYPE_INVALID, + DBUS_TYPE_G_UCHAR_ARRAY, &arr, G_TYPE_INVALID)) + { + g_printerr ("Failed to complete Foobar: %s\n", + error->message); + g_error_free (error); + exit (1); + } + g_assert (arr != NULL); + printf ("got back %u values", arr->len); + + + + + Sending a GHashTable + + + 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"); - - - 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. - - - - - - 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", &error, + DBUS_TYPE_G_STRING_STRING_HASH, hash, G_TYPE_INVALID, + G_TYPE_UINT, &ret, G_TYPE_INVALID)) + { + g_printerr ("Failed to complete HashSize: %s\n", + error->message); + g_error_free (error); + exit (1); + } + g_assert (ret == 2); + g_hash_table_destroy (hash); + + + + + Receiving a boolean and a string + + + gboolean boolret; + char *strret; + + error = NULL; + if (!dbus_g_proxy_invoke (proxy, "GetStuff", &error, + G_TYPE_INVALID, + G_TYPE_BOOLEAN, &boolret, + G_TYPE_STRING, &strret, + G_TYPE_INVALID)) + { + g_printerr ("Failed to complete GetStuff: %s\n", + error->message); + g_error_free (error); + exit (1); + } + printf ("%s %s", boolret ? "TRUE" : "FALSE", strret); + g_free (strret); + + + + + Sending two arrays of strings + + + /* 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; - + 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", &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->message); + g_error_free (error); + exit (1); + } + g_strfreev (strs_dynamic); + + + + + Sending a boolean, receiving an array of strings + + + char **strs; + char **strs_p; + gboolean blah; - - - 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", &error, + G_TYPE_BOOLEAN, blah, + G_TYPE_INVALID, + G_TYPE_STRV, &strs, + G_TYPE_INVALID)) + { + g_printerr ("Failed to complete GetStrs: %s\n", + error->message); + g_error_free (error); + exit (1); + } + for (strs_p = strs; *strs_p; strs_p++) + printf ("got string: \"%s\"", *strs_p); + g_strfreev (strs); + + + + + Sending a variant + + + GValue val = {0, }; - + g_value_init (&val, G_TYPE_STRING); + g_value_set_string (&val, "hello world"); + + error = NULL; + if (!dbus_g_proxy_invoke (proxy, "SendVariant", &error, + G_TYPE_VALUE, &val, G_TYPE_INVALID, + G_TYPE_INVALID)) + { + g_printerr ("Failed to complete SendVariant: %s\n", + error->message); + g_error_free (error); + exit (1); + } + g_assert (ret == 2); + g_value_unset (&val); + + + + + Receiving a variant + + + GValue val = {0, }; + error = NULL; + if (!dbus_g_proxy_invoke (proxy, "GetVariant", &error, G_TYPE_INVALID, + G_TYPE_VALUE, &val, G_TYPE_INVALID)) + { + g_printerr ("Failed to complete GetVariant: %s\n", + error->message); + g_error_free (error); + exit (1); + } + if (G_VALUE_TYPE (&val) == G_TYPE_STRING) + printf ("%s\n", g_value_get_string (&val)); + else if (G_VALUE_TYPE (&val) == G_TYPE_INT) + printf ("%d\n", g_value_get_int (&val)); + else + ... + g_value_unset (&val); + + + + GLib API: Implementing Objects - - - The GLib binding is defined in the header file - <dbus/dbus-glib.h>. 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. + + Here is a sample XML file which describes an object that exposes + one method, named ManyArgs. + +<?xml version="1.0" encoding="UTF-8" ?> + +<node name="/com/example/MyObject"> + <interface name="com.example.MyObject"> + <annotation name="org.freedesktop.DBus.GLib.CSymbol" value="my_object"/> + <method name="ManyArgs"> + <!-- This is optional, and in this case is redunundant --> + <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="d" name="d_ret" direction="out" /> + <arg type="s" name="str_ret" direction="out" /> + </method> + </interface> +</node> + + - 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 + (my_object). In addition, if particular + methods symbol names deviate from C convention + (i.e. ManyArgs -> + many_args), you may specify an annotation + giving the C symbol. + + + Once you have written this XML, run dbus-binding-tool --mode=glib-server FILENAME > HEADER_NAME. to + generate a header file. For example: dbus-binding-tool --mode=glib-server my-objet.xml > my-object-glue.h. + + + Next, include the generated header in your program, and invoke + dbus_g_object_class_install_info, passing the + object class and "object info" included in the header. For + example: + + dbus_g_object_type_install_info (COM_FOO_TYPE_MY_OBJECT, &com_foo_my_object_info); + + This should be done exactly once per object class. + + + To actually implement the method, just define a C function named e.g. + my_object_many_args in the same file as the info + header is included. At the moment, it is required that this function + conform to the following rules: + + + + The function must return a value of type gboolean; + TRUE on success, and FALSE + otherwise. + + + + + The first parameter is a pointer to an instance of the object. + + + + + Following the object instance pointer are the method + input values. + + + + + Following the input values are pointers to return values. + + + + + The final parameter must be a GError **. + If the function returns FALSE for an + error, the error parameter must be initalized with + g_set_error. + + + + + + Finally, you can export an object using dbus_g_connection_register_g_object. For example: + + dbus_g_connection_register_g_object (connection, + "/com/foo/MyObject", + obj); + - diff --git a/glib/Makefile.am b/glib/Makefile.am index 6d81e7d1..b9b9a70f 100644 --- a/glib/Makefile.am +++ b/glib/Makefile.am @@ -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 diff --git a/glib/dbus-binding-tool-glib.c b/glib/dbus-binding-tool-glib.c index 3c86d81f..76536aad 100644 --- a/glib/dbus-binding-tool-glib.c +++ b/glib/dbus-binding-tool-glib.c @@ -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 @@ -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: * \0\0(\0\0\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,14 +890,302 @@ 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) { @@ -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; diff --git a/glib/dbus-binding-tool-glib.h b/glib/dbus-binding-tool-glib.h index 9988df29..257b5347 100644 --- a/glib/dbus-binding-tool-glib.h +++ b/glib/dbus-binding-tool-glib.h @@ -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); diff --git a/glib/dbus-gmain.c b/glib/dbus-gmain.c index 046c493c..08f8aef4 100644 --- a/glib/dbus-gmain.c +++ b/glib/dbus-gmain.c @@ -27,6 +27,9 @@ #include #include "dbus-gtest.h" #include "dbus-gutils.h" +#include "dbus-gvalue.h" +#include "dbus-gvalue-utils.h" +#include #include #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; } diff --git a/glib/dbus-gobject.c b/glib/dbus-gobject.c index 09fc97fc..e2645f41 100644 --- a/glib/dbus-gobject.c +++ b/glib/dbus-gobject.c @@ -22,12 +22,15 @@ */ #include +#include #include #include #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 /** @@ -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, " \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 diff --git a/glib/dbus-gobject.h b/glib/dbus-gobject.h index 6c78546a..fcc38cda 100644 --- a/glib/dbus-gobject.h +++ b/glib/dbus-gobject.h @@ -24,11 +24,19 @@ #define DBUS_GLIB_OBJECT_H #include +#include #include +#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 diff --git a/glib/dbus-gproxy.c b/glib/dbus-gproxy.c index b5e977a4..42bfca8e 100644 --- a/glib/dbus-gproxy.c +++ b/glib/dbus-gproxy.c @@ -24,8 +24,8 @@ #include #include #include "dbus-gutils.h" -#include "dbus-gmarshal.h" #include "dbus-gvalue.h" +#include "dbus-gvalue-utils.h" #include "dbus-gobject.h" #include #include @@ -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 (¶m_values[0]); message = g_value_get_boxed (¶m_values[1]); - signature = g_value_get_string (¶m_values[2]); + gsignature = g_value_get_pointer (¶m_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 */ @@ -1203,6 +1143,33 @@ dbus_g_proxy_new_for_name_owner (DBusGConnection *connection, return proxy; } +/** + * 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 @@ -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 index 00000000..b331fcc5 --- /dev/null +++ b/glib/dbus-gtype-specialized.c @@ -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 +#include +#include + +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 index 00000000..5157f33f --- /dev/null +++ b/glib/dbus-gtype-specialized.h @@ -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 +#include + +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 index 00000000..9eea4bf0 --- /dev/null +++ b/glib/dbus-gvalue-utils.c @@ -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 +#include +#include + + +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 index 00000000..781569ff --- /dev/null +++ b/glib/dbus-gvalue-utils.h @@ -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 +#include + +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 diff --git a/glib/dbus-gvalue.c b/glib/dbus-gvalue.c index eae5a458..8506330d 100644 --- a/glib/dbus-gvalue.c +++ b/glib/dbus-gvalue.c @@ -22,202 +22,680 @@ * */ -#include +#include "dbus-gvalue.h" +#include "dbus-gobject.h" +#include "dbus-gvalue-utils.h" +#include "dbus/dbus-glib.h" +#include +#include +#include #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); } diff --git a/glib/dbus-gvalue.h b/glib/dbus-gvalue.h index 4caa6880..54ef780b 100644 --- a/glib/dbus-gvalue.h +++ b/glib/dbus-gvalue.h @@ -2,40 +2,49 @@ #define DBUS_GOBJECT_VALUE_H #include +#include #include #include +#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 diff --git a/python/Makefile.am b/python/Makefile.am index 23e9cea4..b5ee4243 100644 --- a/python/Makefile.am +++ b/python/Makefile.am @@ -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 diff --git a/test/glib/Makefile.am b/test/glib/Makefile.am index b0b8361a..fffad087 100644 --- a/test/glib/Makefile.am +++ b/test/glib/Makefile.am @@ -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 diff --git a/test/glib/test-dbus-glib.c b/test/glib/test-dbus-glib.c index b0dc9efc..2fc8665b 100644 --- a/test/glib/test-dbus-glib.c +++ b/test/glib/test-dbus-glib.c @@ -6,9 +6,11 @@ #include "test-service-glib-bindings.h" #include #include +#include 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)); diff --git a/test/glib/test-service-glib.c b/test/glib/test-service-glib.c index d929999d..208bfb24 100644 --- a/test/glib/test-service-glib.c +++ b/test/glib/test-service-glib.c @@ -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, diff --git a/test/glib/test-service-glib.xml b/test/glib/test-service-glib.xml index 80a815fa..f976572b 100644 --- a/test/glib/test-service-glib.xml +++ b/test/glib/test-service-glib.xml @@ -1,32 +1,24 @@ - - - - - - - - @@ -34,6 +26,71 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tools/Makefile.am b/tools/Makefile.am index 333d89d1..71db5203 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -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 diff --git a/tools/dbus-names-model.c b/tools/dbus-names-model.c index ab83e5f2..01f7c4e8 100644 --- a/tools/dbus-names-model.c +++ b/tools/dbus-names-model.c @@ -23,6 +23,7 @@ #include "dbus-names-model.h" #include #include +#include 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", diff --git a/tools/dbus-viewer.c b/tools/dbus-viewer.c index b031c7dd..95d00c19 100644 --- a/tools/dbus-viewer.c +++ b/tools/dbus-viewer.c @@ -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); diff --git a/tools/print-introspect.c b/tools/print-introspect.c index 9784af00..e9fe3ce4 100644 --- a/tools/print-introspect.c +++ b/tools/print-introspect.c @@ -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); -- cgit v1.2.1