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/dbus-gproxy.c | 789 ++++++++++++++++++++++++++++------------------------- 1 file changed, 417 insertions(+), 372 deletions(-) (limited to 'glib/dbus-gproxy.c') 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); } -- cgit