[glib] gdbus-codegen: Rework C property getters



commit 05448a6befddb83eefa1214ca699efed248c32e5
Author: David Zeuthen <davidz redhat com>
Date:   Tue Aug 23 12:46:32 2011 -0400

    gdbus-codegen: Rework C property getters
    
    Rework property getters to use a vfunc so we can take the fast path
    and avoid allocating memory for both the skeleton and the proxy
    cases. This requires some special case because of how GVariant expects
    you to free memory in some cases, see #657100. Add test cases for
    this.
    
    Document the _get_ functions as not being thread-safe and also
    generate _dup_ C getters (which are thread-safe).
    
    Mark all the generated _get_, _dup_ and _set_ as (skip) as non-C
    languages should just use GObject properties and not the (socalled)
    "C binding".
    
    Signed-off-by: David Zeuthen <davidz redhat com>

 .../gdbus-object-manager-example-sections.txt      |    2 +
 gio/gdbus-codegen/codegen.py                       |  267 +++++++++++++++-----
 gio/gdbus-codegen/dbustypes.py                     |   42 +++
 gio/tests/gdbus-test-codegen.c                     |   23 ++
 4 files changed, 274 insertions(+), 60 deletions(-)
---
diff --git a/docs/reference/gio/gdbus-object-manager-example/gdbus-object-manager-example-sections.txt b/docs/reference/gio/gdbus-object-manager-example/gdbus-object-manager-example-sections.txt
index 97207e8..36143fe 100644
--- a/docs/reference/gio/gdbus-object-manager-example/gdbus-object-manager-example-sections.txt
+++ b/docs/reference/gio/gdbus-object-manager-example/gdbus-object-manager-example-sections.txt
@@ -11,6 +11,7 @@ example_animal_call_poke_sync
 example_animal_complete_poke
 example_animal_emit_jumped
 example_animal_get_mood
+example_animal_dup_mood
 example_animal_set_mood
 ExampleAnimalProxy
 ExampleAnimalProxyClass
@@ -55,6 +56,7 @@ EXAMPLE_IS_ANIMAL_SKELETON_CLASS
 ExampleCat
 ExampleCatIface
 example_cat_interface_info
+example_cat_override_properties
 ExampleCatProxy
 ExampleCatProxyClass
 example_cat_proxy_new
diff --git a/gio/gdbus-codegen/codegen.py b/gio/gdbus-codegen/codegen.py
index 871e443..2f1230b 100644
--- a/gio/gdbus-codegen/codegen.py
+++ b/gio/gdbus-codegen/codegen.py
@@ -233,7 +233,7 @@ class CodeGenerator:
             self.h.write('#define %sTYPE_%s (%s_get_type ())\n'%(i.ns_upper, i.name_upper, i.name_lower))
             self.h.write('#define %s%s(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), %sTYPE_%s, %s))\n'%(i.ns_upper, i.name_upper, i.ns_upper, i.name_upper, i.camel_name))
             self.h.write('#define %sIS_%s(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), %sTYPE_%s))\n'%(i.ns_upper, i.name_upper, i.ns_upper, i.name_upper))
-            self.h.write('#define %s%s_GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), %sTYPE_%s, %s))\n'%(i.ns_upper, i.name_upper, i.ns_upper, i.name_upper, i.camel_name))
+            self.h.write('#define %s%s_GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), %sTYPE_%s, %sIface))\n'%(i.ns_upper, i.name_upper, i.ns_upper, i.name_upper, i.camel_name))
             self.h.write('\n')
             self.h.write('struct _%s;\n'%(i.camel_name))
             self.h.write('typedef struct _%s %s;\n'%(i.camel_name, i.camel_name))
@@ -245,6 +245,7 @@ class CodeGenerator:
 
             function_pointers = {}
 
+            # vfuncs for methods
             if len(i.methods) > 0:
                 self.h.write('\n')
                 for m in i.methods:
@@ -262,6 +263,7 @@ class CodeGenerator:
                     value += ');\n\n'
                     function_pointers[key] = value
 
+            # vfuncs for signals
             if len(i.signals) > 0:
                 self.h.write('\n')
                 for s in i.signals:
@@ -273,6 +275,14 @@ class CodeGenerator:
                     value += ');\n\n'
                     function_pointers[key] = value
 
+            # vfuncs for properties
+            if len(i.properties) > 0:
+                self.h.write('\n')
+                for p in i.properties:
+                    key = (p.since, '_prop_get_%s'%p.name_lower)
+                    value = '  %s (*get_%s) (%s *object);\n\n'%(p.arg.ctype_in, p.name_lower, i.camel_name)
+                    function_pointers[key] = value
+
             # Sort according to @since tag, then name.. this ensures
             # that the function pointers don't change order assuming
             # judicious use of @since
