diff --git a/docs/reference/gio/gdbus-codegen.xml b/docs/reference/gio/gdbus-codegen.xml
index a0615cb8b..5efb3080b 100644
--- a/docs/reference/gio/gdbus-codegen.xml
+++ b/docs/reference/gio/gdbus-codegen.xml
@@ -435,6 +435,12 @@ gdbus-codegen --c-namespace MyApp \
upwards, as that is when gdbus-codegen was first
released.
+
+ Note that some version parameters introduce incompatible changes: all callers
+ of the generated code might need to be updated, and if the generated code is part of
+ a library's API or ABI, then increasing the version parameter can result in an API
+ or ABI break.
+
The version number must be of the form
MAJOR.MINOR.MICRO,
@@ -442,6 +448,15 @@ gdbus-codegen --c-namespace MyApp \
MICRO are optional. The version number may not be smaller
than 2.30.
+
+ If the version number is 2.64 or greater, the generated code will
+ have the following features: (1) If a method has h (file
+ descriptor) parameter(s), a GUnixFDList parameter will exist in the
+ generated code for it (whereas previously the annotation
+ org.gtk.GDBus.C.UnixFD was required), and (2) Method call functions will
+ have two additional arguments to allow the user to specify GDBusCallFlags
+ and a timeout value, as is possible when using g_dbus_proxy_call().
+
diff --git a/gio/gdbus-2.0/codegen/codegen.py b/gio/gdbus-2.0/codegen/codegen.py
index b0aa5865e..41481b8c2 100644
--- a/gio/gdbus-2.0/codegen/codegen.py
+++ b/gio/gdbus-2.0/codegen/codegen.py
@@ -73,6 +73,10 @@ class HeaderCodeGenerator:
self.glib_min_version = glib_min_version
self.outfile = outfile
+ self.glib_min_version_is_2_64 = (glib_min_version[0] > 2 or
+ (glib_min_version[0] == 2 and
+ glib_min_version[1] >= 64))
+
# ----------------------------------------------------------------------------------------------------
def generate_header_preamble(self):
@@ -222,6 +226,9 @@ class HeaderCodeGenerator:
' %s *proxy'%(i.name_lower, m.name_lower, i.camel_name))
for a in m.in_args:
self.outfile.write(',\n %sarg_%s'%(a.ctype_in, a.name))
+ if self.glib_min_version_is_2_64:
+ self.outfile.write(',\n GDBusCallFlags call_flags'
+ ',\n gint timeout_msec')
if m.unix_fd:
self.outfile.write(',\n GUnixFDList *fd_list')
self.outfile.write(',\n'
@@ -249,6 +256,9 @@ class HeaderCodeGenerator:
' %s *proxy'%(i.name_lower, m.name_lower, i.camel_name))
for a in m.in_args:
self.outfile.write(',\n %sarg_%s'%(a.ctype_in, a.name))
+ if self.glib_min_version_is_2_64:
+ self.outfile.write(',\n GDBusCallFlags call_flags'
+ ',\n gint timeout_msec')
if m.unix_fd:
self.outfile.write(',\n GUnixFDList *fd_list')
for a in m.out_args:
@@ -916,6 +926,10 @@ class CodeGenerator:
self.glib_min_version = glib_min_version
self.outfile = outfile
+ self.glib_min_version_is_2_64 = (glib_min_version[0] > 2 or
+ (glib_min_version[0] == 2 and
+ glib_min_version[1] >= 64))
+
# ----------------------------------------------------------------------------------------------------
def generate_body_preamble(self):
@@ -1666,6 +1680,11 @@ class CodeGenerator:
%(i.name_lower, m.name_lower, i.camel_name))
for a in m.in_args:
self.outfile.write(' * @arg_%s: Argument to pass with the method invocation.\n'%(a.name))
+ if self.glib_min_version_is_2_64:
+ self.outfile.write(' * @call_flags: Flags from the #GDBusCallFlags enumeration. If you want to allow interactive\n'
+ ' authorization be sure to set %G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION.\n'
+ ' * @timeout_msec: The timeout in milliseconds (with %G_MAXINT meaning "infinite") or\n'
+ ' -1 to use the proxy default timeout.\n')
if m.unix_fd:
self.outfile.write(' * @fd_list: (nullable): A #GUnixFDList or %NULL.\n')
self.outfile.write(self.docbook_gen.expand(
@@ -1685,6 +1704,9 @@ class CodeGenerator:
' %s *proxy'%(i.name_lower, m.name_lower, i.camel_name))
for a in m.in_args:
self.outfile.write(',\n %sarg_%s'%(a.ctype_in, a.name))
+ if self.glib_min_version_is_2_64:
+ self.outfile.write(',\n GDBusCallFlags call_flags'
+ ',\n gint timeout_msec')
if m.unix_fd:
self.outfile.write(',\n GUnixFDList *fd_list')
self.outfile.write(',\n'
@@ -1703,9 +1725,13 @@ class CodeGenerator:
self.outfile.write(')"')
for a in m.in_args:
self.outfile.write(',\n arg_%s'%(a.name))
- self.outfile.write('),\n'
- ' G_DBUS_CALL_FLAGS_NONE,\n'
- ' -1,\n')
+ self.outfile.write('),\n')
+ if self.glib_min_version_is_2_64:
+ self.outfile.write(' call_flags,\n'
+ ' timeout_msec,\n')
+ else:
+ self.outfile.write(' G_DBUS_CALL_FLAGS_NONE,\n'
+ ' -1,\n')
if m.unix_fd:
self.outfile.write(' fd_list,\n')
self.outfile.write(' cancellable,\n'
@@ -1771,6 +1797,11 @@ class CodeGenerator:
%(i.name_lower, m.name_lower, i.camel_name))
for a in m.in_args:
self.outfile.write(' * @arg_%s: Argument to pass with the method invocation.\n'%(a.name))
+ if self.glib_min_version_is_2_64:
+ self.outfile.write(' * @call_flags: Flags from the #GDBusCallFlags enumeration. If you want to allow interactive\n'
+ ' authorization be sure to set %G_DBUS_CALL_FLAGS_ALLOW_INTERACTIVE_AUTHORIZATION.\n'
+ ' * @timeout_msec: The timeout in milliseconds (with %G_MAXINT meaning "infinite") or\n'
+ ' -1 to use the proxy default timeout.\n')
if m.unix_fd:
self.outfile.write(' * @fd_list: (nullable): A #GUnixFDList or %NULL.\n')
for a in m.out_args:
@@ -1793,6 +1824,9 @@ class CodeGenerator:
' %s *proxy'%(i.name_lower, m.name_lower, i.camel_name))
for a in m.in_args:
self.outfile.write(',\n %sarg_%s'%(a.ctype_in, a.name))
+ if self.glib_min_version_is_2_64:
+ self.outfile.write(',\n GDBusCallFlags call_flags'
+ ',\n gint timeout_msec')
if m.unix_fd:
self.outfile.write(',\n GUnixFDList *fd_list')
for a in m.out_args:
@@ -1815,9 +1849,13 @@ class CodeGenerator:
self.outfile.write(')"')
for a in m.in_args:
self.outfile.write(',\n arg_%s'%(a.name))
- self.outfile.write('),\n'
- ' G_DBUS_CALL_FLAGS_NONE,\n'
- ' -1,\n')
+ self.outfile.write('),\n')
+ if self.glib_min_version_is_2_64:
+ self.outfile.write(' call_flags,\n'
+ ' timeout_msec,\n')
+ else:
+ self.outfile.write(' G_DBUS_CALL_FLAGS_NONE,\n'
+ ' -1,\n')
if m.unix_fd:
self.outfile.write(' fd_list,\n'
' out_fd_list,\n')
diff --git a/gio/tests/codegen.py b/gio/tests/codegen.py
index dc2c9ff1a..9d8bfadd1 100644
--- a/gio/tests/codegen.py
+++ b/gio/tests/codegen.py
@@ -447,6 +447,44 @@ G_END_DECLS
self.assertEqual('', result.err)
self.assertEqual(result.out.strip().count('GUnixFDList'), 18)
+ def test_call_flags_and_timeout_method_args(self):
+ """Test that generated method call functions have @call_flags and
+ @timeout_msec args if and only if GLib >= 2.64.
+ """
+ interface_xml = '''
+
+
+
+
+ '''
+
+ # Try without specifying --glib-min-version.
+ result = self.runCodegenWithInterface(interface_xml,
+ '--output', '/dev/stdout',
+ '--header')
+ self.assertEqual('', result.err)
+ self.assertEqual(result.out.strip().count('GDBusCallFlags call_flags,'), 0)
+ self.assertEqual(result.out.strip().count('gint timeout_msec,'), 0)
+
+ # Specify an old --glib-min-version.
+ result = self.runCodegenWithInterface(interface_xml,
+ '--output', '/dev/stdout',
+ '--header',
+ '--glib-min-version', '2.32')
+ self.assertEqual('', result.err)
+ self.assertEqual(result.out.strip().count('GDBusCallFlags call_flags,'), 0)
+ self.assertEqual(result.out.strip().count('gint timeout_msec,'), 0)
+
+ # Specify a --glib-min-version ≥ 2.64. The two arguments should be
+ # present for both the async and sync method call functions.
+ result = self.runCodegenWithInterface(interface_xml,
+ '--output', '/dev/stdout',
+ '--header',
+ '--glib-min-version', '2.64')
+ self.assertEqual('', result.err)
+ self.assertEqual(result.out.strip().count('GDBusCallFlags call_flags,'), 2)
+ self.assertEqual(result.out.strip().count('gint timeout_msec,'), 2)
+
if __name__ == '__main__':
unittest.main(testRunner=taptestrunner.TAPTestRunner())
diff --git a/gio/tests/gdbus-test-codegen.c b/gio/tests/gdbus-test-codegen.c
index c085af84d..604c85c03 100644
--- a/gio/tests/gdbus-test-codegen.c
+++ b/gio/tests/gdbus-test-codegen.c
@@ -25,7 +25,12 @@
#include "gdbus-tests.h"
+#if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_64
+#include "gdbus-test-codegen-generated-min-version-2-64.h"
+#else
#include "gdbus-test-codegen-generated.h"
+#endif
+
#include "gdbus-test-codegen-generated-interface-info.h"
/* ---------------------------------------------------------------------------------------------------- */
@@ -878,6 +883,10 @@ check_bar_proxy (FooiGenBar *proxy,
"/a/path",
"asig",
"bytestring\xff",
+#if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_64
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+#endif
&ret_val_byte,
&ret_val_boolean,
&ret_val_int16,
@@ -913,6 +922,10 @@ check_bar_proxy (FooiGenBar *proxy,
array_of_objpaths,
array_of_signatures,
array_of_bytestrings,
+#if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_64
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+#endif
&ret_array_of_strings,
&ret_array_of_objpaths,
&ret_array_of_signatures,
@@ -946,7 +959,11 @@ check_bar_proxy (FooiGenBar *proxy,
* unimplemented methods.
*/
error = NULL;
+#if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_64
+ ret = foo_igen_bar_call_unimplemented_method_sync (proxy, G_DBUS_CALL_FLAGS_NONE, -1, NULL /* GCancellable */, &error);
+#else
ret = foo_igen_bar_call_unimplemented_method_sync (proxy, NULL /* GCancellable */, &error);
+#endif
g_assert_error (error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD);
g_error_free (error);
error = NULL;
@@ -957,7 +974,11 @@ check_bar_proxy (FooiGenBar *proxy,
G_CALLBACK (on_test_signal),
data);
error = NULL;
+#if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_64
+ ret = foo_igen_bar_call_request_signal_emission_sync (proxy, 0, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
+#else
ret = foo_igen_bar_call_request_signal_emission_sync (proxy, 0, NULL, &error);
+#endif
g_assert_no_error (error);
g_assert (ret);
@@ -1011,7 +1032,11 @@ check_bar_proxy (FooiGenBar *proxy,
G_CALLBACK (on_g_properties_changed),
data);
error = NULL;
+#if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_64
+ ret = foo_igen_bar_call_request_multi_property_mods_sync (proxy, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
+#else
ret = foo_igen_bar_call_request_multi_property_mods_sync (proxy, NULL, &error);
+#endif
g_assert_no_error (error);
g_assert (ret);
g_main_loop_run (thread_loop);
@@ -1077,6 +1102,10 @@ check_bar_proxy (FooiGenBar *proxy,
*/
error = NULL;
foo_igen_bar_call_property_cancellation (proxy,
+#if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_64
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+#endif
NULL, /* GCancellable */
(GAsyncReadyCallback) on_property_cancellation_cb,
data);
@@ -1159,6 +1188,10 @@ check_bat_proxy (FooiGenBat *proxy,
g_variant_new_string ("a string"),
g_variant_new_bytestring ("a bytestring\xff"),
g_variant_new ("(i)", 4200),
+#if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_64
+ G_DBUS_CALL_FLAGS_NONE,
+ -1,
+#endif
&ret_i,
&ret_s,
&ret_ay,
@@ -1191,18 +1224,30 @@ check_authorize_proxy (FooiGenAuthorize *proxy,
/* Check that g-authorize-method works as intended */
error = NULL;
+#if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_64
+ ret = foo_igen_authorize_call_check_not_authorized_sync (proxy, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
+#else
ret = foo_igen_authorize_call_check_not_authorized_sync (proxy, NULL, &error);
+#endif
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED);
g_error_free (error);
g_assert (!ret);
error = NULL;
+#if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_64
+ ret = foo_igen_authorize_call_check_authorized_sync (proxy, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
+#else
ret = foo_igen_authorize_call_check_authorized_sync (proxy, NULL, &error);
+#endif
g_assert_no_error (error);
g_assert (ret);
error = NULL;
+#if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_64
+ ret = foo_igen_authorize_call_check_not_authorized_from_object_sync (proxy, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error);
+#else
ret = foo_igen_authorize_call_check_not_authorized_from_object_sync (proxy, NULL, &error);
+#endif
g_assert_error (error, G_IO_ERROR, G_IO_ERROR_PENDING);
g_error_free (error);
g_assert (!ret);
@@ -1219,7 +1264,11 @@ get_self_via_proxy (FooiGenMethodThreads *proxy_1)
gpointer self;
error = NULL;
+#if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_64
+ ret = foo_igen_method_threads_call_get_self_sync (proxy_1, G_DBUS_CALL_FLAGS_NONE, -1, &self_str, NULL, &error);
+#else
ret = foo_igen_method_threads_call_get_self_sync (proxy_1, &self_str, NULL, &error);
+#endif
g_assert_no_error (error);
g_assert (ret);
@@ -2599,6 +2648,28 @@ handle_hello_fd (FooiGenFDPassing *object,
return TRUE;
}
+#if GLIB_VERSION_MIN_REQUIRED >= GLIB_VERSION_2_64
+static gboolean
+handle_no_annotation (FooiGenFDPassing *object,
+ GDBusMethodInvocation *invocation,
+ GUnixFDList *fd_list,
+ GVariant *arg_greeting,
+ const gchar *arg_greeting_locale)
+{
+ foo_igen_fdpassing_complete_no_annotation (object, invocation, fd_list, arg_greeting, arg_greeting_locale);
+ return TRUE;
+}
+
+static gboolean
+handle_no_annotation_nested (FooiGenFDPassing *object,
+ GDBusMethodInvocation *invocation,
+ GUnixFDList *fd_list,
+ GVariant *arg_files)
+{
+ foo_igen_fdpassing_complete_no_annotation_nested (object, invocation, fd_list);
+ return TRUE;
+}
+#else
static gboolean
handle_no_annotation (FooiGenFDPassing *object,
GDBusMethodInvocation *invocation,
@@ -2617,9 +2688,12 @@ handle_no_annotation_nested (FooiGenFDPassing *object,
foo_igen_fdpassing_complete_no_annotation_nested (object, invocation);
return TRUE;
}
+#endif
-/* Test that generated code for methods includes GUnixFDList arguments if and
- * only if the method is explicitly annotated as C.UnixFD.
+/* Test that generated code for methods includes GUnixFDList arguments
+ * unconditionally if the method is explicitly annotated as C.UnixFD, and only
+ * emits GUnixFDList arguments when there's merely an 'h' parameter if
+ * --glib-min-version=2.64 or greater.
*/
static void
test_unix_fd_list (void)
@@ -2631,10 +2705,12 @@ test_unix_fd_list (void)
/* This method is explicitly annotated. */
iface.handle_hello_fd = handle_hello_fd;
- /* This one is not annotated; even though it's got an in and out 'h' parameter, for
- * backwards compatibility we cannot emit GUnixFDList arguments.
+ /* This one is not annotated; even though it's got an in and out 'h'
+ * parameter, for backwards compatibility we cannot emit GUnixFDList
+ * arguments unless --glib-min-version >= 2.64 was used.
*/
iface.handle_no_annotation = handle_no_annotation;
+
/* This method has an 'h' inside a complex type. */
iface.handle_no_annotation_nested = handle_no_annotation_nested;
diff --git a/gio/tests/meson.build b/gio/tests/meson.build
index 224301e24..5832bfb43 100644
--- a/gio/tests/meson.build
+++ b/gio/tests/meson.build
@@ -230,7 +230,23 @@ if host_machine.system() != 'windows'
'--generate-docbook', 'gdbus-test-codegen-generated-doc',
annotate_args,
'@INPUT@'])
-
+ # Generate gdbus-test-codegen-generated-min-version-2-64.{c,h}
+ gdbus_test_codegen_generated_min_version_2_64 = custom_target('gdbus-test-codegen-generated-min-version-2-64',
+ input : ['test-codegen.xml'],
+ output : ['gdbus-test-codegen-generated-min-version-2-64.h',
+ 'gdbus-test-codegen-generated-min-version-2-64.c'],
+ depend_files : gdbus_codegen_built_files,
+ command : [python, gdbus_codegen,
+ '--glib-min-version', '2.64',
+ '--interface-prefix', 'org.project.',
+ '--output-directory', '@OUTDIR@',
+ '--generate-c-code', 'gdbus-test-codegen-generated-min-version-2-64',
+ '--c-generate-object-manager',
+ '--c-generate-autocleanup', 'all',
+ '--c-namespace', 'Foo_iGen',
+ '--generate-docbook', 'gdbus-test-codegen-generated-doc',
+ annotate_args,
+ '@INPUT@'])
gdbus_test_codegen_generated_interface_info = [
custom_target('gdbus-test-codegen-generated-interface-info-h',
input : ['test-codegen.xml'],
@@ -282,6 +298,7 @@ if host_machine.system() != 'windows'
'gdbus-proxy-well-known-name' : {'extra_sources' : extra_sources},
'gdbus-test-codegen' : {
'extra_sources' : [extra_sources, gdbus_test_codegen_generated, gdbus_test_codegen_generated_interface_info],
+ 'c_args' : ['-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_32'],
},
'gdbus-threading' : {
'extra_sources' : extra_sources,
@@ -300,6 +317,11 @@ if host_machine.system() != 'windows'
'c_args' : ['-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_36',
'-DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_36'],
},
+ 'gdbus-test-codegen-min-version-2-64' : {
+ 'source' : 'gdbus-test-codegen.c',
+ 'extra_sources' : [extra_sources, gdbus_test_codegen_generated_min_version_2_64, gdbus_test_codegen_generated_interface_info],
+ 'c_args' : ['-DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_64'],
+ },
'gapplication' : {'extra_sources' : extra_sources},
}