[glib] gdbus-codegen: Add support for new org.gtk.GDBus.C.UnixFD annotation



commit c404dbed11bc8bf4212d15719ef3a87ebf76efff
Author: David Zeuthen <davidz redhat com>
Date:   Thu Jul 21 16:03:27 2011 -0400

    gdbus-codegen: Add support for new org.gtk.GDBus.C.UnixFD annotation
    
    Also add convenience _with_unix_fd_list variants to GDBusConnection,
    GDBusProxy and GDBusMethodInvocation types to easily support this.
    
    Signed-off-by: David Zeuthen <davidz redhat com>

 docs/reference/gio/gdbus-codegen.xml |   21 +-
 docs/reference/gio/gio-sections.txt  |    7 +
 gio/gdbus-codegen/codegen.py         |  148 ++++++++--
 gio/gdbusconnection.c                |  557 +++++++++++++++++++++++-----------
 gio/gdbusconnection.h                |   30 ++
 gio/gdbusmethodinvocation.c          |   76 ++++-
 gio/gdbusmethodinvocation.h          |    3 +
 gio/gdbusproxy.c                     |  495 ++++++++++++++++++++++---------
 gio/gdbusproxy.h                     |   23 ++
 gio/gio.symbols                      |    7 +
 gio/tests/test-codegen.xml           |    8 +
 11 files changed, 1012 insertions(+), 363 deletions(-)
---
diff --git a/docs/reference/gio/gdbus-codegen.xml b/docs/reference/gio/gdbus-codegen.xml
index 33c4617..3a741e6 100644
--- a/docs/reference/gio/gdbus-codegen.xml
+++ b/docs/reference/gio/gdbus-codegen.xml
@@ -332,6 +332,18 @@ gdbus-codegen --c-namespace MyApp                           \
       </listitem>
     </varlistentry>
 
+    <varlistentry>
+      <term><literal>org.gtk.GDBus.C.UnixFD</literal></term>
+      <listitem>
+        <para>
+          If set to a non-empty string, the generated code will
+          include parameters to exchange file descriptors using the
+          #GUnixFDList type. This annotation can be used on
+          <literal>&lt;method&gt;</literal> elements.
+        </para>
+      </listitem>
+    </varlistentry>
+
   </variablelist>
 
   <para>
@@ -744,8 +756,7 @@ on_handle_hello_world (MyAppFrobber           *interface,
     <link linkend="G-VARIANT-TYPE-INT32:CAPS">'i'</link>,
     <link linkend="G-VARIANT-TYPE-UINT32:CAPS">'u'</link>,
     <link linkend="G-VARIANT-TYPE-INT64:CAPS">'x'</link>,
-    <link linkend="G-VARIANT-TYPE-UINT64:CAPS">'t'</link>,
-    <link linkend="G-VARIANT-TYPE-HANDLE:CAPS">'h'</link> and
+    <link linkend="G-VARIANT-TYPE-UINT64:CAPS">'t'</link> and
     <link linkend="G-VARIANT-TYPE-DOUBLE:CAPS">'d'</link>)
     ),
     strings (type-strings
@@ -780,11 +791,7 @@ on_handle_hello_world (MyAppFrobber           *interface,
     The generated C functions are guaranteed to not change their ABI
     that is, if a method, signal or property does not change its
     signature in the introspection XML, the generated C functions will
-    not change its C ABI either. One exception to this guarantee is if
-    you are using type <link
-    linkend="G-VARIANT-TYPE-HANDLE:CAPS">'h'</link> for passing file
-    descriptors on Unix. Future versions of gdbus-codegen will include
-    guarantees for this type as well.
+    not change its C ABI either.
   </para>
   <para>
     The ABI of the generated #GType<!-- -->s will be preserved only if
diff --git a/docs/reference/gio/gio-sections.txt b/docs/reference/gio/gio-sections.txt
index 87617a6..29f10c1 100644
--- a/docs/reference/gio/gio-sections.txt
+++ b/docs/reference/gio/gio-sections.txt
@@ -2468,6 +2468,9 @@ GDBusCallFlags
 g_dbus_connection_call
 g_dbus_connection_call_finish
 g_dbus_connection_call_sync
+g_dbus_connection_call_with_unix_fd_list
+g_dbus_connection_call_with_unix_fd_list_finish
+g_dbus_connection_call_with_unix_fd_list_sync
 g_dbus_connection_emit_signal
 GDBusSignalFlags
 GDBusSignalCallback
@@ -2522,6 +2525,7 @@ g_dbus_method_invocation_return_error_literal
 g_dbus_method_invocation_return_gerror
 g_dbus_method_invocation_return_dbus_error
 g_dbus_method_invocation_take_error
+g_dbus_method_invocation_return_value_with_unix_fd_list
 <SUBSECTION Standard>
 G_DBUS_METHOD_INVOCATION
 G_IS_DBUS_METHOD_INVOCATION
@@ -2602,6 +2606,9 @@ g_dbus_proxy_get_interface_info
 g_dbus_proxy_call
 g_dbus_proxy_call_finish
 g_dbus_proxy_call_sync
+g_dbus_proxy_call_with_unix_fd_list
+g_dbus_proxy_call_with_unix_fd_list_finish
+g_dbus_proxy_call_with_unix_fd_list_sync
 <SUBSECTION Standard>
 G_DBUS_PROXY
 G_IS_DBUS_PROXY
diff --git a/gio/gdbus-codegen/codegen.py b/gio/gdbus-codegen/codegen.py
index 3d29e56..00c59b8 100644
--- a/gio/gdbus-codegen/codegen.py
+++ b/gio/gdbus-codegen/codegen.py
@@ -63,6 +63,11 @@ class CodeGenerator:
                      '#include "%s"\n'
                      '\n'%(self.h.name))
 
+        self.c.write('#ifdef G_OS_UNIX\n'
+                     '#  include <gio/gunixfdlist.h>\n'
+                     '#endif\n'
+                     '\n')
+
         self.c.write('typedef struct\n'
                      '{\n'
                      '  GDBusArgInfo parent_struct;\n'
@@ -74,6 +79,7 @@ class CodeGenerator:
                      '{\n'
                      '  GDBusMethodInfo parent_struct;\n'
                      '  const gchar *signal_name;\n'
+                     '  gboolean pass_fdlist;\n'
                      '} _ExtendedGDBusMethodInfo;\n'
                      '\n')
 
@@ -242,10 +248,15 @@ class CodeGenerator:
             if len(i.methods) > 0:
                 self.h.write('\n')
                 for m in i.methods:
+                    unix_fd = False
+                    if utils.lookup_annotation(m.annotations, 'org.gtk.GDBus.C.UnixFD'):
+                        unix_fd = True
                     key = (m.since, '_method_%s'%m.name_lower)
                     value  = '  gboolean (*handle_%s) (\n'%(m.name_lower)
                     value += '    %s *object,\n'%(i.camel_name)
                     value += '    GDBusMethodInvocation *invocation'%()
+                    if unix_fd:
+                        value += ',\n    GUnixFDList *fd_list'
                     for a in m.in_args:
                         value += ',\n    %s%s'%(a.ctype_in, a.name)
                     value += ');\n\n'
@@ -291,11 +302,16 @@ class CodeGenerator:
                 self.h.write('\n')
                 self.h.write('/* D-Bus method call completion functions: */\n')
                 for m in i.methods:
+                    unix_fd = False
+                    if utils.lookup_annotation(m.annotations, 'org.gtk.GDBus.C.UnixFD'):
+                        unix_fd = True
                     if m.deprecated:
                         self.h.write('G_GNUC_DEPRECATED ')
                     self.h.write('void %s_complete_%s (\n'
                                  '    %s *object,\n'
                                  '    GDBusMethodInvocation *invocation'%(i.name_lower, m.name_lower, i.camel_name))
+                    if unix_fd:
+                        self.h.write(',\n    GUnixFDList *fd_list')
                     for a in m.out_args:
                         self.h.write(',\n    %s%s'%(a.ctype_in, a.name))
                     self.h.write(');\n')
@@ -322,6 +338,9 @@ class CodeGenerator:
                 self.h.write('\n')
                 self.h.write('/* D-Bus method calls: */\n')
                 for m in i.methods:
+                    unix_fd = False
+                    if utils.lookup_annotation(m.annotations, 'org.gtk.GDBus.C.UnixFD'):
+                        unix_fd = True
                     # async begin
                     if m.deprecated:
                         self.h.write('G_GNUC_DEPRECATED ')
@@ -329,6 +348,8 @@ class CodeGenerator:
                                  '    %s *proxy'%(i.name_lower, m.name_lower, i.camel_name))
                     for a in m.in_args:
                         self.h.write(',\n    %s%s'%(a.ctype_in, a.name))
+                    if unix_fd:
+                        self.h.write(',\n    GUnixFDList *fd_list')
                     self.h.write(',\n'
                                  '    GCancellable *cancellable,\n'
                                  '    GAsyncReadyCallback callback,\n'
@@ -341,6 +362,8 @@ class CodeGenerator:
                                  '    %s *proxy'%(i.name_lower, m.name_lower, i.camel_name))
                     for a in m.out_args:
                         self.h.write(',\n    %sout_%s'%(a.ctype_out, a.name))
+                    if unix_fd:
+                        self.h.write(',\n    GUnixFDList **out_fd_list')
                     self.h.write(',\n'
                                  '    GAsyncResult *res,\n'
                                  '    GError **error);\n')
@@ -352,8 +375,12 @@ class CodeGenerator:
                                  '    %s *proxy'%(i.name_lower, m.name_lower, i.camel_name))
                     for a in m.in_args:
                         self.h.write(',\n    %s%s'%(a.ctype_in, a.name))
+                    if unix_fd:
+                        self.h.write(',\n    GUnixFDList  *fd_list')
                     for a in m.out_args:
                         self.h.write(',\n    %sout_%s'%(a.ctype_out, a.name))
+                    if unix_fd:
+                        self.h.write(',\n    GUnixFDList **out_fd_list')
                     self.h.write(',\n'
                                  '    GCancellable *cancellable,\n'
                                  '    GError **error);\n')
@@ -749,6 +776,9 @@ class CodeGenerator:
 
             if len(i.methods) > 0:
                 for m in i.methods:
+                    unix_fd = False
+                    if utils.lookup_annotation(m.annotations, 'org.gtk.GDBus.C.UnixFD'):
+                        unix_fd = True
                     self.generate_args('_%s_method_info_%s_IN_ARG'%(i.name_lower, m.name_lower), m.in_args)
                     self.generate_args('_%s_method_info_%s_OUT_ARG'%(i.name_lower, m.name_lower), m.out_args)
 