@@ -293,8 +303,7 @@ class CodeGenerator:
             self.h.write('GType %s_get_type (void) G_GNUC_CONST;\n'%(i.name_lower))
             self.h.write('\n')
             self.h.write('GDBusInterfaceInfo *%s_interface_info (void);\n'%(i.name_lower))
-            if len(i.properties) > 0:
-                self.h.write('guint %s_override_properties (GObjectClass *klass, guint property_id_begin);\n'%(i.name_lower))
+            self.h.write('guint %s_override_properties (GObjectClass *klass, guint property_id_begin);\n'%(i.name_lower))
             self.h.write('\n')
 
             # Then method call completion functions
@@ -396,6 +405,10 @@ class CodeGenerator:
                     if p.deprecated:
                         self.h.write('G_GNUC_DEPRECATED ')
                     self.h.write('%s%s_get_%s (%s *object);\n'%(p.arg.ctype_in, i.name_lower, p.name_lower, i.camel_name))
+                    if p.arg.free_func != None:
+                        if p.deprecated:
+                            self.h.write('G_GNUC_DEPRECATED ')
+                        self.h.write('%s%s_dup_%s (%s *object);\n'%(p.arg.ctype_in_dup, i.name_lower, p.name_lower, i.camel_name))
                     # setter
                     if p.deprecated:
                         self.h.write('G_GNUC_DEPRECATED ')
@@ -936,27 +949,26 @@ class CodeGenerator:
                          '}\n'
                          '\n'%(i.name_lower, i.name_lower))
 
-            if len(i.properties) > 0:
-                self.c.write(self.docbook_gen.expand(
-                        '/**\n'
-                        ' * %s_override_properties:\n'
-                        ' * @klass: The class structure for a #GObject<!-- -->-derived class.\n'
-                        ' * @property_id_begin: The property id to assign to the first overridden property.\n'
-                        ' *\n'
-                        ' * Overrides all #GObject properties in the #%s interface for a concrete class.\n'
-                        ' * The properties are overridden in the order they are defined.\n'
-                        ' *\n'
-                        ' * Returns: The last property id.\n'
-                        %(i.name_lower, i.camel_name), False))
-                self.write_gtkdoc_deprecated_and_since_and_close(i, self.c, 0)
-                self.c.write('guint\n'
-                             '%s_override_properties (GObjectClass *klass, guint property_id_begin)\n'
-                             '{\n'%(i.name_lower))
-                for p in i.properties:
-                    self.c.write ('  g_object_class_override_property (klass, property_id_begin++, "%s");\n'%(p.name_hyphen))
-                self.c.write('  return property_id_begin - 1;\n'
-                             '}\n'
-                             '\n')
+            self.c.write(self.docbook_gen.expand(
+                    '/**\n'
+                    ' * %s_override_properties:\n'
+                    ' * @klass: The class structure for a #GObject<!-- -->-derived class.\n'
+                    ' * @property_id_begin: The property id to assign to the first overridden property.\n'
+                    ' *\n'
+                    ' * Overrides all #GObject properties in the #%s interface for a concrete class.\n'
+                    ' * The properties are overridden in the order they are defined.\n'
+                    ' *\n'
+                    ' * Returns: The last property id.\n'
+                    %(i.name_lower, i.camel_name), False))
+            self.write_gtkdoc_deprecated_and_since_and_close(i, self.c, 0)
+            self.c.write('guint\n'
+                         '%s_override_properties (GObjectClass *klass, guint property_id_begin)\n'
+                         '{\n'%(i.name_lower))
+            for p in i.properties:
+                self.c.write ('  g_object_class_override_property (klass, property_id_begin++, "%s");\n'%(p.name_hyphen))
+            self.c.write('  return property_id_begin - 1;\n'
+                         '}\n'
+                         '\n')
             self.c.write('\n')
 
     # ----------------------------------------------------------------------------------------------------
@@ -992,6 +1004,12 @@ class CodeGenerator:
                 value  = '@%s: '%(s.name_lower)
                 value += 'Handler for the #%s::%s signal.'%(i.camel_name, s.name_hyphen)
                 doc_bits[key] = value
+        if len(i.properties) > 0:
+            for p in i.properties:
+                key = (p.since, '_prop_get_%s'%p.name_lower)
+                value  = '@get_%s: '%(p.name_lower)
+                value += 'Getter for the #%s:%s property.'%(i.camel_name, p.name_hyphen)
+                doc_bits[key] = value
         keys = doc_bits.keys()
         if len(keys) > 0:
             keys.sort(cmp=utils.my_version_cmp)
