From 5d014a802a4b47fbf5774f613d61b4218a1aa2a2 Mon Sep 17 00:00:00 2001 From: Janusz Lewandowski Date: Fri, 10 Oct 2014 22:58:20 +0200 Subject: [PATCH] Add a g_dbus_connection_register_object_with_closures function This is a binding-friendly version of g_dbus_connection_register_object. Based on a patch by Martin Pitt and the code of g_bus_watch_name_with_closures. https://bugzilla.gnome.org/show_bug.cgi?id=656325 --- docs/reference/gio/gio-sections.txt | 1 + gio/gdbusconnection.c | 253 ++++++++++++++++++++++++++++ gio/gdbusconnection.h | 8 + gio/tests/gdbus-export.c | 58 ++++++- 4 files changed, 313 insertions(+), 7 deletions(-) diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt index 993fe0659..2f3f46d9f 100644 --- a/docs/reference/gio/gio-sections.txt +++ b/docs/reference/gio/gio-sections.txt @@ -2876,6 +2876,7 @@ GDBusInterfaceGetPropertyFunc GDBusInterfaceSetPropertyFunc g_dbus_connection_register_object g_dbus_connection_unregister_object +g_dbus_connection_register_object_with_closures GDBusSubtreeVTable GDBusSubtreeEnumerateFunc GDBusSubtreeIntrospectFunc diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c index 9824ad9c2..4a8d9a5a7 100644 --- a/gio/gdbusconnection.c +++ b/gio/gdbusconnection.c @@ -5172,6 +5172,259 @@ g_dbus_connection_unregister_object (GDBusConnection *connection, return ret; } +typedef struct { + GClosure *method_call_closure; + GClosure *get_property_closure; + GClosure *set_property_closure; +} RegisterObjectData; + +static RegisterObjectData * +register_object_data_new (GClosure *method_call_closure, + GClosure *get_property_closure, + GClosure *set_property_closure) +{ + RegisterObjectData *data; + + data = g_new0 (RegisterObjectData, 1); + + if (method_call_closure != NULL) + { + data->method_call_closure = g_closure_ref (method_call_closure); + g_closure_sink (method_call_closure); + if (G_CLOSURE_NEEDS_MARSHAL (method_call_closure)) + g_closure_set_marshal (method_call_closure, g_cclosure_marshal_generic); + } + + if (get_property_closure != NULL) + { + data->get_property_closure = g_closure_ref (get_property_closure); + g_closure_sink (get_property_closure); + if (G_CLOSURE_NEEDS_MARSHAL (get_property_closure)) + g_closure_set_marshal (get_property_closure, g_cclosure_marshal_generic); + } + + if (set_property_closure != NULL) + { + data->set_property_closure = g_closure_ref (set_property_closure); + g_closure_sink (set_property_closure); + if (G_CLOSURE_NEEDS_MARSHAL (set_property_closure)) + g_closure_set_marshal (set_property_closure, g_cclosure_marshal_generic); + } + + return data; +} + +static void +register_object_free_func (gpointer user_data) +{ + RegisterObjectData *data = user_data; + + g_clear_pointer (&data->method_call_closure, g_closure_unref); + g_clear_pointer (&data->get_property_closure, g_closure_unref); + g_clear_pointer (&data->set_property_closure, g_closure_unref); + + g_free (data); +} + +static void +register_with_closures_on_method_call (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *method_name, + GVariant *parameters, + GDBusMethodInvocation *invocation, + gpointer user_data) +{ + RegisterObjectData *data = user_data; + GValue params[] = { G_VALUE_INIT, G_VALUE_INIT, G_VALUE_INIT, G_VALUE_INIT, G_VALUE_INIT, G_VALUE_INIT, G_VALUE_INIT }; + + g_value_init (¶ms[0], G_TYPE_DBUS_CONNECTION); + g_value_set_object (¶ms[0], connection); + + g_value_init (¶ms[1], G_TYPE_STRING); + g_value_set_string (¶ms[1], sender); + + g_value_init (¶ms[2], G_TYPE_STRING); + g_value_set_string (¶ms[2], object_path); + + g_value_init (¶ms[3], G_TYPE_STRING); + g_value_set_string (¶ms[3], interface_name); + + g_value_init (¶ms[4], G_TYPE_STRING); + g_value_set_string (¶ms[4], method_name); + + g_value_init (¶ms[5], G_TYPE_VARIANT); + g_value_set_variant (¶ms[5], parameters); + + g_value_init (¶ms[6], G_TYPE_DBUS_METHOD_INVOCATION); + g_value_set_object (¶ms[6], invocation); + + g_closure_invoke (data->method_call_closure, NULL, G_N_ELEMENTS (params), params, NULL); + + g_value_unset (params + 0); + g_value_unset (params + 1); + g_value_unset (params + 2); + g_value_unset (params + 3); + g_value_unset (params + 4); + g_value_unset (params + 5); + g_value_unset (params + 6); +} + +static GVariant * +register_with_closures_on_get_property (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, + GError **error, + gpointer user_data) +{ + RegisterObjectData *data = user_data; + GValue params[] = { G_VALUE_INIT, G_VALUE_INIT, G_VALUE_INIT, G_VALUE_INIT, G_VALUE_INIT }; + GValue result_value = G_VALUE_INIT; + GVariant *result; + + g_value_init (¶ms[0], G_TYPE_DBUS_CONNECTION); + g_value_set_object (¶ms[0], connection); + + g_value_init (¶ms[1], G_TYPE_STRING); + g_value_set_string (¶ms[1], sender); + + g_value_init (¶ms[2], G_TYPE_STRING); + g_value_set_string (¶ms[2], object_path); + + g_value_init (¶ms[3], G_TYPE_STRING); + g_value_set_string (¶ms[3], interface_name); + + g_value_init (¶ms[4], G_TYPE_STRING); + g_value_set_string (¶ms[4], property_name); + + g_value_init (&result_value, G_TYPE_VARIANT); + + g_closure_invoke (data->get_property_closure, &result_value, G_N_ELEMENTS (params), params, NULL); + + result = g_value_get_variant (&result_value); + if (result) + g_variant_ref (result); + + g_value_unset (params + 0); + g_value_unset (params + 1); + g_value_unset (params + 2); + g_value_unset (params + 3); + g_value_unset (params + 4); + g_value_unset (&result_value); + + if (!result) + g_set_error (error, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, + _("Unable to retrieve property %s.%s"), + interface_name, property_name); + + return result; +} + +static gboolean +register_with_closures_on_set_property (GDBusConnection *connection, + const gchar *sender, + const gchar *object_path, + const gchar *interface_name, + const gchar *property_name, + GVariant *value, + GError **error, + gpointer user_data) +{ + RegisterObjectData *data = user_data; + GValue params[] = { G_VALUE_INIT, G_VALUE_INIT, G_VALUE_INIT, G_VALUE_INIT, G_VALUE_INIT, G_VALUE_INIT }; + GValue result_value = G_VALUE_INIT; + gboolean result; + + g_value_init (¶ms[0], G_TYPE_DBUS_CONNECTION); + g_value_set_object (¶ms[0], connection); + + g_value_init (¶ms[1], G_TYPE_STRING); + g_value_set_string (¶ms[1], sender); + + g_value_init (¶ms[2], G_TYPE_STRING); + g_value_set_string (¶ms[2], object_path); + + g_value_init (¶ms[3], G_TYPE_STRING); + g_value_set_string (¶ms[3], interface_name); + + g_value_init (¶ms[4], G_TYPE_STRING); + g_value_set_string (¶ms[4], property_name); + + g_value_init (¶ms[5], G_TYPE_VARIANT); + g_value_set_variant (¶ms[5], value); + + g_value_init (&result_value, G_TYPE_BOOLEAN); + + g_closure_invoke (data->set_property_closure, &result_value, G_N_ELEMENTS (params), params, NULL); + + result = g_value_get_boolean (&result_value); + + g_value_unset (params + 0); + g_value_unset (params + 1); + g_value_unset (params + 2); + g_value_unset (params + 3); + g_value_unset (params + 4); + g_value_unset (params + 5); + g_value_unset (&result_value); + + if (!result) + g_set_error (error, + G_DBUS_ERROR, G_DBUS_ERROR_FAILED, + _("Unable to set property %s.%s"), + interface_name, property_name); + + return result; +} + +/** + * g_dbus_connection_register_object_with_closures: (rename-to g_dbus_connection_register_object) + * @connection: A #GDBusConnection. + * @object_path: The object path to register at. + * @interface_info: Introspection data for the interface. + * @method_call_closure: (nullable): #GClosure for handling incoming method calls. + * @get_property_closure: (nullable): #GClosure for getting a property. + * @set_property_closure: (nullable): #GClosure for setting a property. + * @error: Return location for error or %NULL. + * + * Version of g_dbus_connection_register_object() using closures instead of a + * #GDBusInterfaceVTable for easier binding in other languages. + * + * Returns: 0 if @error is set, otherwise a registration id (never 0) + * that can be used with g_dbus_connection_unregister_object() . + * + * Since: 2.46 + */ +guint +g_dbus_connection_register_object_with_closures (GDBusConnection *connection, + const gchar *object_path, + GDBusInterfaceInfo *interface_info, + GClosure *method_call_closure, + GClosure *get_property_closure, + GClosure *set_property_closure, + GError **error) +{ + RegisterObjectData *data; + GDBusInterfaceVTable vtable = + { + method_call_closure != NULL ? register_with_closures_on_method_call : NULL, + get_property_closure != NULL ? register_with_closures_on_get_property : NULL, + set_property_closure != NULL ? register_with_closures_on_set_property : NULL + }; + + data = register_object_data_new (method_call_closure, get_property_closure, set_property_closure); + + return g_dbus_connection_register_object (connection, + object_path, + interface_info, + &vtable, + data, + register_object_free_func, + error); +} + /* ---------------------------------------------------------------------------------------------------- */ /** diff --git a/gio/gdbusconnection.h b/gio/gdbusconnection.h index ba054dbc1..6876cd420 100644 --- a/gio/gdbusconnection.h +++ b/gio/gdbusconnection.h @@ -401,6 +401,14 @@ guint g_dbus_connection_register_object (GDBusConnection gpointer user_data, GDestroyNotify user_data_free_func, GError **error); +GLIB_AVAILABLE_IN_2_46 +guint g_dbus_connection_register_object_with_closures (GDBusConnection *connection, + const gchar *object_path, + GDBusInterfaceInfo *interface_info, + GClosure *method_call_closure, + GClosure *get_property_closure, + GClosure *set_property_closure, + GError **error); GLIB_AVAILABLE_IN_ALL gboolean g_dbus_connection_unregister_object (GDBusConnection *connection, guint registration_id); diff --git a/gio/tests/gdbus-export.c b/gio/tests/gdbus-export.c index 594050152..921ba40f7 100644 --- a/gio/tests/gdbus-export.c +++ b/gio/tests/gdbus-export.c @@ -733,10 +733,17 @@ static const GDBusSubtreeVTable dynamic_subtree_vtable = /* -------------------- */ +typedef struct +{ + const gchar *object_path; + gboolean check_remote_errors; +} TestDispatchThreadFuncArgs; + static gpointer test_dispatch_thread_func (gpointer user_data) { - const gchar *object_path = user_data; + TestDispatchThreadFuncArgs *args = user_data; + const gchar *object_path = args->object_path; GDBusProxy *foo_proxy; GVariant *value; GVariant *inner; @@ -897,8 +904,13 @@ test_dispatch_thread_func (gpointer user_data) NULL, &error); g_assert (value == NULL); - g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_SPAWN_FILE_INVALID); - g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.Spawn.FileInvalid: Returning some error instead of writing the value 'NotReadable' to the property ''But Writable you are!''"); + if (args->check_remote_errors) + { + /* _with_closures variant doesn't support customizing error data. */ + g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_SPAWN_FILE_INVALID); + g_assert_cmpstr (error->message, ==, "GDBus.Error:org.freedesktop.DBus.Error.Spawn.FileInvalid: Returning some error instead of writing the value 'NotReadable' to the property ''But Writable you are!''"); + } + g_assert (error != NULL && error->domain == G_DBUS_ERROR); g_error_free (error); error = NULL; @@ -941,14 +953,17 @@ test_dispatch_thread_func (gpointer user_data) } static void -test_dispatch (const gchar *object_path) +test_dispatch (const gchar *object_path, + gboolean check_remote_errors) { GThread *thread; + + TestDispatchThreadFuncArgs args = {object_path, check_remote_errors}; /* run this in a thread to avoid deadlocks */ thread = g_thread_new ("test_dispatch", test_dispatch_thread_func, - (gpointer) object_path); + (gpointer) &args); g_main_loop_run (loop); g_thread_join (thread); } @@ -1344,8 +1359,8 @@ test_object_registration (void) * We do this for both a regular registered object (/foo/boss) and also for an object * registered through the subtree mechanism. */ - test_dispatch ("/foo/boss"); - test_dispatch ("/foo/boss/executives/vp0"); + test_dispatch ("/foo/boss", TRUE); + test_dispatch ("/foo/boss/executives/vp0", TRUE); /* To prevent from exiting and attaching a D-Bus tool like D-Feet; uncomment: */ #if 0 @@ -1392,6 +1407,34 @@ test_object_registration (void) g_object_unref (c); } +static void +test_object_registration_with_closures (void) +{ + GError *error; + guint registration_id; + + error = NULL; + c = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); + g_assert_no_error (error); + g_assert (c != NULL); + + registration_id = g_dbus_connection_register_object_with_closures (c, + "/foo/boss", + (GDBusInterfaceInfo *) &foo_interface_info, + g_cclosure_new (G_CALLBACK (foo_method_call), NULL, NULL), + g_cclosure_new (G_CALLBACK (foo_get_property), NULL, NULL), + g_cclosure_new (G_CALLBACK (foo_set_property), NULL, NULL), + &error); + g_assert_no_error (error); + g_assert (registration_id > 0); + + test_dispatch ("/foo/boss", FALSE); + + g_assert (g_dbus_connection_unregister_object (c, registration_id)); + + g_object_unref (c); +} + static const GDBusInterfaceInfo test_interface_info1 = { -1, @@ -1738,6 +1781,7 @@ main (int argc, loop = g_main_loop_new (NULL, FALSE); g_test_add_func ("/gdbus/object-registration", test_object_registration); + g_test_add_func ("/gdbus/object-registration-with-closures", test_object_registration_with_closures); g_test_add_func ("/gdbus/registered-interfaces", test_registered_interfaces); g_test_add_func ("/gdbus/async-properties", test_async_properties);