@@ -772,8 +802,9 @@ class CodeGenerator:
                     else:
                         self.c.write('    (GDBusAnnotationInfo **) &_%s_method_%s_annotation_info_pointers\n'%(i.name_lower, m.name_lower))
                     self.c.write('  },\n'
-                                 '  "handle-%s"\n'
-                                 %(m.name_hyphen))
+                                 '  "handle-%s",\n'
+                                 '  %s\n'
+                                 %(m.name_hyphen, 'TRUE' if unix_fd else 'FALSE'))
                     self.c.write('};\n'
                                  '\n')
 
@@ -980,12 +1011,17 @@ class CodeGenerator:
         if len(i.methods) > 0:
             self.c.write('  /* GObject signals for incoming D-Bus method calls: */\n')
             for m in i.methods:
+                unix_fd = False
+                if utils.lookup_annotation(m.annotations, 'org.gtk.GDBus.C.UnixFD'):
+                    unix_fd = True
                 self.c.write(self.docbook_gen.expand(
                         '  /**\n'
                         '   * %s::handle-%s:\n'
                         '   * @object: A #%s.\n'
                         '   * @invocation: A #GDBusMethodInvocation.\n'
                         %(i.camel_name, m.name_hyphen, i.camel_name), False))
+                if unix_fd:
+                    self.c.write ('   * @fd_list: (allow-none): A #GUnixFDList or %NULL.\n')
                 for a in m.in_args:
                     self.c.write ('   * @%s: Argument passed by remote caller.\n'%(a.name))
                 self.c.write(self.docbook_gen.expand(
@@ -997,6 +1033,10 @@ class CodeGenerator:
                         '   * Returns: %%TRUE if the invocation was handled, %%FALSE to let other signal handlers run.\n'
                         %(i.name, m.name, i.name_lower, m.name_lower), False))
                 self.write_gtkdoc_deprecated_and_since_and_close(m, self.c, 2)
+                if unix_fd:
+                    extra_args = 2
+                else:
+                    extra_args = 1
                 self.c.write('  g_signal_new ("handle-%s",\n'
                              '    G_TYPE_FROM_INTERFACE (iface),\n'
                              '    G_SIGNAL_RUN_LAST,\n'
@@ -1007,7 +1047,9 @@ class CodeGenerator:
                              '    G_TYPE_BOOLEAN,\n'
                              '    %d,\n'
                              '    G_TYPE_DBUS_METHOD_INVOCATION'
-                             %(m.name_hyphen, i.camel_name, m.name_lower, len(m.in_args) + 1))
+                             %(m.name_hyphen, i.camel_name, m.name_lower, len(m.in_args) + extra_args))
+                if unix_fd:
+                    self.c.write(', G_TYPE_UNIX_FD_LIST')
                 for a in m.in_args:
                     self.c.write (', %s'%(a.gtype))
                 self.c.write(');\n')
@@ -1212,6 +1254,9 @@ class CodeGenerator:
 
     def generate_method_calls(self, i):
         for m in i.methods:
+            unix_fd = False
+            if utils.lookup_annotation(m.annotations, 'org.gtk.GDBus.C.UnixFD'):
+                unix_fd = True
             # async begin
             self.c.write('/**\n'
                          ' * %s_call_%s:\n'
@@ -1219,6 +1264,8 @@ class CodeGenerator:
                          %(i.name_lower, m.name_lower, i.camel_name))
             for a in m.in_args:
                 self.c.write(' * @%s: Argument to pass with the method invocation.\n'%(a.name))