@@ -1169,30 +1187,51 @@ class CodeGenerator:
                 raise RuntimeError('Cannot handle property %s that neither readable nor writable'%(p.name))
             self.c.write(self.docbook_gen.expand(
                     '/**\n'
-                    ' * %s_get_%s:\n'
+                    ' * %s_get_%s: (skip)\n'
                     ' * @object: A #%s.\n'
                     ' *\n'
                     ' * Gets the value of the #%s:%s D-Bus property.\n'
                     ' *\n'
                     ' * %s\n'
                     ' *\n'
-                    ' * Returns: (transfer none): The property value.\n'
                     %(i.name_lower, p.name_lower, i.camel_name, i.name, p.name, hint), False))
+            if p.arg.free_func != None:
+                self.c.write(' * <warning>The returned value is only valid until the property changes so on the client-side it is only safe to use this function on the thread where @object was constructed. Use %s_dup_%s() if on another thread.</warning>\n'
+                             ' *\n'
+                             ' * Returns: (transfer none): The property value or %%NULL if the property is not set. Do not free the returned value, it belongs to @object.\n'
+                             %(i.name_lower, p.name_lower))
+            else:
+                self.c.write(' * Returns: The property value.\n')
             self.write_gtkdoc_deprecated_and_since_and_close(p, self.c, 0)
             self.c.write('%s\n'
                          '%s_get_%s (%s *object)\n'
-                         '{\n'
-                         '  %svalue;\n'%(p.arg.ctype_in, i.name_lower, p.name_lower, i.camel_name, p.arg.ctype_in_g))
-            self.c.write('  g_object_get (G_OBJECT (object), "%s", &value, NULL);\n'%(p.name_hyphen))
-            if p.arg.free_func:
-                self.c.write('  if (value != NULL)\n'
-                             '    g_object_set_data_full (G_OBJECT (object), "-x-memoizing-%s", (gpointer) value, (GDestroyNotify) %s);\n'
-                             '  else\n'
-                             '    g_object_set_data_full (G_OBJECT (object), "-x-memoizing-%s", (gpointer) value, NULL);\n'
-                             %(p.name_hyphen, p.arg.free_func, p.name_hyphen))
-            self.c.write('  return value;\n')
+                         '{\n'%(p.arg.ctype_in, i.name_lower, p.name_lower, i.camel_name))
+            self.c.write('  return %s%s_GET_IFACE (object)->get_%s (object);\n'%(i.ns_upper, i.name_upper, p.name_lower))
             self.c.write('}\n')
             self.c.write('\n')
+            if p.arg.free_func != None:
+
+                self.c.write(self.docbook_gen.expand(
+                        '/**\n'
+                        ' * %s_dup_%s: (skip)\n'
+                        ' * @object: A #%s.\n'
+                        ' *\n'
+                        ' * Gets a copy of the #%s:%s D-Bus property.\n'
+                        ' *\n'
+                        ' * %s\n'
+                        ' *\n'
+                        ' * Returns: (transfer full): The property value or %%NULL if the property is not set. The returned value should be freed with %s().\n'
+                        %(i.name_lower, p.name_lower, i.camel_name, i.name, p.name, hint, p.arg.free_func), False))
+                self.write_gtkdoc_deprecated_and_since_and_close(p, self.c, 0)
+                self.c.write('%s\n'
+                             '%s_dup_%s (%s *object)\n'
+                             '{\n'
+                             '  %svalue;\n'%(p.arg.ctype_in_dup, i.name_lower, p.name_lower, i.camel_name, p.arg.ctype_in_dup))
+                self.c.write('  g_object_get (G_OBJECT (object), "%s", &value, NULL);\n'%(p.name_hyphen))
+                self.c.write('  return value;\n')
+                self.c.write('}\n')
+                self.c.write('\n')
+
             # setter
             if p.readable and p.writable:
                 hint = 'Since this D-Bus property is both readable and writable, it is meaningful to use this function on both the client- and service-side.'
