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-gobject.c | 599 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 494 insertions(+), 105 deletions(-) (limited to 'glib/dbus-gobject.c') 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 -- cgit