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. --- 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 +- 13 files changed, 4185 insertions(+), 784 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 (limited to 'glib') 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 -- cgit