@@ -1204,7 +1243,7 @@ class CodeGenerator:
                 raise RuntimeError('Cannot handle property %s that neither readable nor writable'%(p.name))
             self.c.write(self.docbook_gen.expand(
                     '/**\n'
-                    ' * %s_set_%s:\n'
+                    ' * %s_set_%s: (skip)\n'
                     ' * @object: A #%s.\n'
                     ' * @value: The value to set.\n'
                     ' *\n'
@@ -1515,15 +1554,25 @@ class CodeGenerator:
         self.write_gtkdoc_deprecated_and_since_and_close(i, self.c, 0)
         self.c.write('\n')
 
-        self.c.write('static void\n'
-                     '%s_proxy_iface_init (%sIface *iface)\n'
+        self.c.write('struct _%sProxyPrivate\n'
                      '{\n'
-                     '}\n'
+                     '  GData *qdata;\n'
+                     '};\n'
+                     '\n'%i.camel_name)
+
+        self.c.write('static void %s_proxy_iface_init (%sIface *iface);\n'
                      '\n'%(i.name_lower, i.camel_name))
-        self.c.write('#define %s_proxy_get_type %s_proxy_get_type\n'%(i.name_lower, i.name_lower))
         self.c.write('G_DEFINE_TYPE_WITH_CODE (%sProxy, %s_proxy, G_TYPE_DBUS_PROXY,\n'%(i.camel_name, i.name_lower))
-        self.c.write('                         G_IMPLEMENT_INTERFACE (%sTYPE_%s, %s_proxy_iface_init));\n'%(i.ns_upper, i.name_upper, i.name_lower))
-        self.c.write('#undef %s_proxy_get_type\n'
+        self.c.write('                         G_IMPLEMENT_INTERFACE (%sTYPE_%s, %s_proxy_iface_init));\n\n'%(i.ns_upper, i.name_upper, i.name_lower))
+
+        # finalize
+        self.c.write('static void\n'
+                     '%s_proxy_finalize (GObject *object)\n'
+                     '{\n'%(i.name_lower))
+        self.c.write('  %sProxy *proxy = %s%s_PROXY (object);\n'%(i.camel_name, i.ns_upper, i.name_upper))
+        self.c.write('  g_datalist_clear (&proxy->priv->qdata);\n')
+        self.c.write('  G_OBJECT_CLASS (%s_proxy_parent_class)->finalize (object);\n'
+                     '}\n'
                      '\n'%(i.name_lower))
 
         # property accessors
@@ -1652,12 +1701,13 @@ class CodeGenerator:
 
         # property changed
         self.c.write('static void\n'
-                     '%s_proxy_g_properties_changed (GDBusProxy *proxy,\n'
+                     '%s_proxy_g_properties_changed (GDBusProxy *_proxy,\n'
                      '  GVariant *changed_properties,\n'
                      '  const gchar *const *invalidated_properties)\n'
                      '{\n'%(i.name_lower))
         # Note: info could be NULL if we are talking to a newer version of the interface
-        self.c.write('  guint n;\n'
+        self.c.write('  %sProxy *proxy = %s%s_PROXY (_proxy);\n'
+                     '  guint n;\n'
                      '  const gchar *key;\n'
                      '  GVariantIter *iter;\n'
                      '  _ExtendedGDBusPropertyInfo *info;\n'
@@ -1665,6 +1715,7 @@ class CodeGenerator:
                      '  while (g_variant_iter_next (iter, "{&sv}", &key, NULL))\n'
                      '    {\n'
                      '      info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_%s_interface_info, key);\n'
+                     '      g_datalist_remove_data (&proxy->priv->qdata, key);\n'
                      '      if (info != NULL)\n'
                      '        g_object_notify (G_OBJECT (proxy), info->hyphen_name);\n'
                      '    }\n'
@@ -1672,40 +1723,114 @@ class CodeGenerator:
                      '  for (n = 0; invalidated_properties[n] != NULL; n++)\n'
                      '    {\n'
                      '      info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_%s_interface_info, invalidated_properties[n]);\n'
+                     '      g_datalist_remove_data (&proxy->priv->qdata, key);\n'
                      '      if (info != NULL)\n'
                      '        g_object_notify (G_OBJECT (proxy), info->hyphen_name);\n'
                      '    }\n'
                      '}\n'
                      '\n'
-                     %(i.name_lower, i.name_lower))
+                     %(i.camel_name, i.ns_upper, i.name_upper,
+                       i.name_lower, i.name_lower))
+
+        # property vfuncs
+        for p in i.properties:
+            nul_value = '0'
+            if p.arg.free_func != None:
+                nul_value = 'NULL'
+            self.c.write('static %s\n'
+                         '%s_proxy_get_%s (%s *object)\n'
+                         '{\n'
+                         '  %sProxy *proxy = %s%s_PROXY (object);\n'
+                         '  GVariant *variant;\n'
+                         '  %svalue = %s;\n'%(p.arg.ctype_in, i.name_lower, p.name_lower, i.camel_name,
+                                              i.camel_name, i.ns_upper, i.name_upper,
+                                              p.arg.ctype_in, nul_value))
+            # For some property types, we have to free the returned
+            # value (or part of it, e.g. the container) because of how
+            # GVariant works.. see https://bugzilla.gnome.org/show_bug.cgi?id=657100
+            # for details
+            #
+            # NOTE: Since we currently only use the qdata for this, we just use the
+            # the Quark numbers corresponding to the property number (starting
+            # from 1)
+            #
+            free_container = False;
+            if p.arg.gvariant_get == 'g_variant_get_strv' or p.arg.gvariant_get == 'g_variant_get_objpathv' or p.arg.gvariant_get == 'g_variant_get_bytestring_array':
+                free_container = True;
+            # If already using an old value for strv, objpathv, bytestring_array (see below),
+            # then just return that... that way the result from multiple consecutive calls
+            # to the getter are valid as long as they're freed
+            #
+            if free_container:
+                self.c.write('  value = g_datalist_get_data (&proxy->priv->qdata, \"%s\");\n'
+                             '  if (value != NULL)\n'
+                             '    return value;\n'
+                             %(p.name))
+            self.c.write('  variant = g_dbus_proxy_get_cached_property (G_DBUS_PROXY (proxy), \"%s\");\n'%(p.name))
+            if p.arg.gtype == 'G_TYPE_VARIANT':
+                self.c.write('  value = variant;\n')
+                self.c.write('  if (variant != NULL)\n')
+                self.c.write('    g_variant_unref (variant);\n')
+            else:
+                self.c.write('  if (variant != NULL)\n'
+                             '    {\n')
+                extra_len = ''
+                if p.arg.gvariant_get == 'g_variant_get_string' or p.arg.gvariant_get == 'g_variant_get_strv' or p.arg.gvariant_get == 'g_variant_get_objv' or p.arg.gvariant_get == 'g_variant_get_bytestring_array':
+                    extra_len = ', NULL'
+                self.c.write('      value = %s (variant%s);\n'%(p.arg.gvariant_get, extra_len))
+                if free_container:
+                    self.c.write('      g_datalist_set_data_full (&proxy->priv->qdata, \"%s\", (gpointer) value, g_free);\n'
+                                 %(p.name))
+                self.c.write('      g_variant_unref (variant);\n')
+                self.c.write('    }\n')
+            self.c.write('  return value;\n')
+            self.c.write('}\n')
+            self.c.write('\n')
 
         # class boilerplate
         self.c.write('static void\n'
                      '%s_proxy_init (%sProxy *proxy)\n'
                      '{\n'
+                     '  proxy->priv = G_TYPE_INSTANCE_GET_PRIVATE (proxy, %sTYPE_%s_PROXY, %sProxyPrivate);\n'
                      '  g_dbus_proxy_set_interface_info (G_DBUS_PROXY (proxy), %s_interface_info ());\n'
                      '}\n'
-                     '\n'%(i.name_lower, i.camel_name, i.name_lower))
+                     '\n'
+                     %(i.name_lower, i.camel_name,
+                       i.ns_upper, i.name_upper, i.camel_name,
+                       i.name_lower))
         self.c.write('static void\n'
                      '%s_proxy_class_init (%sProxyClass *klass)\n'
                      '{\n'
                      '  GObjectClass *gobject_class;\n'
                      '  GDBusProxyClass *proxy_class;\n'
                      '\n'
+                     '  g_type_class_add_private (klass, sizeof (%sProxyPrivate));\n'
+                     '\n'
                      '  gobject_class = G_OBJECT_CLASS (klass);\n'
+                     '  gobject_class->finalize     = %s_proxy_finalize;\n'
                      '  gobject_class->get_property = %s_proxy_get_property;\n'
                      '  gobject_class->set_property = %s_proxy_set_property;\n'
                      '\n'
                      '  proxy_class = G_DBUS_PROXY_CLASS (klass);\n'
                      '  proxy_class->g_signal = %s_proxy_g_signal;\n'
                      '  proxy_class->g_properties_changed = %s_proxy_g_properties_changed;\n'
-                     '\n'%(i.name_lower, i.camel_name, i.name_lower, i.name_lower, i.name_lower, i.name_lower))
+                     '\n'%(i.name_lower, i.camel_name,
+                           i.camel_name,
+                           i.name_lower, i.name_lower, i.name_lower, i.name_lower, i.name_lower))
         if len(i.properties) > 0:
             self.c.write('\n'
                          '  %s_override_properties (gobject_class, 1);\n'%(i.name_lower))
         self.c.write('}\n'
                      '\n')
 
+        self.c.write('static void\n'
+                     '%s_proxy_iface_init (%sIface *iface)\n'
+                     '{\n'%(i.name_lower, i.camel_name))
+        for p in i.properties:
+            self.c.write('  iface->get_%s = %s_proxy_get_%s;\n'%(p.name_lower, i.name_lower, p.name_lower))
+        self.c.write('}\n'
+                     '\n')
+
         # constructors
         self.c.write(self.docbook_gen.expand(
                 '/**\n'
@@ -2199,20 +2324,11 @@ class CodeGenerator:
             self.c.write('}\n'
                          '\n')
 
-        self.c.write('static void\n'
-                     '%s_skeleton_iface_init (%sIface *iface)\n'
-                     '{\n'
+        self.c.write('static void %s_skeleton_iface_init (%sIface *iface);\n'
                      %(i.name_lower, i.camel_name))
-        for s in i.signals:
-            self.c.write('  iface->%s = _%s_on_signal_%s;\n'
-                         %(s.name_lower, i.name_lower, s.name_lower))
-        self.c.write('}\n'
-                     '\n')
-        self.c.write('#define %s_skeleton_get_type %s_skeleton_get_type\n'%(i.name_lower, i.name_lower))
+
         self.c.write('G_DEFINE_TYPE_WITH_CODE (%sSkeleton, %s_skeleton, G_TYPE_DBUS_INTERFACE_SKELETON,\n'%(i.camel_name, i.name_lower))
-        self.c.write('                         G_IMPLEMENT_INTERFACE (%sTYPE_%s, %s_skeleton_iface_init));\n'%(i.ns_upper, i.name_upper, i.name_lower))
-        self.c.write('#undef %s_skeleton_get_type\n'
-                     '\n'%(i.name_lower))
+        self.c.write('                         G_IMPLEMENT_INTERFACE (%sTYPE_%s, %s_skeleton_iface_init));\n\n'%(i.ns_upper, i.name_upper, i.name_lower))
 
         # finalize
         self.c.write('static void\n'
@@ -2409,6 +2525,25 @@ class CodeGenerator:
                 n += 1
         self.c.write('}\n'
                      '\n')
+
+        # property vfuncs
+        n = 0
+        for p in i.properties:
+            self.c.write('static %s\n'
+                         '%s_skeleton_get_%s (%s *object)\n'
+                         '{\n'
+                         %(p.arg.ctype_in, i.name_lower, p.name_lower, i.camel_name))
+            self.c.write('  %sSkeleton *skeleton = %s%s_SKELETON (object);\n'%(i.camel_name, i.ns_upper, i.name_upper))
+            self.c.write('  %svalue;\n'
+                         '  g_mutex_lock (skeleton->priv->lock);\n'
+                         '  value = %s (&(skeleton->priv->properties->values[%d]));\n'
+                         '  g_mutex_unlock (skeleton->priv->lock);\n'
+                         %(p.arg.ctype_in_g, p.arg.gvalue_get, n))
+            self.c.write('  return value;\n')
+            self.c.write('}\n')
+            self.c.write('\n')
+            n += 1
+
         self.c.write('static void\n'
                      '%s_skeleton_class_init (%sSkeletonClass *klass)\n'
                      '{\n'
@@ -2436,6 +2571,18 @@ class CodeGenerator:
         self.c.write('}\n'
                      '\n')
 
+        self.c.write('static void\n'
+                     '%s_skeleton_iface_init (%sIface *iface)\n'
+                     '{\n'
+                     %(i.name_lower, i.camel_name))
+        for s in i.signals:
+            self.c.write('  iface->%s = _%s_on_signal_%s;\n'
+                         %(s.name_lower, i.name_lower, s.name_lower))
+        for p in i.properties:
+            self.c.write('  iface->get_%s = %s_skeleton_get_%s;\n'%(p.name_lower, i.name_lower, p.name_lower))
+        self.c.write('}\n'
+                     '\n')
+
         # constructors
         self.c.write(self.docbook_gen.expand(
                 '/**\n'
diff --git a/gio/gdbus-codegen/dbustypes.py b/gio/gdbus-codegen/dbustypes.py
index 1289da4..277e346 100644
--- a/gio/gdbus-codegen/dbustypes.py
+++ b/gio/gdbus-codegen/dbustypes.py
@@ -48,11 +48,14 @@ class Arg:
         # default to GVariant
         self.ctype_in_g  = 'GVariant *'
         self.ctype_in  = 'GVariant *'
+        self.ctype_in_dup  = 'GVariant *'
         self.ctype_out = 'GVariant **'
         self.gtype = 'G_TYPE_VARIANT'
         self.free_func = 'g_variant_unref'
         self.format_in = '@' + self.signature
         self.format_out = '@' + self.signature
+        self.gvariant_get = 'XXX'
+        self.gvalue_get = 'g_value_get_variant'
         if not utils.lookup_annotation(self.annotations, 'org.gtk.GDBus.C.ForceGVariant'):
             if self.signature == 'b':
                 self.ctype_in_g  = 'gboolean '
@@ -62,6 +65,8 @@ class Arg:
                 self.free_func = None
                 self.format_in = 'b'
                 self.format_out = 'b'
+                self.gvariant_get = 'g_variant_get_boolean'
+                self.gvalue_get = 'g_value_get_boolean'
             elif self.signature == 'y':
                 self.ctype_in_g  = 'guchar '
                 self.ctype_in  = 'guchar '
@@ -70,6 +75,8 @@ class Arg:
                 self.free_func = None
                 self.format_in = 'y'
                 self.format_out = 'y'
+                self.gvariant_get = 'g_variant_get_byte'
+                self.gvalue_get = 'g_value_get_uchar'
             elif self.signature == 'n':
                 self.ctype_in_g  = 'gint '
                 self.ctype_in  = 'gint16 '
@@ -78,6 +85,8 @@ class Arg:
                 self.free_func = None
                 self.format_in = 'n'
                 self.format_out = 'n'
+                self.gvariant_get = 'g_variant_get_int16'
+                self.gvalue_get = 'g_value_get_int'
             elif self.signature == 'q':
                 self.ctype_in_g  = 'guint '
                 self.ctype_in  = 'guint16 '
@@ -86,6 +95,8 @@ class Arg:
                 self.free_func = None
                 self.format_in = 'q'
                 self.format_out = 'q'
+                self.gvariant_get = 'g_variant_get_uint16'
+                self.gvalue_get = 'g_value_get_uint'
             elif self.signature == 'i':
                 self.ctype_in_g  = 'gint '
                 self.ctype_in  = 'gint '
@@ -94,6 +105,8 @@ class Arg:
                 self.free_func = None
                 self.format_in = 'i'
                 self.format_out = 'i'
+                self.gvariant_get = 'g_variant_get_int32'
+                self.gvalue_get = 'g_value_get_int'
             elif self.signature == 'u':
                 self.ctype_in_g  = 'guint '
                 self.ctype_in  = 'guint '
@@ -102,6 +115,8 @@ class Arg:
                 self.free_func = None
                 self.format_in = 'u'
                 self.format_out = 'u'
+                self.gvariant_get = 'g_variant_get_uint32'
+                self.gvalue_get = 'g_value_get_uint'
             elif self.signature == 'x':
                 self.ctype_in_g  = 'gint64 '
                 self.ctype_in  = 'gint64 '
@@ -110,6 +125,8 @@ class Arg:
                 self.free_func = None
                 self.format_in = 'x'
                 self.format_out = 'x'
+                self.gvariant_get = 'g_variant_get_int64'
+                self.gvalue_get = 'g_value_get_int64'
             elif self.signature == 't':
                 self.ctype_in_g  = 'guint64 '
                 self.ctype_in  = 'guint64 '
@@ -118,6 +135,8 @@ class Arg:
                 self.free_func = None
                 self.format_in = 't'
                 self.format_out = 't'
+                self.gvariant_get = 'g_variant_get_uint64'
+                self.gvalue_get = 'g_value_get_uint64'
             elif self.signature == 'd':
                 self.ctype_in_g  = 'gdouble '
                 self.ctype_in  = 'gdouble '
@@ -126,62 +145,85 @@ class Arg:
                 self.free_func = None
                 self.format_in = 'd'
                 self.format_out = 'd'
+                self.gvariant_get = 'g_variant_get_double'
+                self.gvalue_get = 'g_value_get_double'
             elif self.signature == 's':
                 self.ctype_in_g  = 'const gchar *'
                 self.ctype_in  = 'const gchar *'
+                self.ctype_in_dup  = 'gchar *'
                 self.ctype_out = 'gchar **'
                 self.gtype = 'G_TYPE_STRING'
                 self.free_func = 'g_free'
                 self.format_in = 's'
                 self.format_out = 's'
+                self.gvariant_get = 'g_variant_get_string'
+                self.gvalue_get = 'g_value_get_string'
             elif self.signature == 'o':
                 self.ctype_in_g  = 'const gchar *'
                 self.ctype_in  = 'const gchar *'
+                self.ctype_in_dup  = 'gchar *'
                 self.ctype_out = 'gchar **'
                 self.gtype = 'G_TYPE_STRING'
                 self.free_func = 'g_free'
                 self.format_in = 'o'
                 self.format_out = 'o'
+                self.gvariant_get = 'g_variant_get_string'
+                self.gvalue_get = 'g_value_get_string'
             elif self.signature == 'g':
                 self.ctype_in_g  = 'const gchar *'
                 self.ctype_in  = 'const gchar *'
+                self.ctype_in_dup  = 'gchar *'
                 self.ctype_out = 'gchar **'
                 self.gtype = 'G_TYPE_STRING'
                 self.free_func = 'g_free'
                 self.format_in = 'g'
                 self.format_out = 'g'
+                self.gvariant_get = 'g_variant_get_string'
+                self.gvalue_get = 'g_value_get_string'
             elif self.signature == 'ay':
                 self.ctype_in_g  = 'const gchar *'
                 self.ctype_in  = 'const gchar *'
+                self.ctype_in_dup  = 'gchar *'
                 self.ctype_out = 'gchar **'
                 self.gtype = 'G_TYPE_STRING'
                 self.free_func = 'g_free'
                 self.format_in = '^ay'
                 self.format_out = '^ay'
+                self.gvariant_get = 'g_variant_get_bytestring'
+                self.gvalue_get = 'g_value_get_string'
             elif self.signature == 'as':
                 self.ctype_in_g  = 'const gchar *const *'
                 self.ctype_in  = 'const gchar *const *'
+                self.ctype_in_dup  = 'gchar **'
                 self.ctype_out = 'gchar ***'
                 self.gtype = 'G_TYPE_STRV'
                 self.free_func = 'g_strfreev'
                 self.format_in = '^as'
                 self.format_out = '^as'
+                self.gvariant_get = 'g_variant_get_strv'
+                self.gvalue_get = 'g_value_get_boxed'
             elif self.signature == 'ao':
                 self.ctype_in_g  = 'const gchar *const *'
                 self.ctype_in  = 'const gchar *const *'
+                self.ctype_in_dup  = 'gchar **'
                 self.ctype_out = 'gchar ***'
                 self.gtype = 'G_TYPE_STRV'
                 self.free_func = 'g_strfreev'
                 self.format_in = '^ao'
                 self.format_out = '^ao'
+                self.gvariant_get = 'g_variant_get_objv'
+                self.gvalue_get = 'g_value_get_boxed'
             elif self.signature == 'aay':
                 self.ctype_in_g  = 'const gchar *const *'
                 self.ctype_in  = 'const gchar *const *'
+                self.ctype_in_dup  = 'gchar **'
                 self.ctype_out = 'gchar ***'
                 self.gtype = 'G_TYPE_STRV'
                 self.free_func = 'g_strfreev'
                 self.format_in = '^aay'
                 self.format_out = '^aay'
+                self.gvariant_get = 'g_variant_get_bytestring_array'
+                self.gvalue_get = 'g_value_get_boxed'
 
 class Method:
     def __init__(self, name):
diff --git a/gio/tests/gdbus-test-codegen.c b/gio/tests/gdbus-test-codegen.c
index 30906e0..e25932a 100644
--- a/gio/tests/gdbus-test-codegen.c
+++ b/gio/tests/gdbus-test-codegen.c
@@ -825,6 +825,7 @@ check_bar_proxy (FooBar    *proxy,
    * is to exercise the paths that frees the references.
    */
   const gchar *array_of_strings[3] = {"one", "two", NULL};
+  const gchar *array_of_strings_2[3] = {"one2", "two2", NULL};
   const gchar *array_of_objpaths[3] = {"/one", "/one/two", NULL};
   const gchar *array_of_bytestrings[3] = {"one\xff", "two\xff", NULL};
 
@@ -920,6 +921,28 @@ check_bar_proxy (FooBar    *proxy,
   _g_assert_property_notify (proxy, "finally-normal-name");
   g_assert_cmpstr (foo_bar_get_finally_normal_name (proxy), ==, "hey back!");
 
+  /* Check that multiple calls to a strv getter works... and that
+   * updates on them works as well (See comment for "property vfuncs"
+   * in gio/gdbus-codegen/codegen.py for details)
+   */
+  const gchar *const *read_as;
+  const gchar *const *read_as2;
+  const gchar *const *read_as3;
+  read_as = foo_bar_get_as (proxy);
+  read_as2 = foo_bar_get_as (proxy);
+  g_assert_cmpint (g_strv_length ((gchar **) read_as), ==, 2);
+  g_assert_cmpstr (read_as[0], ==, "one");
+  g_assert_cmpstr (read_as[1], ==, "two");
+  g_assert (read_as == read_as2); /* this is more testing an implementation detail */
+  g_object_set (proxy,
+                "as", array_of_strings_2,
+                NULL);
+  _g_assert_property_notify (proxy, "as");
+  read_as3 = foo_bar_get_as (proxy);
+  g_assert_cmpint (g_strv_length ((gchar **) read_as3), ==, 2);
+  g_assert_cmpstr (read_as3[0], ==, "one2");
+  g_assert_cmpstr (read_as3[1], ==, "two2");
+
   /* Check that grouping changes in idle works.
    *
    * See on_handle_request_multi_property_mods(). The server should



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