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>
This commit is contained in:
David Zeuthen 2011-08-23 12:46:32 -04:00
parent a9e2305dce
commit 05448a6bef
4 changed files with 274 additions and 60 deletions

View File

@ -11,6 +11,7 @@ example_animal_call_poke_sync
example_animal_complete_poke example_animal_complete_poke
example_animal_emit_jumped example_animal_emit_jumped
example_animal_get_mood example_animal_get_mood
example_animal_dup_mood
example_animal_set_mood example_animal_set_mood
ExampleAnimalProxy ExampleAnimalProxy
ExampleAnimalProxyClass ExampleAnimalProxyClass
@ -55,6 +56,7 @@ EXAMPLE_IS_ANIMAL_SKELETON_CLASS
ExampleCat ExampleCat
ExampleCatIface ExampleCatIface
example_cat_interface_info example_cat_interface_info
example_cat_override_properties
ExampleCatProxy ExampleCatProxy
ExampleCatProxyClass ExampleCatProxyClass
example_cat_proxy_new example_cat_proxy_new

View File

@ -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 %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 %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 %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('\n')
self.h.write('struct _%s;\n'%(i.camel_name)) self.h.write('struct _%s;\n'%(i.camel_name))
self.h.write('typedef struct _%s %s;\n'%(i.camel_name, 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 = {} function_pointers = {}
# vfuncs for methods
if len(i.methods) > 0: if len(i.methods) > 0:
self.h.write('\n') self.h.write('\n')
for m in i.methods: for m in i.methods:
@ -262,6 +263,7 @@ class CodeGenerator:
value += ');\n\n' value += ');\n\n'
function_pointers[key] = value function_pointers[key] = value
# vfuncs for signals
if len(i.signals) > 0: if len(i.signals) > 0:
self.h.write('\n') self.h.write('\n')
for s in i.signals: for s in i.signals:
@ -273,6 +275,14 @@ class CodeGenerator:
value += ');\n\n' value += ');\n\n'
function_pointers[key] = value 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 # Sort according to @since tag, then name.. this ensures
# that the function pointers don't change order assuming # that the function pointers don't change order assuming
# judicious use of @since # judicious use of @since
@ -293,7 +303,6 @@ class CodeGenerator:
self.h.write('GType %s_get_type (void) G_GNUC_CONST;\n'%(i.name_lower)) self.h.write('GType %s_get_type (void) G_GNUC_CONST;\n'%(i.name_lower))
self.h.write('\n') self.h.write('\n')
self.h.write('GDBusInterfaceInfo *%s_interface_info (void);\n'%(i.name_lower)) 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') self.h.write('\n')
@ -396,6 +405,10 @@ class CodeGenerator:
if p.deprecated: if p.deprecated:
self.h.write('G_GNUC_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)) 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 # setter
if p.deprecated: if p.deprecated:
self.h.write('G_GNUC_DEPRECATED ') self.h.write('G_GNUC_DEPRECATED ')
@ -936,7 +949,6 @@ class CodeGenerator:
'}\n' '}\n'
'\n'%(i.name_lower, i.name_lower)) '\n'%(i.name_lower, i.name_lower))
if len(i.properties) > 0:
self.c.write(self.docbook_gen.expand( self.c.write(self.docbook_gen.expand(
'/**\n' '/**\n'
' * %s_override_properties:\n' ' * %s_override_properties:\n'
@ -992,6 +1004,12 @@ class CodeGenerator:
value = '@%s: '%(s.name_lower) value = '@%s: '%(s.name_lower)
value += 'Handler for the #%s::%s signal.'%(i.camel_name, s.name_hyphen) value += 'Handler for the #%s::%s signal.'%(i.camel_name, s.name_hyphen)
doc_bits[key] = value 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() keys = doc_bits.keys()
if len(keys) > 0: if len(keys) > 0:
keys.sort(cmp=utils.my_version_cmp) 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)) raise RuntimeError('Cannot handle property %s that neither readable nor writable'%(p.name))
self.c.write(self.docbook_gen.expand( self.c.write(self.docbook_gen.expand(
'/**\n' '/**\n'
' * %s_get_%s:\n' ' * %s_get_%s: (skip)\n'
' * @object: A #%s.\n' ' * @object: A #%s.\n'
' *\n' ' *\n'
' * Gets the value of the #%s:%s D-Bus property.\n' ' * Gets the value of the #%s:%s D-Bus property.\n'
' *\n' ' *\n'
' * %s\n' ' * %s\n'
' *\n' ' *\n'
' * Returns: (transfer none): The property value.\n'
%(i.name_lower, p.name_lower, i.camel_name, i.name, p.name, hint), False)) %(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.write_gtkdoc_deprecated_and_since_and_close(p, self.c, 0)
self.c.write('%s\n' self.c.write('%s\n'
'%s_get_%s (%s *object)\n' '%s_get_%s (%s *object)\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' '{\n'
' %svalue;\n'%(p.arg.ctype_in, i.name_lower, p.name_lower, i.camel_name, p.arg.ctype_in_g)) ' %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(' 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') self.c.write(' return value;\n')
self.c.write('}\n') self.c.write('}\n')
self.c.write('\n') self.c.write('\n')
# setter # setter
if p.readable and p.writable: 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.' 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)) raise RuntimeError('Cannot handle property %s that neither readable nor writable'%(p.name))
self.c.write(self.docbook_gen.expand( self.c.write(self.docbook_gen.expand(
'/**\n' '/**\n'
' * %s_set_%s:\n' ' * %s_set_%s: (skip)\n'
' * @object: A #%s.\n' ' * @object: A #%s.\n'
' * @value: The value to set.\n' ' * @value: The value to set.\n'
' *\n' ' *\n'
@ -1515,15 +1554,25 @@ class CodeGenerator:
self.write_gtkdoc_deprecated_and_since_and_close(i, self.c, 0) self.write_gtkdoc_deprecated_and_since_and_close(i, self.c, 0)
self.c.write('\n') self.c.write('\n')
self.c.write('static void\n' self.c.write('struct _%sProxyPrivate\n'
'%s_proxy_iface_init (%sIface *iface)\n'
'{\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)) '\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_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(' G_IMPLEMENT_INTERFACE (%sTYPE_%s, %s_proxy_iface_init));\n\n'%(i.ns_upper, i.name_upper, i.name_lower))
self.c.write('#undef %s_proxy_get_type\n'
# 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)) '\n'%(i.name_lower))
# property accessors # property accessors
@ -1652,12 +1701,13 @@ class CodeGenerator:
# property changed # property changed
self.c.write('static void\n' 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' ' GVariant *changed_properties,\n'
' const gchar *const *invalidated_properties)\n' ' const gchar *const *invalidated_properties)\n'
'{\n'%(i.name_lower)) '{\n'%(i.name_lower))
# Note: info could be NULL if we are talking to a newer version of the interface # 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' ' const gchar *key;\n'
' GVariantIter *iter;\n' ' GVariantIter *iter;\n'
' _ExtendedGDBusPropertyInfo *info;\n' ' _ExtendedGDBusPropertyInfo *info;\n'
@ -1665,6 +1715,7 @@ class CodeGenerator:
' while (g_variant_iter_next (iter, "{&sv}", &key, NULL))\n' ' while (g_variant_iter_next (iter, "{&sv}", &key, NULL))\n'
' {\n' ' {\n'
' info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_%s_interface_info, key);\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' ' if (info != NULL)\n'
' g_object_notify (G_OBJECT (proxy), info->hyphen_name);\n' ' g_object_notify (G_OBJECT (proxy), info->hyphen_name);\n'
' }\n' ' }\n'
@ -1672,40 +1723,114 @@ class CodeGenerator:
' for (n = 0; invalidated_properties[n] != NULL; n++)\n' ' for (n = 0; invalidated_properties[n] != NULL; n++)\n'
' {\n' ' {\n'
' info = (_ExtendedGDBusPropertyInfo *) g_dbus_interface_info_lookup_property ((GDBusInterfaceInfo *) &_%s_interface_info, invalidated_properties[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' ' if (info != NULL)\n'
' g_object_notify (G_OBJECT (proxy), info->hyphen_name);\n' ' g_object_notify (G_OBJECT (proxy), info->hyphen_name);\n'
' }\n' ' }\n'
'}\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 # class boilerplate
self.c.write('static void\n' self.c.write('static void\n'
'%s_proxy_init (%sProxy *proxy)\n' '%s_proxy_init (%sProxy *proxy)\n'
'{\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' ' g_dbus_proxy_set_interface_info (G_DBUS_PROXY (proxy), %s_interface_info ());\n'
'}\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' self.c.write('static void\n'
'%s_proxy_class_init (%sProxyClass *klass)\n' '%s_proxy_class_init (%sProxyClass *klass)\n'
'{\n' '{\n'
' GObjectClass *gobject_class;\n' ' GObjectClass *gobject_class;\n'
' GDBusProxyClass *proxy_class;\n' ' GDBusProxyClass *proxy_class;\n'
'\n' '\n'
' g_type_class_add_private (klass, sizeof (%sProxyPrivate));\n'
'\n'
' gobject_class = G_OBJECT_CLASS (klass);\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->get_property = %s_proxy_get_property;\n'
' gobject_class->set_property = %s_proxy_set_property;\n' ' gobject_class->set_property = %s_proxy_set_property;\n'
'\n' '\n'
' proxy_class = G_DBUS_PROXY_CLASS (klass);\n' ' proxy_class = G_DBUS_PROXY_CLASS (klass);\n'
' proxy_class->g_signal = %s_proxy_g_signal;\n' ' proxy_class->g_signal = %s_proxy_g_signal;\n'
' proxy_class->g_properties_changed = %s_proxy_g_properties_changed;\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: if len(i.properties) > 0:
self.c.write('\n' self.c.write('\n'
' %s_override_properties (gobject_class, 1);\n'%(i.name_lower)) ' %s_override_properties (gobject_class, 1);\n'%(i.name_lower))
self.c.write('}\n' self.c.write('}\n'
'\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 # constructors
self.c.write(self.docbook_gen.expand( self.c.write(self.docbook_gen.expand(
'/**\n' '/**\n'
@ -2199,20 +2324,11 @@ class CodeGenerator:
self.c.write('}\n' self.c.write('}\n'
'\n') '\n')
self.c.write('static void\n' self.c.write('static void %s_skeleton_iface_init (%sIface *iface);\n'
'%s_skeleton_iface_init (%sIface *iface)\n'
'{\n'
%(i.name_lower, i.camel_name)) %(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_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(' G_IMPLEMENT_INTERFACE (%sTYPE_%s, %s_skeleton_iface_init));\n\n'%(i.ns_upper, i.name_upper, i.name_lower))
self.c.write('#undef %s_skeleton_get_type\n'
'\n'%(i.name_lower))
# finalize # finalize
self.c.write('static void\n' self.c.write('static void\n'
@ -2409,6 +2525,25 @@ class CodeGenerator:
n += 1 n += 1
self.c.write('}\n' self.c.write('}\n'
'\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' self.c.write('static void\n'
'%s_skeleton_class_init (%sSkeletonClass *klass)\n' '%s_skeleton_class_init (%sSkeletonClass *klass)\n'
'{\n' '{\n'
@ -2436,6 +2571,18 @@ class CodeGenerator:
self.c.write('}\n' self.c.write('}\n'
'\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 # constructors
self.c.write(self.docbook_gen.expand( self.c.write(self.docbook_gen.expand(
'/**\n' '/**\n'

View File

@ -48,11 +48,14 @@ class Arg:
# default to GVariant # default to GVariant
self.ctype_in_g = 'GVariant *' self.ctype_in_g = 'GVariant *'
self.ctype_in = 'GVariant *' self.ctype_in = 'GVariant *'
self.ctype_in_dup = 'GVariant *'
self.ctype_out = 'GVariant **' self.ctype_out = 'GVariant **'
self.gtype = 'G_TYPE_VARIANT' self.gtype = 'G_TYPE_VARIANT'
self.free_func = 'g_variant_unref' self.free_func = 'g_variant_unref'
self.format_in = '@' + self.signature self.format_in = '@' + self.signature
self.format_out = '@' + 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 not utils.lookup_annotation(self.annotations, 'org.gtk.GDBus.C.ForceGVariant'):
if self.signature == 'b': if self.signature == 'b':
self.ctype_in_g = 'gboolean ' self.ctype_in_g = 'gboolean '
@ -62,6 +65,8 @@ class Arg:
self.free_func = None self.free_func = None
self.format_in = 'b' self.format_in = 'b'
self.format_out = 'b' self.format_out = 'b'
self.gvariant_get = 'g_variant_get_boolean'
self.gvalue_get = 'g_value_get_boolean'
elif self.signature == 'y': elif self.signature == 'y':
self.ctype_in_g = 'guchar ' self.ctype_in_g = 'guchar '
self.ctype_in = 'guchar ' self.ctype_in = 'guchar '
@ -70,6 +75,8 @@ class Arg:
self.free_func = None self.free_func = None
self.format_in = 'y' self.format_in = 'y'
self.format_out = 'y' self.format_out = 'y'
self.gvariant_get = 'g_variant_get_byte'
self.gvalue_get = 'g_value_get_uchar'
elif self.signature == 'n': elif self.signature == 'n':
self.ctype_in_g = 'gint ' self.ctype_in_g = 'gint '
self.ctype_in = 'gint16 ' self.ctype_in = 'gint16 '
@ -78,6 +85,8 @@ class Arg:
self.free_func = None self.free_func = None
self.format_in = 'n' self.format_in = 'n'
self.format_out = 'n' self.format_out = 'n'
self.gvariant_get = 'g_variant_get_int16'
self.gvalue_get = 'g_value_get_int'
elif self.signature == 'q': elif self.signature == 'q':
self.ctype_in_g = 'guint ' self.ctype_in_g = 'guint '
self.ctype_in = 'guint16 ' self.ctype_in = 'guint16 '
@ -86,6 +95,8 @@ class Arg:
self.free_func = None self.free_func = None
self.format_in = 'q' self.format_in = 'q'
self.format_out = 'q' self.format_out = 'q'
self.gvariant_get = 'g_variant_get_uint16'
self.gvalue_get = 'g_value_get_uint'
elif self.signature == 'i': elif self.signature == 'i':
self.ctype_in_g = 'gint ' self.ctype_in_g = 'gint '
self.ctype_in = 'gint ' self.ctype_in = 'gint '
@ -94,6 +105,8 @@ class Arg:
self.free_func = None self.free_func = None
self.format_in = 'i' self.format_in = 'i'
self.format_out = 'i' self.format_out = 'i'
self.gvariant_get = 'g_variant_get_int32'
self.gvalue_get = 'g_value_get_int'
elif self.signature == 'u': elif self.signature == 'u':
self.ctype_in_g = 'guint ' self.ctype_in_g = 'guint '
self.ctype_in = 'guint ' self.ctype_in = 'guint '
@ -102,6 +115,8 @@ class Arg:
self.free_func = None self.free_func = None
self.format_in = 'u' self.format_in = 'u'
self.format_out = 'u' self.format_out = 'u'
self.gvariant_get = 'g_variant_get_uint32'
self.gvalue_get = 'g_value_get_uint'
elif self.signature == 'x': elif self.signature == 'x':
self.ctype_in_g = 'gint64 ' self.ctype_in_g = 'gint64 '
self.ctype_in = 'gint64 ' self.ctype_in = 'gint64 '
@ -110,6 +125,8 @@ class Arg:
self.free_func = None self.free_func = None
self.format_in = 'x' self.format_in = 'x'
self.format_out = 'x' self.format_out = 'x'
self.gvariant_get = 'g_variant_get_int64'
self.gvalue_get = 'g_value_get_int64'
elif self.signature == 't': elif self.signature == 't':
self.ctype_in_g = 'guint64 ' self.ctype_in_g = 'guint64 '
self.ctype_in = 'guint64 ' self.ctype_in = 'guint64 '
@ -118,6 +135,8 @@ class Arg:
self.free_func = None self.free_func = None
self.format_in = 't' self.format_in = 't'
self.format_out = 't' self.format_out = 't'
self.gvariant_get = 'g_variant_get_uint64'
self.gvalue_get = 'g_value_get_uint64'
elif self.signature == 'd': elif self.signature == 'd':
self.ctype_in_g = 'gdouble ' self.ctype_in_g = 'gdouble '
self.ctype_in = 'gdouble ' self.ctype_in = 'gdouble '
@ -126,62 +145,85 @@ class Arg:
self.free_func = None self.free_func = None
self.format_in = 'd' self.format_in = 'd'
self.format_out = 'd' self.format_out = 'd'
self.gvariant_get = 'g_variant_get_double'
self.gvalue_get = 'g_value_get_double'
elif self.signature == 's': elif self.signature == 's':
self.ctype_in_g = 'const gchar *' self.ctype_in_g = 'const gchar *'
self.ctype_in = 'const gchar *' self.ctype_in = 'const gchar *'
self.ctype_in_dup = 'gchar *'
self.ctype_out = 'gchar **' self.ctype_out = 'gchar **'
self.gtype = 'G_TYPE_STRING' self.gtype = 'G_TYPE_STRING'
self.free_func = 'g_free' self.free_func = 'g_free'
self.format_in = 's' self.format_in = 's'
self.format_out = 's' self.format_out = 's'
self.gvariant_get = 'g_variant_get_string'
self.gvalue_get = 'g_value_get_string'
elif self.signature == 'o': elif self.signature == 'o':
self.ctype_in_g = 'const gchar *' self.ctype_in_g = 'const gchar *'
self.ctype_in = 'const gchar *' self.ctype_in = 'const gchar *'
self.ctype_in_dup = 'gchar *'
self.ctype_out = 'gchar **' self.ctype_out = 'gchar **'
self.gtype = 'G_TYPE_STRING' self.gtype = 'G_TYPE_STRING'
self.free_func = 'g_free' self.free_func = 'g_free'
self.format_in = 'o' self.format_in = 'o'
self.format_out = 'o' self.format_out = 'o'
self.gvariant_get = 'g_variant_get_string'
self.gvalue_get = 'g_value_get_string'
elif self.signature == 'g': elif self.signature == 'g':
self.ctype_in_g = 'const gchar *' self.ctype_in_g = 'const gchar *'
self.ctype_in = 'const gchar *' self.ctype_in = 'const gchar *'
self.ctype_in_dup = 'gchar *'
self.ctype_out = 'gchar **' self.ctype_out = 'gchar **'
self.gtype = 'G_TYPE_STRING' self.gtype = 'G_TYPE_STRING'
self.free_func = 'g_free' self.free_func = 'g_free'
self.format_in = 'g' self.format_in = 'g'
self.format_out = 'g' self.format_out = 'g'
self.gvariant_get = 'g_variant_get_string'
self.gvalue_get = 'g_value_get_string'
elif self.signature == 'ay': elif self.signature == 'ay':
self.ctype_in_g = 'const gchar *' self.ctype_in_g = 'const gchar *'
self.ctype_in = 'const gchar *' self.ctype_in = 'const gchar *'
self.ctype_in_dup = 'gchar *'
self.ctype_out = 'gchar **' self.ctype_out = 'gchar **'
self.gtype = 'G_TYPE_STRING' self.gtype = 'G_TYPE_STRING'
self.free_func = 'g_free' self.free_func = 'g_free'
self.format_in = '^ay' self.format_in = '^ay'
self.format_out = '^ay' self.format_out = '^ay'
self.gvariant_get = 'g_variant_get_bytestring'
self.gvalue_get = 'g_value_get_string'
elif self.signature == 'as': elif self.signature == 'as':
self.ctype_in_g = 'const gchar *const *' self.ctype_in_g = 'const gchar *const *'
self.ctype_in = 'const gchar *const *' self.ctype_in = 'const gchar *const *'
self.ctype_in_dup = 'gchar **'
self.ctype_out = 'gchar ***' self.ctype_out = 'gchar ***'
self.gtype = 'G_TYPE_STRV' self.gtype = 'G_TYPE_STRV'
self.free_func = 'g_strfreev' self.free_func = 'g_strfreev'
self.format_in = '^as' self.format_in = '^as'
self.format_out = '^as' self.format_out = '^as'
self.gvariant_get = 'g_variant_get_strv'
self.gvalue_get = 'g_value_get_boxed'
elif self.signature == 'ao': elif self.signature == 'ao':
self.ctype_in_g = 'const gchar *const *' self.ctype_in_g = 'const gchar *const *'
self.ctype_in = 'const gchar *const *' self.ctype_in = 'const gchar *const *'
self.ctype_in_dup = 'gchar **'
self.ctype_out = 'gchar ***' self.ctype_out = 'gchar ***'
self.gtype = 'G_TYPE_STRV' self.gtype = 'G_TYPE_STRV'
self.free_func = 'g_strfreev' self.free_func = 'g_strfreev'
self.format_in = '^ao' self.format_in = '^ao'
self.format_out = '^ao' self.format_out = '^ao'
self.gvariant_get = 'g_variant_get_objv'
self.gvalue_get = 'g_value_get_boxed'
elif self.signature == 'aay': elif self.signature == 'aay':
self.ctype_in_g = 'const gchar *const *' self.ctype_in_g = 'const gchar *const *'
self.ctype_in = 'const gchar *const *' self.ctype_in = 'const gchar *const *'
self.ctype_in_dup = 'gchar **'
self.ctype_out = 'gchar ***' self.ctype_out = 'gchar ***'
self.gtype = 'G_TYPE_STRV' self.gtype = 'G_TYPE_STRV'
self.free_func = 'g_strfreev' self.free_func = 'g_strfreev'
self.format_in = '^aay' self.format_in = '^aay'
self.format_out = '^aay' self.format_out = '^aay'
self.gvariant_get = 'g_variant_get_bytestring_array'
self.gvalue_get = 'g_value_get_boxed'
class Method: class Method:
def __init__(self, name): def __init__(self, name):

View File

@ -825,6 +825,7 @@ check_bar_proxy (FooBar *proxy,
* is to exercise the paths that frees the references. * is to exercise the paths that frees the references.
*/ */
const gchar *array_of_strings[3] = {"one", "two", NULL}; 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_objpaths[3] = {"/one", "/one/two", NULL};
const gchar *array_of_bytestrings[3] = {"one\xff", "two\xff", 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_property_notify (proxy, "finally-normal-name");
g_assert_cmpstr (foo_bar_get_finally_normal_name (proxy), ==, "hey back!"); 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. /* Check that grouping changes in idle works.
* *
* See on_handle_request_multi_property_mods(). The server should * See on_handle_request_multi_property_mods(). The server should