+            if unix_fd:
+                self.c.write(' * @fd_list: (allow-none): A #GUnixFDList or %NULL.\n')
             self.c.write(self.docbook_gen.expand(
                     ' * @cancellable: (allow-none): A #GCancellable or %%NULL.\n'
                     ' * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %%NULL.\n'
@@ -1236,13 +1283,18 @@ class CodeGenerator:
                          '    %s *proxy'%(i.name_lower, m.name_lower, i.camel_name))
             for a in m.in_args:
                 self.c.write(',\n    %s%s'%(a.ctype_in, a.name))
+            if unix_fd:
+                self.c.write(',\n    GUnixFDList *fd_list')
             self.c.write(',\n'
                          '    GCancellable *cancellable,\n'
                          '    GAsyncReadyCallback callback,\n'
                          '    gpointer user_data)\n'
                          '{\n')
-            self.c.write('  g_dbus_proxy_call (G_DBUS_PROXY (proxy),\n'
-                         '    "%s",\n'
+            if unix_fd:
+                self.c.write('  g_dbus_proxy_call_with_unix_fd_list (G_DBUS_PROXY (proxy),\n')
+            else:
+                self.c.write('  g_dbus_proxy_call (G_DBUS_PROXY (proxy),\n')
+            self.c.write('    "%s",\n'
                          '    g_variant_new ("('%(m.name))
             for a in m.in_args:
                 self.c.write('%s'%(a.format_in))
@@ -1251,8 +1303,10 @@ class CodeGenerator:
                 self.c.write(',\n                   %s'%(a.name))
             self.c.write('),\n'
                          '    G_DBUS_CALL_FLAGS_NONE,\n'
-                         '    -1,\n'
-                         '    cancellable,\n'
+                         '    -1,\n')
+            if unix_fd:
+                self.c.write('    fd_list,\n')
+            self.c.write('    cancellable,\n'
                          '    callback,\n'
                          '    user_data);\n')
             self.c.write('}\n'
@@ -1264,6 +1318,8 @@ class CodeGenerator:
                          %(i.name_lower, m.name_lower, i.camel_name))
             for a in m.out_args:
                 self.c.write(' * @out_%s: (out): Return location for return parameter or %%NULL to ignore.\n'%(a.name))
+            if unix_fd:
+                self.c.write(' * @out_fd_list: (out): Return location for a #GUnixFDList or %NULL.\n')
             self.c.write(self.docbook_gen.expand(
                     ' * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to %s_call_%s().\n'
                     ' * @error: Return location for error or %%NULL.\n'
@@ -1278,13 +1334,18 @@ class CodeGenerator:
                          '    %s *proxy'%(i.name_lower, m.name_lower, i.camel_name))
             for a in m.out_args:
                 self.c.write(',\n    %sout_%s'%(a.ctype_out, a.name))
+            if unix_fd:
+                self.c.write(',\n    GUnixFDList **out_fd_list')
             self.c.write(',\n'
                          '    GAsyncResult *res,\n'
                          '    GError **error)\n'
                          '{\n'
-                         '  GVariant *_ret;\n'
-                         '  _ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, error);\n'
-                         '  if (_ret == NULL)\n'
+                         '  GVariant *_ret;\n')
+            if unix_fd:
+                self.c.write('  _ret = g_dbus_proxy_call_with_unix_fd_list_finish (G_DBUS_PROXY (proxy), out_fd_list, res, error);\n')
+            else:
+                self.c.write('  _ret = g_dbus_proxy_call_finish (G_DBUS_PROXY (proxy), res, error);\n')
+            self.c.write('  if (_ret == NULL)\n'
                          '    goto _out;\n')
             self.c.write('  g_variant_get (_ret,\n'
                          '                 \"(')
@@ -1308,8 +1369,12 @@ class CodeGenerator:
                          %(i.name_lower, m.name_lower, i.camel_name))
             for a in m.in_args:
                 self.c.write(' * @%s: Argument to pass with the method invocation.\n'%(a.name))
+            if unix_fd:
+                self.c.write(' * @fd_list: (allow-none): A #GUnixFDList or %NULL.\n')
             for a in m.out_args:
                 self.c.write(' * @out_%s: (out): Return location for return parameter or %%NULL to ignore.\n'%(a.name))
+            if unix_fd:
+                self.c.write(' * @out_fd_list: (out): Return location for a #GUnixFDList or %NULL.\n')
             self.c.write(self.docbook_gen.expand(
                     ' * @cancellable: (allow-none): A #GCancellable or %%NULL.\n'
                     ' * @error: Return location for error or %%NULL.\n'
@@ -1326,15 +1391,22 @@ class CodeGenerator:
                          '    %s *proxy'%(i.name_lower, m.name_lower, i.camel_name))
             for a in m.in_args:
                 self.c.write(',\n    %s%s'%(a.ctype_in, a.name))
+            if unix_fd:
+                self.c.write(',\n    GUnixFDList  *fd_list')
             for a in m.out_args:
                 self.c.write(',\n    %sout_%s'%(a.ctype_out, a.name))
+            if unix_fd:
+                self.c.write(',\n    GUnixFDList **out_fd_list')
             self.c.write(',\n'
                          '    GCancellable *cancellable,\n'
                          '    GError **error)\n'
                          '{\n'
                          '  GVariant *_ret;\n')
-            self.c.write('  _ret = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy),\n'
-                         '    "%s",\n'
+            if unix_fd:
+                self.c.write('  _ret = g_dbus_proxy_call_with_unix_fd_list_sync (G_DBUS_PROXY (proxy),\n')
+            else:
+                self.c.write('  _ret = g_dbus_proxy_call_sync (G_DBUS_PROXY (proxy),\n')
+            self.c.write('    "%s",\n'
                          '    g_variant_new ("('%(m.name))
             for a in m.in_args:
                 self.c.write('%s'%(a.format_in))
@@ -1343,8 +1415,11 @@ class CodeGenerator:
                 self.c.write(',\n                   %s'%(a.name))
             self.c.write('),\n'
                          '    G_DBUS_CALL_FLAGS_NONE,\n'
-                         '    -1,\n'
-                         '    cancellable,\n'
+                         '    -1,\n')
+            if unix_fd:
+                self.c.write('    fd_list,\n'
+                             '    out_fd_list,\n')
+            self.c.write('    cancellable,\n'
                          '    error);\n'
                          '  if (_ret == NULL)\n'
                          '    goto _out;\n')
@@ -1366,11 +1441,16 @@ class CodeGenerator:
 
     def generate_method_completers(self, i):
         for m in i.methods:
+            unix_fd = False
+            if utils.lookup_annotation(m.annotations, 'org.gtk.GDBus.C.UnixFD'):
+                unix_fd = True
             self.c.write('/**\n'
                          ' * %s_complete_%s:\n'
                          ' * @object: A #%s.\n'
                          ' * @invocation: (transfer full): A #GDBusMethodInvocation.\n'
                          %(i.name_lower, m.name_lower, i.camel_name))
+            if unix_fd:
+                self.c.write (' * @fd_list: (allow-none): A #GUnixFDList or %NULL.\n')
             for a in m.out_args:
                 self.c.write(' * @%s: Parameter to return.\n'%(a.name))
             self.c.write(self.docbook_gen.expand(
@@ -1384,20 +1464,29 @@ class CodeGenerator:
                          '%s_complete_%s (\n'
                          '    %s *object,\n'
                          '    GDBusMethodInvocation *invocation'%(i.name_lower, m.name_lower, i.camel_name))
+            if unix_fd:
+                self.c.write(',\n    GUnixFDList *fd_list')
             for a in m.out_args:
                 self.c.write(',\n    %s%s'%(a.ctype_in, a.name))
             self.c.write(')\n'
                          '{\n')
 
-            self.c.write('  g_dbus_method_invocation_return_value (invocation,\n'
-                         '    g_variant_new ("(')
+            if unix_fd:
+                self.c.write('  g_dbus_method_invocation_return_value_with_unix_fd_list (invocation,\n'
+                             '    g_variant_new ("(')
+            else:
+                self.c.write('  g_dbus_method_invocation_return_value (invocation,\n'
+                             '    g_variant_new ("(')
             for a in m.out_args:
                 self.c.write('%s'%(a.format_in))
             self.c.write(')"')
             for a in m.out_args:
                 self.c.write(',\n                   %s'%(a.name))
-            self.c.write('));\n'
-                         '}\n'
+            if unix_fd:
+                self.c.write('),\n    fd_list);\n')
+            else:
+                self.c.write('));\n')
+            self.c.write('}\n'
                          '\n')
 
     # ---------------------------------------------------------------------------------------------------
@@ -1868,6 +1957,7 @@ class CodeGenerator:
                      '  GVariant *child;\n'
                      '  GValue *paramv;\n'
                      '  guint num_params;\n'
+                     '  guint num_extra;\n'
                      '  guint n;\n'
                      '  guint signal_id;\n'
                      '  GValue return_value = {0};\n'
@@ -1876,17 +1966,23 @@ class CodeGenerator:
                      '  g_assert (info != NULL);\n'
                      %())
         self.c.write ('  num_params = g_variant_n_children (parameters);\n'
-                      '  paramv = g_new0 (GValue, num_params + 2);\n'
-                      '  g_value_init (&paramv[0], %sTYPE_%s);\n'
-                      '  g_value_set_object (&paramv[0], skeleton);\n'
-                      '  g_value_init (&paramv[1], G_TYPE_DBUS_METHOD_INVOCATION);\n'
-                      '  g_value_set_object (&paramv[1], invocation);\n'
+                      '  num_extra = info->pass_fdlist ? 3 : 2;'
+                      '  paramv = g_new0 (GValue, num_params + num_extra);\n'
+                      '  n = 0;\n'
+                      '  g_value_init (&paramv[n], %sTYPE_%s);\n'
+                      '  g_value_set_object (&paramv[n++], skeleton);\n'
+                      '  g_value_init (&paramv[n], G_TYPE_DBUS_METHOD_INVOCATION);\n'
+                      '  g_value_set_object (&paramv[n++], invocation);\n'
+                      '  if (info->pass_fdlist)\n'
+                      '  {\n'
+                      '    g_value_init (&paramv[n], G_TYPE_UNIX_FD_LIST);\n'
+                      '    g_value_set_object (&paramv[n++], g_dbus_message_get_unix_fd_list (g_dbus_method_invocation_get_message (invocation)));\n'
+                      '  }\n'
                       %(i.ns_upper, i.name_upper))
         self.c.write('  g_variant_iter_init (&iter, parameters);\n'
-                     '  n = 2;\n'
                      '  while ((child = g_variant_iter_next_value (&iter)) != NULL)\n'
                      '    {\n'
-                     '      _ExtendedGDBusArgInfo *arg_info = (_ExtendedGDBusArgInfo *) info->parent_struct.in_args[n - 2];\n'
+                     '      _ExtendedGDBusArgInfo *arg_info = (_ExtendedGDBusArgInfo *) info->parent_struct.in_args[n - num_extra];\n'
                      '      if (arg_info->use_gvariant)\n'
                      '        {\n'
                      '          g_value_init (&paramv[n], G_TYPE_VARIANT);\n'
@@ -1906,7 +2002,7 @@ class CodeGenerator:
                      '    g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD, "Method %s is not implemented on interface %s", method_name, interface_name);\n'
                      '  g_value_unset (&return_value);\n'
                      )
-        self.c.write('  for (n = 0; n < num_params + 2; n++)\n'
+        self.c.write('  for (n = 0; n < num_params + num_extra; n++)\n'
                      '    g_value_unset (&paramv[n]);\n'
                      '  g_free (paramv);\n')
         self.c.write('}\n'
diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c
index fe12408..6aba57f 100644
--- a/gio/gdbusconnection.c
+++ b/gio/gdbusconnection.c
@@ -4901,6 +4901,7 @@ static GVariant *
 decode_method_reply (GDBusMessage        *reply,
                      const gchar         *method_name,
                      const GVariantType  *reply_type,
+                     GUnixFDList        **out_fd_list,
                      GError             **error)
 {
   GVariant *result;
@@ -4934,6 +4935,18 @@ decode_method_reply (GDBusMessage        *reply,
           g_free (type_string);
           result = NULL;
         }
+
+#ifdef G_OS_UNIX
+      if (result != NULL)
+        {
+          if (out_fd_list != NULL)
+            {
+              *out_fd_list = g_dbus_message_get_unix_fd_list (reply);
+              if (*out_fd_list != NULL)
+                g_object_ref (*out_fd_list);
+            }
+        }
+#endif
       break;
 
     case G_DBUS_MESSAGE_TYPE_ERROR:
@@ -4955,18 +4968,38 @@ typedef struct
   GVariantType *reply_type;
   gchar *method_name; /* for error message */
   guint32 serial;
+
+  GVariant *value;
+#ifdef G_OS_UNIX
+  GUnixFDList *fd_list;
+#endif
 } CallState;
 
 static void
+call_state_free (CallState *state)
+{
+  g_variant_type_free (state->reply_type);
+  g_free (state->method_name);
+
+  if (state->value != NULL)
+    g_variant_unref (state->value);
+#ifdef G_OS_UNIX
+  if (state->fd_list != NULL)
+    g_object_unref (state->fd_list);
+#endif
+  g_slice_free (CallState, state);
+}
+
+static void
 g_dbus_connection_call_done (GObject      *source,
                              GAsyncResult *result,
                              gpointer      user_data)
 {
+  GSimpleAsyncResult *simple;
   GDBusConnection *connection = G_DBUS_CONNECTION (source);
   CallState *state = user_data;
   GError *error;
   GDBusMessage *reply;
-  GVariant *value;
 
   error = NULL;
   reply = g_dbus_connection_send_message_with_reply_finish (connection,
@@ -4994,30 +5027,247 @@ g_dbus_connection_call_done (GObject      *source,
       _g_dbus_debug_print_unlock ();
     }
 
-
   if (reply != NULL)
+    state->value = decode_method_reply (reply, state->method_name, state->reply_type, &state->fd_list, &error);
+
+  simple = state->simple; /* why? because state is freed before we unref simple.. */
+  if (error != NULL)
     {
-      value = decode_method_reply (reply, state->method_name,
-                                   state->reply_type, &error);
-      g_object_unref (reply);
+      g_simple_async_result_take_error (state->simple, error);
+      g_simple_async_result_complete (state->simple);
+      call_state_free (state);
     }
   else
-    value = NULL;
+    {
+      g_simple_async_result_set_op_res_gpointer (state->simple, state, (GDestroyNotify) call_state_free);
+      g_simple_async_result_complete (state->simple);
+      g_object_unref (reply);
+    }
+  g_object_unref (simple);
+}
 
-  if (value == NULL)
-    g_simple_async_result_take_error (state->simple, error);
-  else
-    g_simple_async_result_set_op_res_gpointer (state->simple, value,
-                                               (GDestroyNotify) g_variant_unref);
+static void
+g_dbus_connection_call_internal (GDBusConnection        *connection,
+                                 const gchar            *bus_name,
+                                 const gchar            *object_path,
+                                 const gchar            *interface_name,
+                                 const gchar            *method_name,
+                                 GVariant               *parameters,
+                                 const GVariantType     *reply_type,
+                                 GDBusCallFlags          flags,
+                                 gint                    timeout_msec,
+                                 GUnixFDList            *fd_list,
+                                 GCancellable           *cancellable,
+                                 GAsyncReadyCallback     callback,
+                                 gpointer                user_data)
+{
+  GDBusMessage *message;
+  CallState *state;
 
-  g_simple_async_result_complete (state->simple);
-  g_variant_type_free (state->reply_type);
-  g_object_unref (state->simple);
-  g_free (state->method_name);
+  g_return_if_fail (G_IS_DBUS_CONNECTION (connection));
+  g_return_if_fail (bus_name == NULL || g_dbus_is_name (bus_name));
+  g_return_if_fail (object_path != NULL && g_variant_is_object_path (object_path));
+  g_return_if_fail (interface_name != NULL && g_dbus_is_interface_name (interface_name));
+  g_return_if_fail (method_name != NULL && g_dbus_is_member_name (method_name));
+  g_return_if_fail (timeout_msec >= 0 || timeout_msec == -1);
+  g_return_if_fail ((parameters == NULL) || g_variant_is_of_type (parameters, G_VARIANT_TYPE_TUPLE));
+  g_return_if_fail (fd_list == NULL || G_IS_UNIX_FD_LIST (fd_list));
 
-  g_slice_free (CallState, state);
+  state = g_slice_new0 (CallState);
+  state->simple = g_simple_async_result_new (G_OBJECT (connection),
+                                             callback, user_data,
+                                             g_dbus_connection_call_internal);
+  state->method_name = g_strjoin (".", interface_name, method_name, NULL);
+
+  if (reply_type == NULL)
+    reply_type = G_VARIANT_TYPE_ANY;
+
+  state->reply_type = g_variant_type_copy (reply_type);
+
+  message = g_dbus_message_new_method_call (bus_name,
+                                            object_path,
+                                            interface_name,
+                                            method_name);
+  add_call_flags (message, flags);
+  if (parameters != NULL)
+    g_dbus_message_set_body (message, parameters);
+
+#ifdef G_OS_UNIX
+  if (fd_list != NULL)
+    g_dbus_message_set_unix_fd_list (message, fd_list);
+#endif
+
+  g_dbus_connection_send_message_with_reply (connection,
+                                             message,
+                                             G_DBUS_SEND_MESSAGE_FLAGS_NONE,
+                                             timeout_msec,
+                                             &state->serial,
+                                             cancellable,
+                                             g_dbus_connection_call_done,
+                                             state);
+
+  if (G_UNLIKELY (_g_dbus_debug_call ()))
+    {
+      _g_dbus_debug_print_lock ();
+      g_print ("========================================================================\n"
+               "GDBus-debug:Call:\n"
+               " >>>> ASYNC %s.%s()\n"
+               "      on object %s\n"
+               "      owned by name %s (serial %d)\n",
+               interface_name,
+               method_name,
+               object_path,
+               bus_name != NULL ? bus_name : "(none)",
+               state->serial);
+      _g_dbus_debug_print_unlock ();
+    }
+
+  if (message != NULL)
+    g_object_unref (message);
+}
+
+static GVariant *
+g_dbus_connection_call_finish_internal (GDBusConnection  *connection,
+                                        GUnixFDList     **out_fd_list,
+                                        GAsyncResult     *res,
+                                        GError          **error)
+{
+  GSimpleAsyncResult *simple;
+  CallState *state;
+
+  g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
+  g_return_val_if_fail (g_simple_async_result_is_valid (res, G_OBJECT (connection),
+                                                        g_dbus_connection_call_internal), NULL);
+  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+  simple = G_SIMPLE_ASYNC_RESULT (res);
+
+  if (g_simple_async_result_propagate_error (simple, error))
+    return NULL;
+
+  state = g_simple_async_result_get_op_res_gpointer (simple);
+  if (out_fd_list != NULL)
+    *out_fd_list = state->fd_list != NULL ? g_object_ref (state->fd_list) : NULL;
+  return g_variant_ref (state->value);
+}
+
+static GVariant *
+g_dbus_connection_call_sync_internal (GDBusConnection         *connection,
+                                      const gchar             *bus_name,
+                                      const gchar             *object_path,
+                                      const gchar             *interface_name,
+                                      const gchar             *method_name,
+                                      GVariant                *parameters,
+                                      const GVariantType      *reply_type,
+                                      GDBusCallFlags           flags,
+                                      gint                     timeout_msec,
+                                      GUnixFDList             *fd_list,
+                                      GUnixFDList            **out_fd_list,
+                                      GCancellable            *cancellable,
+                                      GError                 **error)
+{
+  GDBusMessage *message;
+  GDBusMessage *reply;
+  GVariant *result;
+  GError *local_error;
+
+  message = NULL;
+  reply = NULL;
+  result = NULL;
+
+  g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
+  g_return_val_if_fail (bus_name == NULL || g_dbus_is_name (bus_name), NULL);
+  g_return_val_if_fail (object_path != NULL && g_variant_is_object_path (object_path), NULL);
+  g_return_val_if_fail (interface_name != NULL && g_dbus_is_interface_name (interface_name), NULL);
+  g_return_val_if_fail (method_name != NULL && g_dbus_is_member_name (method_name), NULL);
+  g_return_val_if_fail (timeout_msec >= 0 || timeout_msec == -1, NULL);
+  g_return_val_if_fail ((parameters == NULL) || g_variant_is_of_type (parameters, G_VARIANT_TYPE_TUPLE), NULL);
+  g_return_val_if_fail (fd_list == NULL || G_IS_UNIX_FD_LIST (fd_list), NULL);
+  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
+
+  if (reply_type == NULL)
+    reply_type = G_VARIANT_TYPE_ANY;
+
+  message = g_dbus_message_new_method_call (bus_name,
+                                            object_path,
+                                            interface_name,
+                                            method_name);
+  add_call_flags (message, flags);
+  if (parameters != NULL)
+    g_dbus_message_set_body (message, parameters);
+
+#ifdef G_OS_UNIX
+  if (fd_list != NULL)
+    g_dbus_message_set_unix_fd_list (message, fd_list);
+#endif
+
+  if (G_UNLIKELY (_g_dbus_debug_call ()))
+    {
+      _g_dbus_debug_print_lock ();
+      g_print ("========================================================================\n"
+               "GDBus-debug:Call:\n"
+               " >>>> SYNC %s.%s()\n"
+               "      on object %s\n"
+               "      owned by name %s\n",
+               interface_name,
+               method_name,
+               object_path,
+               bus_name != NULL ? bus_name : "(none)");
+      _g_dbus_debug_print_unlock ();
+    }
+
+  local_error = NULL;
+  reply = g_dbus_connection_send_message_with_reply_sync (connection,
+                                                          message,
+							  G_DBUS_SEND_MESSAGE_FLAGS_NONE,
+                                                          timeout_msec,
+                                                          NULL, /* volatile guint32 *out_serial */
+                                                          cancellable,
+                                                          &local_error);
+
+  if (G_UNLIKELY (_g_dbus_debug_call ()))
+    {
+      _g_dbus_debug_print_lock ();
+      g_print ("========================================================================\n"
+               "GDBus-debug:Call:\n"
+               " <<<< SYNC COMPLETE %s.%s()\n"
+               "      ",
+               interface_name,
+               method_name);
+      if (reply != NULL)
+        {
+          g_print ("SUCCESS\n");
+        }
+      else
+        {
+          g_print ("FAILED: %s\n",
+                   local_error->message);
+        }
+      _g_dbus_debug_print_unlock ();
+    }
+
+  if (reply == NULL)
+    {
+      if (error != NULL)
+        *error = local_error;
+      else
+        g_error_free (local_error);
+      goto out;
+    }
+
+  result = decode_method_reply (reply, method_name, reply_type, out_fd_list, error);
+
+ out:
+  if (message != NULL)
+    g_object_unref (message);
+  if (reply != NULL)
+    g_object_unref (reply);
+
+  return result;
 }
 
+/* ---------------------------------------------------------------------------------------------------- */
+
 /**
  * g_dbus_connection_call:
  * @connection: A #GDBusConnection.
@@ -5094,63 +5344,7 @@ g_dbus_connection_call (GDBusConnection        *connection,
                         GAsyncReadyCallback     callback,
                         gpointer                user_data)
 {
-  GDBusMessage *message;
-  CallState *state;
-
-  g_return_if_fail (G_IS_DBUS_CONNECTION (connection));
-  g_return_if_fail (bus_name == NULL || g_dbus_is_name (bus_name));
-  g_return_if_fail (object_path != NULL && g_variant_is_object_path (object_path));
-  g_return_if_fail (interface_name != NULL && g_dbus_is_interface_name (interface_name));
-  g_return_if_fail (method_name != NULL && g_dbus_is_member_name (method_name));
-  g_return_if_fail (timeout_msec >= 0 || timeout_msec == -1);
-  g_return_if_fail ((parameters == NULL) || g_variant_is_of_type (parameters, G_VARIANT_TYPE_TUPLE));
-
-  state = g_slice_new (CallState);
-  state->simple = g_simple_async_result_new (G_OBJECT (connection),
-                                             callback, user_data,
-                                             g_dbus_connection_call);
-  state->method_name = g_strjoin (".", interface_name, method_name, NULL);
-
-  if (reply_type == NULL)
-    reply_type = G_VARIANT_TYPE_ANY;
-
-  state->reply_type = g_variant_type_copy (reply_type);
-
-  message = g_dbus_message_new_method_call (bus_name,
-                                            object_path,
-                                            interface_name,
-                                            method_name);
-  add_call_flags (message, flags);
-  if (parameters != NULL)
-    g_dbus_message_set_body (message, parameters);
-
-  g_dbus_connection_send_message_with_reply (connection,
-                                             message,
-                                             G_DBUS_SEND_MESSAGE_FLAGS_NONE,
-                                             timeout_msec,
-                                             &state->serial,
-                                             cancellable,
-                                             g_dbus_connection_call_done,
-                                             state);
-
-  if (G_UNLIKELY (_g_dbus_debug_call ()))
-    {
-      _g_dbus_debug_print_lock ();
-      g_print ("========================================================================\n"
-               "GDBus-debug:Call:\n"
-               " >>>> ASYNC %s.%s()\n"
-               "      on object %s\n"
-               "      owned by name %s (serial %d)\n",
-               interface_name,
-               method_name,
-               object_path,
-               bus_name != NULL ? bus_name : "(none)",
-               state->serial);
-      _g_dbus_debug_print_unlock ();
-    }
-
-  if (message != NULL)
-    g_object_unref (message);
+  return g_dbus_connection_call_internal (connection, bus_name, object_path, interface_name, method_name, parameters, reply_type, flags, timeout_msec, NULL, cancellable, callback, user_data);
 }
 
 /**
@@ -5171,23 +5365,9 @@ g_dbus_connection_call_finish (GDBusConnection  *connection,
                                GAsyncResult     *res,
                                GError          **error)
 {
-  GSimpleAsyncResult *simple;
-
-  g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
-  g_return_val_if_fail (g_simple_async_result_is_valid (res, G_OBJECT (connection),
-                                                        g_dbus_connection_call), NULL);
-  g_return_val_if_fail (error == NULL || *error == NULL, NULL);
-
-  simple = G_SIMPLE_ASYNC_RESULT (res);
-
-  if (g_simple_async_result_propagate_error (simple, error))
-    return NULL;
-
-  return g_variant_ref (g_simple_async_result_get_op_res_gpointer (simple));
+  return g_dbus_connection_call_finish_internal (connection, NULL, res, error);
 }
 
-/* ---------------------------------------------------------------------------------------------------- */
-
 /**
  * g_dbus_connection_call_sync:
  * @connection: A #GDBusConnection.
@@ -5259,99 +5439,128 @@ g_dbus_connection_call_sync (GDBusConnection         *connection,
                              GCancellable            *cancellable,
                              GError                 **error)
 {
-  GDBusMessage *message;
-  GDBusMessage *reply;
-  GVariant *result;
-  GError *local_error;
-
-  message = NULL;
-  reply = NULL;
-  result = NULL;
-
-  g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
-  g_return_val_if_fail (bus_name == NULL || g_dbus_is_name (bus_name), NULL);
-  g_return_val_if_fail (object_path != NULL && g_variant_is_object_path (object_path), NULL);
-  g_return_val_if_fail (interface_name != NULL && g_dbus_is_interface_name (interface_name), NULL);
-  g_return_val_if_fail (method_name != NULL && g_dbus_is_member_name (method_name), NULL);
-  g_return_val_if_fail (timeout_msec >= 0 || timeout_msec == -1, NULL);
-  g_return_val_if_fail ((parameters == NULL) || g_variant_is_of_type (parameters, G_VARIANT_TYPE_TUPLE), NULL);
-
-  if (reply_type == NULL)
-    reply_type = G_VARIANT_TYPE_ANY;
-
-  message = g_dbus_message_new_method_call (bus_name,
-                                            object_path,
-                                            interface_name,
-                                            method_name);
-  add_call_flags (message, flags);
-  if (parameters != NULL)
-    g_dbus_message_set_body (message, parameters);
-
-  if (G_UNLIKELY (_g_dbus_debug_call ()))
-    {
-      _g_dbus_debug_print_lock ();
-      g_print ("========================================================================\n"
-               "GDBus-debug:Call:\n"
-               " >>>> SYNC %s.%s()\n"
-               "      on object %s\n"
-               "      owned by name %s\n",
-               interface_name,
-               method_name,
-               object_path,
-               bus_name != NULL ? bus_name : "(none)");
-      _g_dbus_debug_print_unlock ();
-    }
-
-  local_error = NULL;
-  reply = g_dbus_connection_send_message_with_reply_sync (connection,
-                                                          message,
-							  G_DBUS_SEND_MESSAGE_FLAGS_NONE,
-                                                          timeout_msec,
-                                                          NULL, /* volatile guint32 *out_serial */
-                                                          cancellable,
-                                                          &local_error);
+  return g_dbus_connection_call_sync_internal (connection, bus_name, object_path, interface_name, method_name, parameters, reply_type, flags, timeout_msec, NULL, NULL, cancellable, error);
+}
 
-  if (G_UNLIKELY (_g_dbus_debug_call ()))
-    {
-      _g_dbus_debug_print_lock ();
-      g_print ("========================================================================\n"
-               "GDBus-debug:Call:\n"
-               " <<<< SYNC COMPLETE %s.%s()\n"
-               "      ",
-               interface_name,
-               method_name);
-      if (reply != NULL)
-        {
-          g_print ("SUCCESS\n");
-        }
-      else
-        {
-          g_print ("FAILED: %s\n",
-                   local_error->message);
-        }
-      _g_dbus_debug_print_unlock ();
-    }
+/* ---------------------------------------------------------------------------------------------------- */
 
-  if (reply == NULL)
-    {
-      if (error != NULL)
-        *error = local_error;
-      else
-        g_error_free (local_error);
-      goto out;
-    }
+#ifdef G_OS_UNIX
 
-  result = decode_method_reply (reply, method_name, reply_type, error);
+/**
+ * g_dbus_connection_call_with_unix_fd_list:
+ * @connection: A #GDBusConnection.
+ * @bus_name: (allow-none): A unique or well-known bus name or %NULL if
+ *            @connection is not a message bus connection.
+ * @object_path: Path of remote object.
+ * @interface_name: D-Bus interface to invoke method on.
+ * @method_name: The name of the method to invoke.
+ * @parameters: (allow-none): A #GVariant tuple with parameters for the method
+ *              or %NULL if not passing parameters.
+ * @reply_type: (allow-none): The expected type of the reply, or %NULL.
+ * @flags: Flags from the #GDBusCallFlags enumeration.
+ * @timeout_msec: The timeout in milliseconds, -1 to use the default
+ *                timeout or %G_MAXINT for no timeout.
+ * @fd_list: (allow-none): A #GUnixFDList or %NULL.
+ * @cancellable: A #GCancellable or %NULL.
+ * @callback: (allow-none): A #GAsyncReadyCallback to call when the request is
+ *            satisfied or %NULL if you don't * care about the result of the
+ *            method invocation.
+ * @user_data: The data to pass to @callback.
+ *
+ * Like g_dbus_connection_call() but also takes a #GUnixFDList object.
+ *
+ * This method is only available on UNIX.
+ *
+ * Since: 2.30
+ */
+void
+g_dbus_connection_call_with_unix_fd_list (GDBusConnection        *connection,
+                                          const gchar            *bus_name,
+                                          const gchar            *object_path,
+                                          const gchar            *interface_name,
+                                          const gchar            *method_name,
+                                          GVariant               *parameters,
+                                          const GVariantType     *reply_type,
+                                          GDBusCallFlags          flags,
+                                          gint                    timeout_msec,
+                                          GUnixFDList            *fd_list,
+                                          GCancellable           *cancellable,
+                                          GAsyncReadyCallback     callback,
+                                          gpointer                user_data)
+{
+  return g_dbus_connection_call_internal (connection, bus_name, object_path, interface_name, method_name, parameters, reply_type, flags, timeout_msec, fd_list, cancellable, callback, user_data);
+}
 
- out:
-  if (message != NULL)
-    g_object_unref (message);
-  if (reply != NULL)
-    g_object_unref (reply);
+/**
+ * g_dbus_connection_call_with_unix_fd_list_finish:
+ * @connection: A #GDBusConnection.
+ * @out_fd_list: (out): Return location for a #GUnixFDList or %NULL.
+ * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_dbus_connection_call_with_unix_fd_list().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with g_dbus_connection_call_with_unix_fd_list().
+ *
+ * Returns: %NULL if @error is set. Otherwise a #GVariant tuple with
+ * return values. Free with g_variant_unref().
+ *
+ * Since: 2.30
+ */
+GVariant *
+g_dbus_connection_call_with_unix_fd_list_finish (GDBusConnection  *connection,
+                                                 GUnixFDList     **out_fd_list,
+                                                 GAsyncResult     *res,
+                                                 GError          **error)
+{
+  return g_dbus_connection_call_finish_internal (connection, out_fd_list, res, error);
+}
 
-  return result;
+/**
+ * g_dbus_connection_call_with_unix_fd_list_sync:
+ * @connection: A #GDBusConnection.
+ * @bus_name: A unique or well-known bus name.
+ * @object_path: Path of remote object.
+ * @interface_name: D-Bus interface to invoke method on.
+ * @method_name: The name of the method to invoke.
+ * @parameters: (allow-none): A #GVariant tuple with parameters for the method
+ *              or %NULL if not passing parameters.
+ * @reply_type: (allow-none): The expected type of the reply, or %NULL.
+ * @flags: Flags from the #GDBusCallFlags enumeration.
+ * @timeout_msec: The timeout in milliseconds, -1 to use the default
+ *                timeout or %G_MAXINT for no timeout.
+ * @fd_list: (allow-none): A #GUnixFDList or %NULL.
+ * @out_fd_list: (out): Return location for a #GUnixFDList or %NULL.
+ * @cancellable: A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Like g_dbus_connection_call_sync() but also takes and returns #GUnixFDList objects.
+ *
+ * This method is only available on UNIX.
+ *
+ * Returns: %NULL if @error is set. Otherwise a #GVariant tuple with
+ * return values. Free with g_variant_unref().
+ *
+ * Since: 2.30
+ */
+GVariant *
+g_dbus_connection_call_with_unix_fd_list_sync (GDBusConnection         *connection,
+                                               const gchar             *bus_name,
+                                               const gchar             *object_path,
+                                               const gchar             *interface_name,
+                                               const gchar             *method_name,
+                                               GVariant                *parameters,
+                                               const GVariantType      *reply_type,
+                                               GDBusCallFlags           flags,
+                                               gint                     timeout_msec,
+                                               GUnixFDList             *fd_list,
+                                               GUnixFDList            **out_fd_list,
+                                               GCancellable            *cancellable,
+                                               GError                 **error)
+{
+  return g_dbus_connection_call_sync_internal (connection, bus_name, object_path, interface_name, method_name, parameters, reply_type, flags, timeout_msec, fd_list, out_fd_list, cancellable, error);
 }
 
+#endif /* G_OS_UNIX */
+
 /* ---------------------------------------------------------------------------------------------------- */
 
 struct ExportedSubtree
diff --git a/gio/gdbusconnection.h b/gio/gdbusconnection.h
index eb3e0f3..ac131dc 100644
--- a/gio/gdbusconnection.h
+++ b/gio/gdbusconnection.h
@@ -181,6 +181,36 @@ GVariant *g_dbus_connection_call_sync                         (GDBusConnection
                                                                gint                timeout_msec,
                                                                GCancellable       *cancellable,
                                                                GError            **error);
+void      g_dbus_connection_call_with_unix_fd_list            (GDBusConnection    *connection,
+                                                               const gchar        *bus_name,
+                                                               const gchar        *object_path,
+                                                               const gchar        *interface_name,
+                                                               const gchar        *method_name,
+                                                               GVariant           *parameters,
+                                                               const GVariantType *reply_type,
+                                                               GDBusCallFlags      flags,
+                                                               gint                timeout_msec,
+                                                               GUnixFDList        *fd_list,
+                                                               GCancellable       *cancellable,
+                                                               GAsyncReadyCallback callback,
+                                                               gpointer            user_data);
+GVariant *g_dbus_connection_call_with_unix_fd_list_finish     (GDBusConnection    *connection,
+                                                               GUnixFDList       **out_fd_list,
+                                                               GAsyncResult       *res,
+                                                               GError            **error);
+GVariant *g_dbus_connection_call_with_unix_fd_list_sync       (GDBusConnection    *connection,
+                                                               const gchar        *bus_name,
+                                                               const gchar        *object_path,
+                                                               const gchar        *interface_name,
+                                                               const gchar        *method_name,
+                                                               GVariant           *parameters,
+                                                               const GVariantType *reply_type,
+                                                               GDBusCallFlags      flags,
+                                                               gint                timeout_msec,
+                                                               GUnixFDList        *fd_list,
+                                                               GUnixFDList       **out_fd_list,
+                                                               GCancellable       *cancellable,
+                                                               GError            **error);
 
 /* ---------------------------------------------------------------------------------------------------- */
 
diff --git a/gio/gdbusmethodinvocation.c b/gio/gdbusmethodinvocation.c
index 6dd7155..fae234f 100644
--- a/gio/gdbusmethodinvocation.c
+++ b/gio/gdbusmethodinvocation.c
@@ -32,6 +32,10 @@
 #include "gdbuserror.h"
 #include "gdbusprivate.h"
 
+#ifdef G_OS_UNIX
+#include "gunixfdlist.h"
+#endif
+
 #include "glibintl.h"
 
 /**
@@ -335,23 +339,10 @@ _g_dbus_method_invocation_new (const gchar           *sender,
 
 /* ---------------------------------------------------------------------------------------------------- */
 
-/**
- * g_dbus_method_invocation_return_value:
- * @invocation: (transfer full): A #GDBusMethodInvocation.
- * @parameters: (allow-none): A #GVariant tuple with out parameters for the method or %NULL if not passing any parameters.
- *
- * Finishes handling a D-Bus method call by returning @parameters.
- * If the @parameters GVariant is floating, it is consumed.
- *
- * It is an error if @parameters is not of the right format.
- *
- * This method will free @invocation, you cannot use it afterwards.
- *
- * Since: 2.26
- */
-void
-g_dbus_method_invocation_return_value (GDBusMethodInvocation *invocation,
-                                       GVariant              *parameters)
+static void
+g_dbus_method_invocation_return_value_internal (GDBusMethodInvocation *invocation,
+                                                GVariant              *parameters,
+                                                GUnixFDList           *fd_list)
 {
   GDBusMessage *reply;
   GError *error;
@@ -401,6 +392,12 @@ g_dbus_method_invocation_return_value (GDBusMethodInvocation *invocation,
 
   reply = g_dbus_message_new_method_reply (invocation->message);
   g_dbus_message_set_body (reply, parameters);
+
+#ifdef G_OS_UNIX
+  if (fd_list != NULL)
+    g_dbus_message_set_unix_fd_list (reply, fd_list);
+#endif
+
   error = NULL;
   if (!g_dbus_connection_send_message (g_dbus_method_invocation_get_connection (invocation), reply, G_DBUS_SEND_MESSAGE_FLAGS_NONE, NULL, &error))
     {
@@ -413,6 +410,51 @@ g_dbus_method_invocation_return_value (GDBusMethodInvocation *invocation,
   g_object_unref (invocation);
 }
 
+/**
+ * g_dbus_method_invocation_return_value:
+ * @invocation: (transfer full): A #GDBusMethodInvocation.
+ * @parameters: (allow-none): A #GVariant tuple with out parameters for the method or %NULL if not passing any parameters.
+ *
+ * Finishes handling a D-Bus method call by returning @parameters.
+ * If the @parameters GVariant is floating, it is consumed.
+ *
+ * It is an error if @parameters is not of the right format.
+ *
+ * This method will free @invocation, you cannot use it afterwards.
+ *
+ * Since: 2.26
+ */
+void
+g_dbus_method_invocation_return_value (GDBusMethodInvocation *invocation,
+                                       GVariant              *parameters)
+{
+  return g_dbus_method_invocation_return_value_internal (invocation, parameters, NULL);
+}
+
+#ifdef G_OS_UNIX
+/**
+ * g_dbus_method_invocation_return_value_with_unix_fd_list:
+ * @invocation: (transfer full): A #GDBusMethodInvocation.
+ * @parameters: (allow-none): A #GVariant tuple with out parameters for the method or %NULL if not passing any parameters.
+ * @fd_list: (allow-none): A #GUnixFDList or %NULL.
+ *
+ * Like g_dbus_method_invocation_return_value() but also takes a #GUnixFDList.
+ *
+ * This method is only available on UNIX.
+ *
+ * This method will free @invocation, you cannot use it afterwards.
+ *
+ * Since: 2.30
+ */
+void
+g_dbus_method_invocation_return_value_with_unix_fd_list (GDBusMethodInvocation *invocation,
+                                                         GVariant              *parameters,
+                                                         GUnixFDList           *fd_list)
+{
+  return g_dbus_method_invocation_return_value_internal (invocation, parameters, fd_list);
+}
+#endif
+
 /* ---------------------------------------------------------------------------------------------------- */
 
 /**
diff --git a/gio/gdbusmethodinvocation.h b/gio/gdbusmethodinvocation.h
index 8b8d050..b1a87ac 100644
--- a/gio/gdbusmethodinvocation.h
+++ b/gio/gdbusmethodinvocation.h
@@ -48,6 +48,9 @@ gpointer               g_dbus_method_invocation_get_user_data        (GDBusMetho
 
 void                   g_dbus_method_invocation_return_value         (GDBusMethodInvocation *invocation,
                                                                       GVariant              *parameters);
+void                   g_dbus_method_invocation_return_value_with_unix_fd_list (GDBusMethodInvocation *invocation,
+                                                                                GVariant              *parameters,
+                                                                                GUnixFDList           *fd_list);
 void                   g_dbus_method_invocation_return_error         (GDBusMethodInvocation *invocation,
                                                                       GQuark                 domain,
                                                                       gint                   code,
diff --git a/gio/gdbusproxy.c b/gio/gdbusproxy.c
index 73359dc..cc77bb9 100644
--- a/gio/gdbusproxy.c
+++ b/gio/gdbusproxy.c
@@ -39,6 +39,10 @@
 #include "gcancellable.h"
 #include "gdbusinterface.h"
 
+#ifdef G_OS_UNIX
+#include "gunixfdlist.h"
+#endif
+
 #include "glibintl.h"
 
 /**
@@ -2325,6 +2329,24 @@ maybe_split_method_name (const gchar  *method_name,
   return was_split;
 }
 
+typedef struct
+{
+  GVariant *value;
+#ifdef G_OS_UNIX
+  GUnixFDList *fd_list;
+#endif
+} ReplyData;
+
+static void
+reply_data_free (ReplyData *data)
+{
+  g_variant_unref (data->value);
+#ifdef G_OS_UNIX
+  if (data->fd_list != NULL)
+    g_object_unref (data->fd_list);
+#endif
+  g_slice_free (ReplyData, data);
+}
 
 static void
 reply_cb (GDBusConnection *connection,
@@ -2334,20 +2356,34 @@ reply_cb (GDBusConnection *connection,
   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (user_data);
   GVariant *value;
   GError *error;
+#ifdef G_OS_UNIX
+  GUnixFDList *fd_list;
+#endif
 
   error = NULL;
+#ifdef G_OS_UNIX
+  value = g_dbus_connection_call_with_unix_fd_list_finish (connection,
+                                                           &fd_list,
+                                                           res,
+                                                           &error);
+#else
   value = g_dbus_connection_call_finish (connection,
                                          res,
                                          &error);
+#endif
   if (error != NULL)
     {
       g_simple_async_result_take_error (simple, error);
     }
   else
     {
-      g_simple_async_result_set_op_res_gpointer (simple,
-                                                 value,
-                                                 (GDestroyNotify) g_variant_unref);
+      ReplyData *data;
+      data = g_slice_new0 (ReplyData);
+      data->value = value;
+#ifdef G_OS_UNIX
+      data->fd_list = fd_list;
+#endif
+      g_simple_async_result_set_op_res_gpointer (simple, data, (GDestroyNotify) reply_data_free);
     }
 
   /* no need to complete in idle since the method GDBusConnection already does */
@@ -2399,66 +2435,18 @@ get_destination_for_call (GDBusProxy *proxy)
   return ret;
 }
 
-/**
- * g_dbus_proxy_call:
- * @proxy: A #GDBusProxy.
- * @method_name: Name of method to invoke.
- * @parameters: (allow-none): A #GVariant tuple with parameters for the signal or %NULL if not passing parameters.
- * @flags: Flags from the #GDBusCallFlags enumeration.
- * @timeout_msec: The timeout in milliseconds (with %G_MAXINT meaning
- *                "infinite") or -1 to use the proxy default timeout.
- * @cancellable: A #GCancellable or %NULL.
- * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL if you don't
- * care about the result of the method invocation.
- * @user_data: The data to pass to @callback.
- *
- * Asynchronously invokes the @method_name method on @proxy.
- *
- * If @method_name contains any dots, then @name is split into interface and
- * method name parts. This allows using @proxy for invoking methods on
- * other interfaces.
- *
- * If the #GDBusConnection associated with @proxy is closed then
- * the operation will fail with %G_IO_ERROR_CLOSED. If
- * @cancellable is canceled, the operation will fail with
- * %G_IO_ERROR_CANCELLED. If @parameters contains a value not
- * compatible with the D-Bus protocol, the operation fails with
- * %G_IO_ERROR_INVALID_ARGUMENT.
- *
- * If the @parameters #GVariant is floating, it is consumed. This allows
- * convenient 'inline' use of g_variant_new(), e.g.:
- * |[
- *  g_dbus_proxy_call (proxy,
- *                     "TwoStrings",
- *                     g_variant_new ("(ss)",
- *                                    "Thing One",
- *                                    "Thing Two"),
- *                     G_DBUS_CALL_FLAGS_NONE,
- *                     -1,
- *                     NULL,
- *                     (GAsyncReadyCallback) two_strings_done,
- *                     &amp;data);
- * ]|
- *
- * This is an asynchronous method. When the operation is finished,
- * @callback will be invoked in the
- * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
- * of the thread you are calling this method from.
- * You can then call g_dbus_proxy_call_finish() to get the result of
- * the operation. See g_dbus_proxy_call_sync() for the synchronous
- * version of this method.
- *
- * Since: 2.26
- */
-void
-g_dbus_proxy_call (GDBusProxy          *proxy,
-                   const gchar         *method_name,
-                   GVariant            *parameters,
-                   GDBusCallFlags       flags,
-                   gint                 timeout_msec,
-                   GCancellable        *cancellable,
-                   GAsyncReadyCallback  callback,
-                   gpointer             user_data)
+/* ---------------------------------------------------------------------------------------------------- */
+
+static void
+g_dbus_proxy_call_internal (GDBusProxy          *proxy,
+                            const gchar         *method_name,
+                            GVariant            *parameters,
+                            GDBusCallFlags       flags,
+                            gint                 timeout_msec,
+                            GUnixFDList         *fd_list,
+                            GCancellable        *cancellable,
+                            GAsyncReadyCallback  callback,
+                            gpointer             user_data)
 {
   GSimpleAsyncResult *simple;
   gboolean was_split;
@@ -2473,6 +2461,7 @@ g_dbus_proxy_call (GDBusProxy          *proxy,
   g_return_if_fail (g_dbus_is_member_name (method_name) || g_dbus_is_interface_name (method_name));
   g_return_if_fail (parameters == NULL || g_variant_is_of_type (parameters, G_VARIANT_TYPE_TUPLE));
   g_return_if_fail (timeout_msec == -1 || timeout_msec >= 0);
+  g_return_if_fail (fd_list == NULL || G_IS_UNIX_FD_LIST (fd_list));
 
   reply_type = NULL;
   split_interface_name = NULL;
@@ -2480,7 +2469,7 @@ g_dbus_proxy_call (GDBusProxy          *proxy,
   simple = g_simple_async_result_new (G_OBJECT (proxy),
                                       callback,
                                       user_data,
-                                      g_dbus_proxy_call);
+                                      g_dbus_proxy_call_internal);
 
   was_split = maybe_split_method_name (method_name, &split_interface_name, &split_method_name);
   target_method_name = was_split ? split_method_name : method_name;
@@ -2509,6 +2498,21 @@ g_dbus_proxy_call (GDBusProxy          *proxy,
         }
     }
 
+#ifdef G_OS_UNIX
+  g_dbus_connection_call_with_unix_fd_list (proxy->priv->connection,
+                                            destination,
+                                            proxy->priv->object_path,
+                                            target_interface_name,
+                                            target_method_name,
+                                            parameters,
+                                            reply_type,
+                                            flags,
+                                            timeout_msec == -1 ? proxy->priv->timeout_msec : timeout_msec,
+                                            fd_list,
+                                            cancellable,
+                                            (GAsyncReadyCallback) reply_cb,
+                                            simple);
+#else
   g_dbus_connection_call (proxy->priv->connection,
                           destination,
                           proxy->priv->object_path,
@@ -2521,6 +2525,7 @@ g_dbus_proxy_call (GDBusProxy          *proxy,
                           cancellable,
                           (GAsyncReadyCallback) reply_cb,
                           simple);
+#endif
 
  out:
   if (reply_type != NULL)
@@ -2529,100 +2534,48 @@ g_dbus_proxy_call (GDBusProxy          *proxy,
   g_free (split_interface_name);
 }
 
-/**
- * g_dbus_proxy_call_finish:
- * @proxy: A #GDBusProxy.
- * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_dbus_proxy_call().
- * @error: Return location for error or %NULL.
- *
- * Finishes an operation started with g_dbus_proxy_call().
- *
- * Returns: %NULL if @error is set. Otherwise a #GVariant tuple with
- * return values. Free with g_variant_unref().
- *
- * Since: 2.26
- */
-GVariant *
-g_dbus_proxy_call_finish (GDBusProxy    *proxy,
-                          GAsyncResult  *res,
-                          GError       **error)
+static GVariant *
+g_dbus_proxy_call_finish_internal (GDBusProxy    *proxy,
+                                   GUnixFDList  **out_fd_list,
+                                   GAsyncResult  *res,
+                                   GError       **error)
 {
   GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (res);
   GVariant *value;
+  ReplyData *data;
 
   g_return_val_if_fail (G_IS_DBUS_PROXY (proxy), NULL);
   g_return_val_if_fail (G_IS_ASYNC_RESULT (res), NULL);
   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
-  g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_dbus_proxy_call);
+  g_warn_if_fail (g_simple_async_result_get_source_tag (simple) == g_dbus_proxy_call_internal);
 
   value = NULL;
 
   if (g_simple_async_result_propagate_error (simple, error))
     goto out;
 
-  value = g_variant_ref (g_simple_async_result_get_op_res_gpointer (simple));
+  data = g_simple_async_result_get_op_res_gpointer (simple);
+  value = g_variant_ref (data->value);
+#ifdef G_OS_UNIX
+  if (out_fd_list != NULL)
+    *out_fd_list = data->fd_list != NULL ? g_object_ref (data->fd_list) : NULL;
+#endif
 
  out:
   return value;
 }
 
-/**
- * g_dbus_proxy_call_sync:
- * @proxy: A #GDBusProxy.
- * @method_name: Name of method to invoke.
- * @parameters: (allow-none): A #GVariant tuple with parameters for the signal
- *              or %NULL if not passing parameters.
- * @flags: Flags from the #GDBusCallFlags enumeration.
- * @timeout_msec: The timeout in milliseconds (with %G_MAXINT meaning
- *                "infinite") or -1 to use the proxy default timeout.
- * @cancellable: A #GCancellable or %NULL.
- * @error: Return location for error or %NULL.
- *
- * Synchronously invokes the @method_name method on @proxy.
- *
- * If @method_name contains any dots, then @name is split into interface and
- * method name parts. This allows using @proxy for invoking methods on
- * other interfaces.
- *
- * If the #GDBusConnection associated with @proxy is disconnected then
- * the operation will fail with %G_IO_ERROR_CLOSED. If
- * @cancellable is canceled, the operation will fail with
- * %G_IO_ERROR_CANCELLED. If @parameters contains a value not
- * compatible with the D-Bus protocol, the operation fails with
- * %G_IO_ERROR_INVALID_ARGUMENT.
- *
- * If the @parameters #GVariant is floating, it is consumed. This allows
- * convenient 'inline' use of g_variant_new(), e.g.:
- * |[
- *  g_dbus_proxy_call_sync (proxy,
- *                          "TwoStrings",
- *                          g_variant_new ("(ss)",
- *                                         "Thing One",
- *                                         "Thing Two"),
- *                          G_DBUS_CALL_FLAGS_NONE,
- *                          -1,
- *                          NULL,
- *                          &amp;error);
- * ]|
- *
- * The calling thread is blocked until a reply is received. See
- * g_dbus_proxy_call() for the asynchronous version of this
- * method.
- *
- * Returns: %NULL if @error is set. Otherwise a #GVariant tuple with
- * return values. Free with g_variant_unref().
- *
- * Since: 2.26
- */
-GVariant *
-g_dbus_proxy_call_sync (GDBusProxy      *proxy,
-                        const gchar     *method_name,
-                        GVariant        *parameters,
-                        GDBusCallFlags   flags,
-                        gint             timeout_msec,
-                        GCancellable    *cancellable,
-                        GError         **error)
+static GVariant *
+g_dbus_proxy_call_sync_internal (GDBusProxy      *proxy,
+                                 const gchar     *method_name,
+                                 GVariant        *parameters,
+                                 GDBusCallFlags   flags,
+                                 gint             timeout_msec,
+                                 GUnixFDList     *fd_list,
+                                 GUnixFDList    **out_fd_list,
+                                 GCancellable    *cancellable,
+                                 GError         **error)
 {
   GVariant *ret;
   gboolean was_split;
@@ -2637,6 +2590,7 @@ g_dbus_proxy_call_sync (GDBusProxy      *proxy,
   g_return_val_if_fail (g_dbus_is_member_name (method_name) || g_dbus_is_interface_name (method_name), NULL);
   g_return_val_if_fail (parameters == NULL || g_variant_is_of_type (parameters, G_VARIANT_TYPE_TUPLE), NULL);
   g_return_val_if_fail (timeout_msec == -1 || timeout_msec >= 0, NULL);
+  g_return_val_if_fail (fd_list == NULL || G_IS_UNIX_FD_LIST (fd_list), NULL);
   g_return_val_if_fail (error == NULL || *error == NULL, NULL);
 
   reply_type = NULL;
@@ -2669,6 +2623,21 @@ g_dbus_proxy_call_sync (GDBusProxy      *proxy,
         }
     }
 
+#ifdef G_OS_UNIX
+  ret = g_dbus_connection_call_with_unix_fd_list_sync (proxy->priv->connection,
+                                                       destination,
+                                                       proxy->priv->object_path,
+                                                       target_interface_name,
+                                                       target_method_name,
+                                                       parameters,
+                                                       reply_type,
+                                                       flags,
+                                                       timeout_msec == -1 ? proxy->priv->timeout_msec : timeout_msec,
+                                                       fd_list,
+                                                       out_fd_list,
+                                                       cancellable,
+                                                       error);
+#else
   ret = g_dbus_connection_call_sync (proxy->priv->connection,
                                      destination,
                                      proxy->priv->object_path,
@@ -2680,6 +2649,7 @@ g_dbus_proxy_call_sync (GDBusProxy      *proxy,
                                      timeout_msec == -1 ? proxy->priv->timeout_msec : timeout_msec,
                                      cancellable,
                                      error);
+#endif
 
  out:
   if (reply_type != NULL)
@@ -2692,6 +2662,253 @@ g_dbus_proxy_call_sync (GDBusProxy      *proxy,
 
 /* ---------------------------------------------------------------------------------------------------- */
 
+/**
+ * g_dbus_proxy_call:
+ * @proxy: A #GDBusProxy.
+ * @method_name: Name of method to invoke.
+ * @parameters: (allow-none): A #GVariant tuple with parameters for the signal or %NULL if not passing parameters.
+ * @flags: Flags from the #GDBusCallFlags enumeration.
+ * @timeout_msec: The timeout in milliseconds (with %G_MAXINT meaning
+ *                "infinite") or -1 to use the proxy default timeout.
+ * @cancellable: A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL if you don't
+ * care about the result of the method invocation.
+ * @user_data: The data to pass to @callback.
+ *
+ * Asynchronously invokes the @method_name method on @proxy.
+ *
+ * If @method_name contains any dots, then @name is split into interface and
+ * method name parts. This allows using @proxy for invoking methods on
+ * other interfaces.
+ *
+ * If the #GDBusConnection associated with @proxy is closed then
+ * the operation will fail with %G_IO_ERROR_CLOSED. If
+ * @cancellable is canceled, the operation will fail with
+ * %G_IO_ERROR_CANCELLED. If @parameters contains a value not
+ * compatible with the D-Bus protocol, the operation fails with
+ * %G_IO_ERROR_INVALID_ARGUMENT.
+ *
+ * If the @parameters #GVariant is floating, it is consumed. This allows
+ * convenient 'inline' use of g_variant_new(), e.g.:
+ * |[
+ *  g_dbus_proxy_call (proxy,
+ *                     "TwoStrings",
+ *                     g_variant_new ("(ss)",
+ *                                    "Thing One",
+ *                                    "Thing Two"),
+ *                     G_DBUS_CALL_FLAGS_NONE,
+ *                     -1,
+ *                     NULL,
+ *                     (GAsyncReadyCallback) two_strings_done,
+ *                     &amp;data);
+ * ]|
+ *
+ * This is an asynchronous method. When the operation is finished,
+ * @callback will be invoked in the
+ * <link linkend="g-main-context-push-thread-default">thread-default main loop</link>
+ * of the thread you are calling this method from.
+ * You can then call g_dbus_proxy_call_finish() to get the result of
+ * the operation. See g_dbus_proxy_call_sync() for the synchronous
+ * version of this method.
+ *
+ * Since: 2.26
+ */
+void
+g_dbus_proxy_call (GDBusProxy          *proxy,
+                   const gchar         *method_name,
+                   GVariant            *parameters,
+                   GDBusCallFlags       flags,
+                   gint                 timeout_msec,
+                   GCancellable        *cancellable,
+                   GAsyncReadyCallback  callback,
+                   gpointer             user_data)
+{
+  return g_dbus_proxy_call_internal (proxy, method_name, parameters, flags, timeout_msec, NULL, cancellable, callback, user_data);
+}
+
+/**
+ * g_dbus_proxy_call_finish:
+ * @proxy: A #GDBusProxy.
+ * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_dbus_proxy_call().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with g_dbus_proxy_call().
+ *
+ * Returns: %NULL if @error is set. Otherwise a #GVariant tuple with
+ * return values. Free with g_variant_unref().
+ *
+ * Since: 2.26
+ */
+GVariant *
+g_dbus_proxy_call_finish (GDBusProxy    *proxy,
+                          GAsyncResult  *res,
+                          GError       **error)
+{
+  return g_dbus_proxy_call_finish_internal (proxy, NULL, res, error);
+}
+
+/**
+ * g_dbus_proxy_call_sync:
+ * @proxy: A #GDBusProxy.
+ * @method_name: Name of method to invoke.
+ * @parameters: (allow-none): A #GVariant tuple with parameters for the signal
+ *              or %NULL if not passing parameters.
+ * @flags: Flags from the #GDBusCallFlags enumeration.
+ * @timeout_msec: The timeout in milliseconds (with %G_MAXINT meaning
+ *                "infinite") or -1 to use the proxy default timeout.
+ * @cancellable: A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously invokes the @method_name method on @proxy.
+ *
+ * If @method_name contains any dots, then @name is split into interface and
+ * method name parts. This allows using @proxy for invoking methods on
+ * other interfaces.
+ *
+ * If the #GDBusConnection associated with @proxy is disconnected then
+ * the operation will fail with %G_IO_ERROR_CLOSED. If
+ * @cancellable is canceled, the operation will fail with
+ * %G_IO_ERROR_CANCELLED. If @parameters contains a value not
+ * compatible with the D-Bus protocol, the operation fails with
+ * %G_IO_ERROR_INVALID_ARGUMENT.
+ *
+ * If the @parameters #GVariant is floating, it is consumed. This allows
+ * convenient 'inline' use of g_variant_new(), e.g.:
+ * |[
+ *  g_dbus_proxy_call_sync (proxy,
+ *                          "TwoStrings",
+ *                          g_variant_new ("(ss)",
+ *                                         "Thing One",
+ *                                         "Thing Two"),
+ *                          G_DBUS_CALL_FLAGS_NONE,
+ *                          -1,
+ *                          NULL,
+ *                          &amp;error);
+ * ]|
+ *
+ * The calling thread is blocked until a reply is received. See
+ * g_dbus_proxy_call() for the asynchronous version of this
+ * method.
+ *
+ * Returns: %NULL if @error is set. Otherwise a #GVariant tuple with
+ * return values. Free with g_variant_unref().
+ *
+ * Since: 2.26
+ */
+GVariant *
+g_dbus_proxy_call_sync (GDBusProxy      *proxy,
+                        const gchar     *method_name,
+                        GVariant        *parameters,
+                        GDBusCallFlags   flags,
+                        gint             timeout_msec,
+                        GCancellable    *cancellable,
+                        GError         **error)
+{
+  return g_dbus_proxy_call_sync_internal (proxy, method_name, parameters, flags, timeout_msec, NULL, NULL, cancellable, error);
+}
+
+/* ---------------------------------------------------------------------------------------------------- */
+
+#ifdef G_OS_UNIX
+
+/**
+ * g_dbus_proxy_call_with_unix_fd_list:
+ * @proxy: A #GDBusProxy.
+ * @method_name: Name of method to invoke.
+ * @parameters: (allow-none): A #GVariant tuple with parameters for the signal or %NULL if not passing parameters.
+ * @flags: Flags from the #GDBusCallFlags enumeration.
+ * @timeout_msec: The timeout in milliseconds (with %G_MAXINT meaning
+ *                "infinite") or -1 to use the proxy default timeout.
+ * @fd_list: (allow-none): A #GUnixFDList or %NULL.
+ * @cancellable: A #GCancellable or %NULL.
+ * @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL if you don't
+ * care about the result of the method invocation.
+ * @user_data: The data to pass to @callback.
+ *
+ * Like g_dbus_proxy_call() but also takes a #GUnixFDList object.
+ *
+ * This method is only available on UNIX.
+ *
+ * Since: 2.30
+ */
+void
+g_dbus_proxy_call_with_unix_fd_list (GDBusProxy          *proxy,
+                                     const gchar         *method_name,
+                                     GVariant            *parameters,
+                                     GDBusCallFlags       flags,
+                                     gint                 timeout_msec,
+                                     GUnixFDList         *fd_list,
+                                     GCancellable        *cancellable,
+                                     GAsyncReadyCallback  callback,
+                                     gpointer             user_data)
+{
+  return g_dbus_proxy_call_internal (proxy, method_name, parameters, flags, timeout_msec, fd_list, cancellable, callback, user_data);
+}
+
+/**
+ * g_dbus_proxy_call_with_unix_fd_list_finish:
+ * @proxy: A #GDBusProxy.
+ * @out_fd_list: (out): Return location for a #GUnixFDList or %NULL.
+ * @res: A #GAsyncResult obtained from the #GAsyncReadyCallback passed to g_dbus_proxy_call_with_unix_fd_list().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with g_dbus_proxy_call_with_unix_fd_list().
+ *
+ * Returns: %NULL if @error is set. Otherwise a #GVariant tuple with
+ * return values. Free with g_variant_unref().
+ *
+ * Since: 2.30
+ */
+GVariant *
+g_dbus_proxy_call_with_unix_fd_list_finish (GDBusProxy    *proxy,
+                                            GUnixFDList  **out_fd_list,
+                                            GAsyncResult  *res,
+                                            GError       **error)
+{
+  return g_dbus_proxy_call_finish_internal (proxy, out_fd_list, res, error);
+}
+
+/**
+ * g_dbus_proxy_call_with_unix_fd_list_sync:
+ * @proxy: A #GDBusProxy.
+ * @method_name: Name of method to invoke.
+ * @parameters: (allow-none): A #GVariant tuple with parameters for the signal
+ *              or %NULL if not passing parameters.
+ * @flags: Flags from the #GDBusCallFlags enumeration.
+ * @timeout_msec: The timeout in milliseconds (with %G_MAXINT meaning
+ *                "infinite") or -1 to use the proxy default timeout.
+ * @fd_list: (allow-none): A #GUnixFDList or %NULL.
+ * @out_fd_list: (out): Return location for a #GUnixFDList or %NULL.
+ * @cancellable: A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Like g_dbus_proxy_call_sync() but also takes and returns #GUnixFDList objects.
+ *
+ * This method is only available on UNIX.
+ *
+ * Returns: %NULL if @error is set. Otherwise a #GVariant tuple with
+ * return values. Free with g_variant_unref().
+ *
+ * Since: 2.30
+ */
+GVariant *
+g_dbus_proxy_call_with_unix_fd_list_sync (GDBusProxy      *proxy,
+                                          const gchar     *method_name,
+                                          GVariant        *parameters,
+                                          GDBusCallFlags   flags,
+                                          gint             timeout_msec,
+                                          GUnixFDList     *fd_list,
+                                          GUnixFDList    **out_fd_list,
+                                          GCancellable    *cancellable,
+                                          GError         **error)
+{
+  return g_dbus_proxy_call_sync_internal (proxy, method_name, parameters, flags, timeout_msec, fd_list, out_fd_list, cancellable, error);
+}
+
+#endif /* G_OS_UNIX */
+
+/* ---------------------------------------------------------------------------------------------------- */
+
 static GDBusInterfaceInfo *
 _g_dbus_proxy_get_info (GDBusInterface *interface)
 {
diff --git a/gio/gdbusproxy.h b/gio/gdbusproxy.h
index d9858c1..1e920d3 100644
--- a/gio/gdbusproxy.h
+++ b/gio/gdbusproxy.h
@@ -162,6 +162,29 @@ GVariant        *g_dbus_proxy_call_sync                 (GDBusProxy          *pr
                                                          GCancellable        *cancellable,
                                                          GError             **error);
 
+void             g_dbus_proxy_call_with_unix_fd_list        (GDBusProxy          *proxy,
+                                                             const gchar         *method_name,
+                                                             GVariant            *parameters,
+                                                             GDBusCallFlags       flags,
+                                                             gint                 timeout_msec,
+                                                             GUnixFDList         *fd_list,
+                                                             GCancellable        *cancellable,
+                                                             GAsyncReadyCallback  callback,
+                                                             gpointer             user_data);
+GVariant        *g_dbus_proxy_call_with_unix_fd_list_finish (GDBusProxy          *proxy,
+                                                             GUnixFDList        **out_fd_list,
+                                                             GAsyncResult        *res,
+                                                             GError             **error);
+GVariant        *g_dbus_proxy_call_with_unix_fd_list_sync   (GDBusProxy          *proxy,
+                                                             const gchar         *method_name,
+                                                             GVariant            *parameters,
+                                                             GDBusCallFlags       flags,
+                                                             gint                 timeout_msec,
+                                                             GUnixFDList         *fd_list,
+                                                             GUnixFDList        **out_fd_list,
+                                                             GCancellable        *cancellable,
+                                                             GError             **error);
+
 G_END_DECLS
 
 #endif /* __G_DBUS_PROXY_H__ */
diff --git a/gio/gio.symbols b/gio/gio.symbols
index 00d5328..e5f96e9 100644
--- a/gio/gio.symbols
+++ b/gio/gio.symbols
@@ -1036,6 +1036,13 @@ g_unix_fd_message_new
 g_unix_fd_message_steal_fds
 g_unix_fd_message_get_fd_list
 g_unix_fd_message_new_with_fd_list
+g_dbus_connection_call_with_unix_fd_list
+g_dbus_connection_call_with_unix_fd_list_finish
+g_dbus_connection_call_with_unix_fd_list_sync
+g_dbus_proxy_call_with_unix_fd_list
+g_dbus_proxy_call_with_unix_fd_list_finish
+g_dbus_proxy_call_with_unix_fd_list_sync
+g_dbus_method_invocation_return_value_with_unix_fd_list
 #endif
 #ifndef G_OS_WIN32
 g_unix_fd_list_append
diff --git a/gio/tests/test-codegen.xml b/gio/tests/test-codegen.xml
index 8b8cac2..b78def0 100644
--- a/gio/tests/test-codegen.xml
+++ b/gio/tests/test-codegen.xml
@@ -467,4 +467,12 @@
   </interface>
   <unknownTag/>
 
+  <interface name="FDPassing">
+    <method name="HelloFD">
+      <annotation name="org.gtk.GDBus.C.UnixFD" value="1"/>
+      <arg name="greeting" direction="in" type="s"/>
+      <arg name="response" direction="out" type="s"/>
+    </method>
+  </interface>
+
 </node>



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]