2d3b65
From aea538fe703652fd0a39b2ac9185133849cfdcc4 Mon Sep 17 00:00:00 2001
2d3b65
From: Thomas Jost <schnouki@schnouki.net>
2d3b65
Date: Thu, 13 Dec 2018 03:06:02 -0800
2d3b65
Subject: [PATCH] gdbus-codegen: honor "Property.EmitsChangedSignal"
2d3b65
 annotations
2d3b65
2d3b65
Co-Authored-by: Andy Holmes <andrew.g.r.holmes@gmail.com>
2d3b65
---
2d3b65
 gio/gdbus-2.0/codegen/codegen.py   | 18 ++++++++++-----
2d3b65
 gio/gdbus-2.0/codegen/dbustypes.py |  7 ++++++
2d3b65
 gio/tests/gdbus-test-codegen.c     | 36 +++++++++++++++++++++++++-----
2d3b65
 gio/tests/test-codegen.xml         |  6 +++++
2d3b65
 4 files changed, 56 insertions(+), 11 deletions(-)
2d3b65
2d3b65
diff --git a/gio/gdbus-2.0/codegen/codegen.py b/gio/gdbus-2.0/codegen/codegen.py
2d3b65
index f6892af95..442bd3f5d 100644
2d3b65
--- a/gio/gdbus-2.0/codegen/codegen.py
2d3b65
+++ b/gio/gdbus-2.0/codegen/codegen.py
2d3b65
@@ -638,61 +638,62 @@ class CodeGenerator:
2d3b65
                            '#  include <gio/gunixfdlist.h>\n'
2d3b65
                            '#endif\n'
2d3b65
                            '\n')
2d3b65
 
2d3b65
         self.outfile.write('typedef struct\n'
2d3b65
                            '{\n'
2d3b65
                            '  GDBusArgInfo parent_struct;\n'
2d3b65
                            '  gboolean use_gvariant;\n'
2d3b65
                            '} _ExtendedGDBusArgInfo;\n'
2d3b65
                            '\n')
2d3b65
 
2d3b65
         self.outfile.write('typedef struct\n'
2d3b65
                            '{\n'
2d3b65
                            '  GDBusMethodInfo parent_struct;\n'
2d3b65
                            '  const gchar *signal_name;\n'
2d3b65
                            '  gboolean pass_fdlist;\n'
2d3b65
                            '} _ExtendedGDBusMethodInfo;\n'
2d3b65
                            '\n')
2d3b65
 
2d3b65
         self.outfile.write('typedef struct\n'
2d3b65
                            '{\n'
2d3b65
                            '  GDBusSignalInfo parent_struct;\n'
2d3b65
                            '  const gchar *signal_name;\n'
2d3b65
                            '} _ExtendedGDBusSignalInfo;\n'
2d3b65
                            '\n')
2d3b65
 
2d3b65
         self.outfile.write('typedef struct\n'
2d3b65
                            '{\n'
2d3b65
                            '  GDBusPropertyInfo parent_struct;\n'
2d3b65
                            '  const gchar *hyphen_name;\n'
2d3b65
-                           '  gboolean use_gvariant;\n'
2d3b65
+                           '  guint use_gvariant : 1;\n'
2d3b65
+                           '  guint emits_changed_signal : 1;\n'
2d3b65
                            '} _ExtendedGDBusPropertyInfo;\n'
2d3b65
                            '\n')
2d3b65
 
2d3b65
         self.outfile.write('typedef struct\n'
2d3b65
                            '{\n'
2d3b65
                            '  GDBusInterfaceInfo parent_struct;\n'
2d3b65
                            '  const gchar *hyphen_name;\n'
2d3b65
                            '} _ExtendedGDBusInterfaceInfo;\n'
2d3b65
                            '\n')
2d3b65
 
2d3b65
         self.outfile.write('typedef struct\n'
2d3b65
                            '{\n'
2d3b65
                            '  const _ExtendedGDBusPropertyInfo *info;\n'
2d3b65
                            '  guint prop_id;\n'
2d3b65
                            '  GValue orig_value; /* the value before the change */\n'
2d3b65
                            '} ChangedProperty;\n'
2d3b65
                            '\n'
2d3b65
                            'static void\n'
2d3b65
                            '_changed_property_free (ChangedProperty *data)\n'
2d3b65
                            '{\n'
2d3b65
                            '  g_value_unset (&data->orig_value);\n'
2d3b65
                            '  g_free (data);\n'
2d3b65
                            '}\n'
2d3b65
                            '\n')
2d3b65
 
2d3b65
         self.outfile.write('static gboolean\n'
2d3b65
                            '_g_strv_equal0 (gchar **a, gchar **b)\n'
2d3b65
                            '{\n'
2d3b65
                            '  gboolean ret = FALSE;\n'
2d3b65
                            '  guint n;\n'
2d3b65
@@ -933,63 +934,67 @@ class CodeGenerator:
2d3b65
                                    '\n')
2d3b65
 
2d3b65
             # ---
2d3b65
 
2d3b65
             if len(i.properties) > 0:
2d3b65
                 for p in i.properties:
2d3b65
                     if p.readable and p.writable:
2d3b65
                         access = 'G_DBUS_PROPERTY_INFO_FLAGS_READABLE | G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE'
2d3b65
                     elif p.readable:
2d3b65
                         access = 'G_DBUS_PROPERTY_INFO_FLAGS_READABLE'
2d3b65
                     elif p.writable:
2d3b65
                         access = 'G_DBUS_PROPERTY_INFO_FLAGS_WRITABLE'
2d3b65
                     else:
2d3b65
                         access = 'G_DBUS_PROPERTY_INFO_FLAGS_NONE'
2d3b65
                     num_anno = self.generate_annotations('_%s_property_%s_annotation_info'%(i.name_lower, p.name_lower), p.annotations)
2d3b65
                     self.outfile.write('static const _ExtendedGDBusPropertyInfo _%s_property_info_%s =\n'
2d3b65
                                        '{\n'
2d3b65
                                        '  {\n'
2d3b65
                                        '    -1,\n'
2d3b65
                                        '    (gchar *) "%s",\n'
2d3b65
                                        '    (gchar *) "%s",\n'
2d3b65
                                        '    %s,\n'%(i.name_lower, p.name_lower, p.name, p.arg.signature, access))
2d3b65
                     if num_anno == 0:
2d3b65
                         self.outfile.write('    NULL\n')
2d3b65
                     else:
2d3b65
                         self.outfile.write('    (GDBusAnnotationInfo **) &_%s_property_%s_annotation_info_pointers\n'%(i.name_lower, p.name_lower))
2d3b65
                     self.outfile.write('  },\n'
2d3b65
                                        '  "%s",\n'
2d3b65
                                        %(p.name_hyphen))
2d3b65
                     if not utils.lookup_annotation(p.annotations, 'org.gtk.GDBus.C.ForceGVariant'):
2d3b65
-                        self.outfile.write('  FALSE\n')
2d3b65
+                        self.outfile.write('  FALSE,\n')
2d3b65
                     else:
2d3b65
+                        self.outfile.write('  TRUE,\n')
2d3b65
+                    if p.emits_changed_signal:
2d3b65
                         self.outfile.write('  TRUE\n')
2d3b65
+                    else:
2d3b65
+                        self.outfile.write('  FALSE\n')
2d3b65
                     self.outfile.write('};\n'
2d3b65
                                        '\n')
2d3b65
 
2d3b65
                 self.outfile.write('static const GDBusPropertyInfo * const _%s_property_info_pointers[] =\n'
2d3b65
                                    '{\n'%(i.name_lower))
2d3b65
                 for p in i.properties:
2d3b65
                     self.outfile.write('  &_%s_property_info_%s.parent_struct,\n'%(i.name_lower, p.name_lower))
2d3b65
                 self.outfile.write('  NULL\n'
2d3b65
                                    '};\n'
2d3b65
                                    '\n')
2d3b65
 
2d3b65
             num_anno = self.generate_annotations('_%s_annotation_info'%(i.name_lower), i.annotations)
2d3b65
             self.outfile.write('static const _ExtendedGDBusInterfaceInfo _%s_interface_info =\n'
2d3b65
                                '{\n'
2d3b65
                                '  {\n'
2d3b65
                                '    -1,\n'
2d3b65
                                '    (gchar *) "%s",\n'%(i.name_lower, i.name))
2d3b65
             if len(i.methods) == 0:
2d3b65
                 self.outfile.write('    NULL,\n')
2d3b65
             else:
2d3b65
                 self.outfile.write('    (GDBusMethodInfo **) &_%s_method_info_pointers,\n'%(i.name_lower))
2d3b65
             if len(i.signals) == 0:
2d3b65
                 self.outfile.write('    NULL,\n')
2d3b65
             else:
2d3b65
                 self.outfile.write('    (GDBusSignalInfo **) &_%s_signal_info_pointers,\n'%(i.name_lower))
2d3b65
             if len(i.properties) == 0:
2d3b65
                 self.outfile.write('    NULL,\n')
2d3b65
             else:
2d3b65
                 self.outfile.write('    (GDBusPropertyInfo **) &_%s_property_info_pointers,\n'%(i.name_lower))
2d3b65
             if num_anno == 0:
2d3b65
@@ -2568,68 +2573,71 @@ class CodeGenerator:
2d3b65
             # this allows use of g_object_freeze_notify()/g_object_thaw_notify() ...
2d3b65
             # This is useful when updating several properties from another thread than
2d3b65
             # where the idle will be emitted from
2d3b65
             self.outfile.write('static void\n'
2d3b65
                                '%s_skeleton_notify (GObject      *object,\n'
2d3b65
                                '  GParamSpec *pspec G_GNUC_UNUSED)\n'
2d3b65
                                '{\n'
2d3b65
                                '  %sSkeleton *skeleton = %s%s_SKELETON (object);\n'
2d3b65
                                '  g_mutex_lock (&skeleton->priv->lock);\n'
2d3b65
                                '  if (skeleton->priv->changed_properties != NULL &&\n'
2d3b65
                                '      skeleton->priv->changed_properties_idle_source == NULL)\n'
2d3b65
                                '    {\n'
2d3b65
                                '      skeleton->priv->changed_properties_idle_source = g_idle_source_new ();\n'
2d3b65
                                '      g_source_set_priority (skeleton->priv->changed_properties_idle_source, G_PRIORITY_DEFAULT);\n'
2d3b65
                                '      g_source_set_callback (skeleton->priv->changed_properties_idle_source, _%s_emit_changed, g_object_ref (skeleton), (GDestroyNotify) g_object_unref);\n'
2d3b65
                                '      g_source_set_name (skeleton->priv->changed_properties_idle_source, "[generated] _%s_emit_changed");\n'
2d3b65
                                '      g_source_attach (skeleton->priv->changed_properties_idle_source, skeleton->priv->context);\n'
2d3b65
                                '      g_source_unref (skeleton->priv->changed_properties_idle_source);\n'
2d3b65
                                '    }\n'
2d3b65
                                '  g_mutex_unlock (&skeleton->priv->lock);\n'
2d3b65
                                '}\n'
2d3b65
                                '\n'
2d3b65
                                %(i.name_lower, i.camel_name, i.ns_upper, i.name_upper, i.name_lower, i.name_lower))
2d3b65
 
2d3b65
             self.outfile.write('static void\n'
2d3b65
                                '%s_skeleton_set_property (GObject      *object,\n'
2d3b65
                                '  guint         prop_id,\n'
2d3b65
                                '  const GValue *value,\n'
2d3b65
                                '  GParamSpec   *pspec)\n'
2d3b65
                                '{\n'%(i.name_lower))
2d3b65
-            self.outfile.write('  %sSkeleton *skeleton = %s%s_SKELETON (object);\n'
2d3b65
+            self.outfile.write('  const _ExtendedGDBusPropertyInfo *info;\n'
2d3b65
+                               '  %sSkeleton *skeleton = %s%s_SKELETON (object);\n'
2d3b65
                                '  g_assert (prop_id != 0 && prop_id - 1 < %d);\n'
2d3b65
+                               '  info = (const _ExtendedGDBusPropertyInfo *) _%s_property_info_pointers[prop_id - 1];\n'
2d3b65
                                '  g_mutex_lock (&skeleton->priv->lock);\n'
2d3b65
                                '  g_object_freeze_notify (object);\n'
2d3b65
                                '  if (!_g_value_equal (value, &skeleton->priv->properties[prop_id - 1]))\n'
2d3b65
                                '    {\n'
2d3b65
-                               '      if (g_dbus_interface_skeleton_get_connection (G_DBUS_INTERFACE_SKELETON (skeleton)) != NULL)\n'
2d3b65
-                               '        _%s_schedule_emit_changed (skeleton, (const _ExtendedGDBusPropertyInfo *) _%s_property_info_pointers[prop_id - 1], prop_id, &skeleton->priv->properties[prop_id - 1]);\n'
2d3b65
+                               '      if (g_dbus_interface_skeleton_get_connection (G_DBUS_INTERFACE_SKELETON (skeleton)) != NULL &&\n'
2d3b65
+                               '          info->emits_changed_signal)\n'
2d3b65
+                               '        _%s_schedule_emit_changed (skeleton, info, prop_id, &skeleton->priv->properties[prop_id - 1]);\n'
2d3b65
                                '      g_value_copy (value, &skeleton->priv->properties[prop_id - 1]);\n'
2d3b65
                                '      g_object_notify_by_pspec (object, pspec);\n'
2d3b65
                                '    }\n'
2d3b65
                                '  g_mutex_unlock (&skeleton->priv->lock);\n'
2d3b65
                                '  g_object_thaw_notify (object);\n'
2d3b65
                                %(i.camel_name, i.ns_upper, i.name_upper, len(i.properties), i.name_lower, i.name_lower))
2d3b65
             self.outfile.write('}\n'
2d3b65
                                '\n')
2d3b65
 
2d3b65
         self.outfile.write('static void\n'
2d3b65
                            '%s_skeleton_init (%sSkeleton *skeleton)\n'
2d3b65
                            '{\n'
2d3b65
                            '#if GLIB_VERSION_MAX_ALLOWED >= GLIB_VERSION_2_38\n'
2d3b65
                            '  skeleton->priv = %s_skeleton_get_instance_private (skeleton);\n'
2d3b65
                            '#else\n'
2d3b65
                            '  skeleton->priv = G_TYPE_INSTANCE_GET_PRIVATE (skeleton, %sTYPE_%s_SKELETON, %sSkeletonPrivate);\n'
2d3b65
                            '#endif\n\n'
2d3b65
                            %(i.name_lower, i.camel_name,
2d3b65
                              i.name_lower,
2d3b65
                              i.ns_upper, i.name_upper, i.camel_name))
2d3b65
         self.outfile.write('  g_mutex_init (&skeleton->priv->lock);\n')
2d3b65
         self.outfile.write('  skeleton->priv->context = g_main_context_ref_thread_default ();\n')
2d3b65
         if len(i.properties) > 0:
2d3b65
             self.outfile.write('  skeleton->priv->properties = g_new0 (GValue, %d);\n'%(len(i.properties)))
2d3b65
             n = 0
2d3b65
             for p in i.properties:
2d3b65
                 self.outfile.write('  g_value_init (&skeleton->priv->properties[%d], %s);\n'%(n, p.arg.gtype))
2d3b65
                 n += 1
2d3b65
         self.outfile.write('}\n'
2d3b65
                            '\n')
2d3b65
diff --git a/gio/gdbus-2.0/codegen/dbustypes.py b/gio/gdbus-2.0/codegen/dbustypes.py
2d3b65
index bfc69f596..359880ff7 100644
2d3b65
--- a/gio/gdbus-2.0/codegen/dbustypes.py
2d3b65
+++ b/gio/gdbus-2.0/codegen/dbustypes.py
2d3b65
@@ -300,89 +300,96 @@ class Signal:
2d3b65
         arg_count = 0
2d3b65
         for a in self.args:
2d3b65
             a.post_process(interface_prefix, cns, cns_upper, cns_lower, arg_count)
2d3b65
             arg_count += 1
2d3b65
 
2d3b65
         if utils.lookup_annotation(self.annotations, 'org.freedesktop.DBus.Deprecated') == 'true':
2d3b65
             self.deprecated = True
2d3b65
 
2d3b65
 class Property:
2d3b65
     def __init__(self, name, signature, access):
2d3b65
         self.name = name
2d3b65
         self.signature = signature
2d3b65
         self.access = access
2d3b65
         self.annotations = []
2d3b65
         self.arg = Arg('value', self.signature)
2d3b65
         self.arg.annotations = self.annotations
2d3b65
         self.readable = False
2d3b65
         self.writable = False
2d3b65
         if self.access == 'readwrite':
2d3b65
             self.readable = True
2d3b65
             self.writable = True
2d3b65
         elif self.access == 'read':
2d3b65
             self.readable = True
2d3b65
         elif self.access == 'write':
2d3b65
             self.writable = True
2d3b65
         else:
2d3b65
             print_error('Invalid access type "{}"'.format(self.access))
2d3b65
         self.doc_string = ''
2d3b65
         self.since = ''
2d3b65
         self.deprecated = False
2d3b65
+        self.emits_changed_signal = True
2d3b65
 
2d3b65
     def post_process(self, interface_prefix, cns, cns_upper, cns_lower, containing_iface):
2d3b65
         if len(self.doc_string) == 0:
2d3b65
             self.doc_string = utils.lookup_docs(self.annotations)
2d3b65
         if len(self.since) == 0:
2d3b65
             self.since = utils.lookup_since(self.annotations)
2d3b65
             if len(self.since) == 0:
2d3b65
                 self.since = containing_iface.since
2d3b65
 
2d3b65
         name = self.name
2d3b65
         overridden_name = utils.lookup_annotation(self.annotations, 'org.gtk.GDBus.C.Name')
2d3b65
         if utils.is_ugly_case(overridden_name):
2d3b65
             self.name_lower = overridden_name.lower()
2d3b65
         else:
2d3b65
             if overridden_name:
2d3b65
                 name = overridden_name
2d3b65
             self.name_lower = utils.camel_case_to_uscore(name).lower().replace('-', '_')
2d3b65
         self.name_hyphen = self.name_lower.replace('_', '-')
2d3b65
         # don't clash with the GType getter, e.g.: GType foo_bar_get_type (void); G_GNUC_CONST
2d3b65
         if self.name_lower == 'type':
2d3b65
             self.name_lower = 'type_'
2d3b65
 
2d3b65
         # recalculate arg
2d3b65
         self.arg.annotations = self.annotations
2d3b65
         self.arg.post_process(interface_prefix, cns, cns_upper, cns_lower, 0)
2d3b65
 
2d3b65
         if utils.lookup_annotation(self.annotations, 'org.freedesktop.DBus.Deprecated') == 'true':
2d3b65
             self.deprecated = True
2d3b65
 
2d3b65
+        # FIXME: for now we only support 'false' and 'const' on the signal itself, see #674913 and
2d3b65
+        # http://dbus.freedesktop.org/doc/dbus-specification.html#introspection-format
2d3b65
+        # for details
2d3b65
+        if utils.lookup_annotation(self.annotations, 'org.freedesktop.DBus.Property.EmitsChangedSignal') in ('false', 'const'):
2d3b65
+            self.emits_changed_signal = False
2d3b65
+
2d3b65
 class Interface:
2d3b65
     def __init__(self, name):
2d3b65
         self.name = name
2d3b65
         self.methods = []
2d3b65
         self.signals = []
2d3b65
         self.properties = []
2d3b65
         self.annotations = []
2d3b65
         self.doc_string = ''
2d3b65
         self.doc_string_brief = ''
2d3b65
         self.since = ''
2d3b65
         self.deprecated = False
2d3b65
 
2d3b65
     def post_process(self, interface_prefix, c_namespace):
2d3b65
         if len(self.doc_string) == 0:
2d3b65
             self.doc_string = utils.lookup_docs(self.annotations)
2d3b65
         if len(self.doc_string_brief) == 0:
2d3b65
             self.doc_string_brief = utils.lookup_brief_docs(self.annotations)
2d3b65
         if len(self.since) == 0:
2d3b65
             self.since = utils.lookup_since(self.annotations)
2d3b65
 
2d3b65
         if len(c_namespace) > 0:
2d3b65
             if utils.is_ugly_case(c_namespace):
2d3b65
                 cns = c_namespace.replace('_', '')
2d3b65
                 cns_upper = c_namespace.upper() + '_'
2d3b65
                 cns_lower = c_namespace.lower() + '_'
2d3b65
             else:
2d3b65
                 cns = c_namespace
2d3b65
                 cns_upper = utils.camel_case_to_uscore(c_namespace).upper() + '_'
2d3b65
                 cns_lower = utils.camel_case_to_uscore(c_namespace).lower() + '_'
2d3b65
         else:
2d3b65
diff --git a/gio/tests/gdbus-test-codegen.c b/gio/tests/gdbus-test-codegen.c
2d3b65
index 1c4e83c4c..c906d05ae 100644
2d3b65
--- a/gio/tests/gdbus-test-codegen.c
2d3b65
+++ b/gio/tests/gdbus-test-codegen.c
2d3b65
@@ -1740,103 +1740,127 @@ on_object_proxy_added (GDBusObjectManagerClient  *manager,
2d3b65
                        gpointer            user_data)
2d3b65
 {
2d3b65
   OMData *om_data = user_data;
2d3b65
   om_data->num_object_proxy_added_signals += 1;
2d3b65
   g_signal_connect (object_proxy,
2d3b65
                     "interface-added",
2d3b65
                     G_CALLBACK (on_interface_added),
2d3b65
                     om_data);
2d3b65
   g_signal_connect (object_proxy,
2d3b65
                     "interface-removed",
2d3b65
                     G_CALLBACK (on_interface_removed),
2d3b65
                     om_data);
2d3b65
 }
2d3b65
 
2d3b65
 static void
2d3b65
 on_object_proxy_removed (GDBusObjectManagerClient  *manager,
2d3b65
                          GDBusObjectProxy   *object_proxy,
2d3b65
                          gpointer            user_data)
2d3b65
 {
2d3b65
   OMData *om_data = user_data;
2d3b65
   om_data->num_object_proxy_removed_signals += 1;
2d3b65
   g_assert_cmpint (g_signal_handlers_disconnect_by_func (object_proxy,
2d3b65
                                                          G_CALLBACK (on_interface_added),
2d3b65
                                                          om_data), ==, 1);
2d3b65
   g_assert_cmpint (g_signal_handlers_disconnect_by_func (object_proxy,
2d3b65
                                                          G_CALLBACK (on_interface_removed),
2d3b65
                                                          om_data), ==, 1);
2d3b65
 }
2d3b65
 
2d3b65
 static void
2d3b65
-property_d_changed (GObject    *object,
2d3b65
-		    GParamSpec *pspec,
2d3b65
-		    gpointer    user_data)
2d3b65
+property_changed (GObject    *object,
2d3b65
+		  GParamSpec *pspec,
2d3b65
+		  gpointer    user_data)
2d3b65
 {
2d3b65
   gboolean *changed = user_data;
2d3b65
 
2d3b65
   *changed = TRUE;
2d3b65
 }
2d3b65
 
2d3b65
 static void
2d3b65
 om_check_property_and_signal_emission (GMainLoop  *loop,
2d3b65
                                        FooiGenBar *skeleton,
2d3b65
                                        FooiGenBar *proxy)
2d3b65
 {
2d3b65
   gboolean d_changed = FALSE;
2d3b65
+  gboolean quiet_changed = FALSE;
2d3b65
+  gboolean quiet_too_changed = FALSE;
2d3b65
   guint handler;
2d3b65
 
2d3b65
   /* First PropertiesChanged */
2d3b65
   g_assert_cmpint (foo_igen_bar_get_i (skeleton), ==, 0);
2d3b65
   g_assert_cmpint (foo_igen_bar_get_i (proxy), ==, 0);
2d3b65
   foo_igen_bar_set_i (skeleton, 1);
2d3b65
   _g_assert_property_notify (proxy, "i");
2d3b65
   g_assert_cmpint (foo_igen_bar_get_i (skeleton), ==, 1);
2d3b65
   g_assert_cmpint (foo_igen_bar_get_i (proxy), ==, 1);
2d3b65
 
2d3b65
   /* Double-check the gdouble case */
2d3b65
   g_assert_cmpfloat (foo_igen_bar_get_d (skeleton), ==, 0.0);
2d3b65
   g_assert_cmpfloat (foo_igen_bar_get_d (proxy), ==, 0.0);
2d3b65
   foo_igen_bar_set_d (skeleton, 1.0);
2d3b65
   _g_assert_property_notify (proxy, "d");
2d3b65
 
2d3b65
   /* Verify that re-setting it to the same value doesn't cause a
2d3b65
    * notify on the proxy, by taking advantage of the fact that
2d3b65
    * notifications are serialized.
2d3b65
    */
2d3b65
   handler = g_signal_connect (proxy, "notify::d",
2d3b65
-			      G_CALLBACK (property_d_changed), &d_changed);
2d3b65
+			      G_CALLBACK (property_changed), &d_changed);
2d3b65
   foo_igen_bar_set_d (skeleton, 1.0);
2d3b65
   foo_igen_bar_set_i (skeleton, 2);
2d3b65
   _g_assert_property_notify (proxy, "i");
2d3b65
   g_assert (d_changed == FALSE);
2d3b65
   g_signal_handler_disconnect (proxy, handler);
2d3b65
 
2d3b65
+  /* Verify that re-setting a property with the "EmitsChangedSignal"
2d3b65
+   * set to false doesn't emit a signal. */
2d3b65
+  handler = g_signal_connect (proxy, "notify::quiet",
2d3b65
+			      G_CALLBACK (property_changed), &quiet_changed);
2d3b65
+  foo_igen_bar_set_quiet (skeleton, "hush!");
2d3b65
+  foo_igen_bar_set_i (skeleton, 3);
2d3b65
+  _g_assert_property_notify (proxy, "i");
2d3b65
+  g_assert (quiet_changed == FALSE);
2d3b65
+  g_assert_cmpstr (foo_igen_bar_get_quiet (skeleton), ==, "hush!");
2d3b65
+  g_signal_handler_disconnect (proxy, handler);
2d3b65
+
2d3b65
+  /* Also verify that re-setting a property with the "EmitsChangedSignal"
2d3b65
+   * set to 'const' doesn't emit a signal. */
2d3b65
+  handler = g_signal_connect (proxy, "notify::quiet-too",
2d3b65
+			      G_CALLBACK (property_changed), &quiet_changed);
2d3b65
+  foo_igen_bar_set_quiet_too (skeleton, "hush too!");
2d3b65
+  foo_igen_bar_set_i (skeleton, 4);
2d3b65
+  _g_assert_property_notify (proxy, "i");
2d3b65
+  g_assert (quiet_too_changed == FALSE);
2d3b65
+  g_assert_cmpstr (foo_igen_bar_get_quiet_too (skeleton), ==, "hush too!");
2d3b65
+  g_signal_handler_disconnect (proxy, handler);
2d3b65
+
2d3b65
   /* Then just a regular signal */
2d3b65
   foo_igen_bar_emit_another_signal (skeleton, "word");
2d3b65
   _g_assert_signal_received (proxy, "another-signal");
2d3b65
 }
2d3b65
 
2d3b65
 static void
2d3b65
 check_object_manager (void)
2d3b65
 {
2d3b65
   FooiGenObjectSkeleton *o = NULL;
2d3b65
   FooiGenObjectSkeleton *o2 = NULL;
2d3b65
   FooiGenObjectSkeleton *o3 = NULL;
2d3b65
   GDBusInterfaceSkeleton *i;
2d3b65
   GDBusConnection *c;
2d3b65
   GDBusObjectManagerServer *manager = NULL;
2d3b65
   GDBusNodeInfo *info;
2d3b65
   GError *error;
2d3b65
   GMainLoop *loop;
2d3b65
   OMData *om_data = NULL;
2d3b65
   guint om_signal_id = -1;
2d3b65
   GDBusObjectManager *pm = NULL;
2d3b65
   GList *object_proxies;
2d3b65
   GList *proxies;
2d3b65
   GDBusObject *op;
2d3b65
   GDBusProxy *p;
2d3b65
   FooiGenBar *bar_skeleton;
2d3b65
   GDBusInterface *iface;
2d3b65
   gchar *path, *name, *name_owner;
2d3b65
   GDBusConnection *c2;
2d3b65
   GDBusObjectManagerClientFlags flags;
2d3b65
 
2d3b65
@@ -2124,73 +2148,73 @@ check_object_manager (void)
2d3b65
                     "({objectpath '/managed/first': {'com.acme.Coyote': {'Mood': <''>}}},)");
2d3b65
 
2d3b65
   /* -------------------------------------------------- */
2d3b65
 
2d3b65
   /* create a new object with two interfaces */
2d3b65
   o2 = foo_igen_object_skeleton_new ("/managed/second");
2d3b65
   i = G_DBUS_INTERFACE_SKELETON (foo_igen_bar_skeleton_new ());
2d3b65
   bar_skeleton = FOO_IGEN_BAR (i); /* save for later test */
2d3b65
   foo_igen_object_skeleton_set_bar (o2, FOO_IGEN_BAR (i));
2d3b65
   g_clear_object (&i);
2d3b65
   i = G_DBUS_INTERFACE_SKELETON (foo_igen_bat_skeleton_new ());
2d3b65
   foo_igen_object_skeleton_set_bat (o2, FOO_IGEN_BAT (i));
2d3b65
   g_clear_object (&i);
2d3b65
   /* ... add it */
2d3b65
   g_dbus_object_manager_server_export (manager, G_DBUS_OBJECT_SKELETON (o2));
2d3b65
   /* ... check we get the InterfacesAdded with _two_ interfaces */
2d3b65
   om_data->state = 101;
2d3b65
   g_main_loop_run (om_data->loop);
2d3b65
   g_assert_cmpint (om_data->state, ==, 102);
2d3b65
   g_assert_cmpint (om_data->num_object_proxy_added_signals, ==, 5);
2d3b65
   g_assert_cmpint (om_data->num_object_proxy_removed_signals, ==, 3);
2d3b65
   g_assert_cmpint (om_data->num_interface_added_signals, ==, 1);
2d3b65
   g_assert_cmpint (om_data->num_interface_removed_signals, ==, 1);
2d3b65
 
2d3b65
   /* -------------------------------------------------- */
2d3b65
 
2d3b65
   /* Now that we have a couple of objects with interfaces, check
2d3b65
    * that ObjectManager.GetManagedObjects() works
2d3b65
    */
2d3b65
   om_check_get_all (c, loop,
2d3b65
-                    "({objectpath '/managed/first': {'com.acme.Coyote': {'Mood': <''>}}, '/managed/second': {'org.project.Bar': {'y': <byte 0x00>, 'b': <false>, 'n': <int16 0>, 'q': <uint16 0>, 'i': <0>, 'u': <uint32 0>, 'x': <int64 0>, 't': <uint64 0>, 'd': <0.0>, 's': <''>, 'o': <objectpath '/'>, 'g': <signature ''>, 'ay': <b''>, 'as': <@as []>, 'aay': <@aay []>, 'ao': <@ao []>, 'ag': <@ag []>, 'FinallyNormalName': <''>, 'ReadonlyProperty': <''>, 'unset_i': <0>, 'unset_d': <0.0>, 'unset_s': <''>, 'unset_o': <objectpath '/'>, 'unset_g': <signature ''>, 'unset_ay': <b''>, 'unset_as': <@as []>, 'unset_ao': <@ao []>, 'unset_ag': <@ag []>, 'unset_struct': <(0, 0.0, '', objectpath '/', signature '', @ay [], @as [], @ao [], @ag [])>}, 'org.project.Bat': {'force_i': <0>, 'force_s': <''>, 'force_ay': <@ay []>, 'force_struct': <(0,)>}}},)");
2d3b65
+                    "({objectpath '/managed/first': {'com.acme.Coyote': {'Mood': <''>}}, '/managed/second': {'org.project.Bar': {'y': <byte 0x00>, 'b': <false>, 'n': <int16 0>, 'q': <uint16 0>, 'i': <0>, 'u': <uint32 0>, 'x': <int64 0>, 't': <uint64 0>, 'd': <0.0>, 's': <''>, 'o': <objectpath '/'>, 'g': <signature ''>, 'ay': <b''>, 'as': <@as []>, 'aay': <@aay []>, 'ao': <@ao []>, 'ag': <@ag []>, 'FinallyNormalName': <''>, 'ReadonlyProperty': <''>, 'quiet': <''>, 'quiet_too': <''>, 'unset_i': <0>, 'unset_d': <0.0>, 'unset_s': <''>, 'unset_o': <objectpath '/'>, 'unset_g': <signature ''>, 'unset_ay': <b''>, 'unset_as': <@as []>, 'unset_ao': <@ao []>, 'unset_ag': <@ag []>, 'unset_struct': <(0, 0.0, '', objectpath '/', signature '', @ay [], @as [], @ao [], @ag [])>}, 'org.project.Bat': {'force_i': <0>, 'force_s': <''>, 'force_ay': <@ay []>, 'force_struct': <(0,)>}}},)");
2d3b65
 
2d3b65
   /* Set connection to NULL, causing everything to be unexported.. verify this.. and
2d3b65
    * then set the connection back.. and then check things still work
2d3b65
    */
2d3b65
   g_dbus_object_manager_server_set_connection (manager, NULL);
2d3b65
   info = introspect (c, g_dbus_connection_get_unique_name (c), "/managed", loop);
2d3b65
   g_assert_cmpint (count_interfaces (info), ==, 0); /* nothing */
2d3b65
   g_dbus_node_info_unref (info);
2d3b65
 
2d3b65
   g_dbus_object_manager_server_set_connection (manager, c);
2d3b65
   om_check_get_all (c, loop,
2d3b65
-                    "({objectpath '/managed/first': {'com.acme.Coyote': {'Mood': <''>}}, '/managed/second': {'org.project.Bar': {'y': <byte 0x00>, 'b': <false>, 'n': <int16 0>, 'q': <uint16 0>, 'i': <0>, 'u': <uint32 0>, 'x': <int64 0>, 't': <uint64 0>, 'd': <0.0>, 's': <''>, 'o': <objectpath '/'>, 'g': <signature ''>, 'ay': <b''>, 'as': <@as []>, 'aay': <@aay []>, 'ao': <@ao []>, 'ag': <@ag []>, 'FinallyNormalName': <''>, 'ReadonlyProperty': <''>, 'unset_i': <0>, 'unset_d': <0.0>, 'unset_s': <''>, 'unset_o': <objectpath '/'>, 'unset_g': <signature ''>, 'unset_ay': <b''>, 'unset_as': <@as []>, 'unset_ao': <@ao []>, 'unset_ag': <@ag []>, 'unset_struct': <(0, 0.0, '', objectpath '/', signature '', @ay [], @as [], @ao [], @ag [])>}, 'org.project.Bat': {'force_i': <0>, 'force_s': <''>, 'force_ay': <@ay []>, 'force_struct': <(0,)>}}},)");
2d3b65
+                    "({objectpath '/managed/first': {'com.acme.Coyote': {'Mood': <''>}}, '/managed/second': {'org.project.Bar': {'y': <byte 0x00>, 'b': <false>, 'n': <int16 0>, 'q': <uint16 0>, 'i': <0>, 'u': <uint32 0>, 'x': <int64 0>, 't': <uint64 0>, 'd': <0.0>, 's': <''>, 'o': <objectpath '/'>, 'g': <signature ''>, 'ay': <b''>, 'as': <@as []>, 'aay': <@aay []>, 'ao': <@ao []>, 'ag': <@ag []>, 'FinallyNormalName': <''>, 'ReadonlyProperty': <''>, 'quiet': <''>, 'quiet_too': <''>, 'unset_i': <0>, 'unset_d': <0.0>, 'unset_s': <''>, 'unset_o': <objectpath '/'>, 'unset_g': <signature ''>, 'unset_ay': <b''>, 'unset_as': <@as []>, 'unset_ao': <@ao []>, 'unset_ag': <@ag []>, 'unset_struct': <(0, 0.0, '', objectpath '/', signature '', @ay [], @as [], @ao [], @ag [])>}, 'org.project.Bat': {'force_i': <0>, 'force_s': <''>, 'force_ay': <@ay []>, 'force_struct': <(0,)>}}},)");
2d3b65
 
2d3b65
   /* Also check that the ObjectManagerClient returns these objects - and
2d3b65
    * that they are of the right GType cf. what was requested via
2d3b65
    * the generated ::get-proxy-type signal handler
2d3b65
    */
2d3b65
   object_proxies = g_dbus_object_manager_get_objects (pm);
2d3b65
   g_assert (g_list_length (object_proxies) == 2);
2d3b65
   g_list_free_full (object_proxies, g_object_unref);
2d3b65
   op = g_dbus_object_manager_get_object (pm, "/managed/first");
2d3b65
   g_assert (op != NULL);
2d3b65
   g_assert (FOO_IGEN_IS_OBJECT_PROXY (op));
2d3b65
   g_assert_cmpstr (g_dbus_object_get_object_path (op), ==, "/managed/first");
2d3b65
   proxies = g_dbus_object_get_interfaces (op);
2d3b65
   g_assert (g_list_length (proxies) == 1);
2d3b65
   g_list_free_full (proxies, g_object_unref);
2d3b65
   p = G_DBUS_PROXY (foo_igen_object_get_com_acme_coyote (FOO_IGEN_OBJECT (op)));
2d3b65
   g_assert (p != NULL);
2d3b65
   g_assert_cmpint (G_TYPE_FROM_INSTANCE (p), ==, FOO_IGEN_TYPE_COM_ACME_COYOTE_PROXY);
2d3b65
   g_assert (g_type_is_a (G_TYPE_FROM_INSTANCE (p), FOO_IGEN_TYPE_COM_ACME_COYOTE));
2d3b65
   g_clear_object (&p);
2d3b65
   p = (GDBusProxy *) g_dbus_object_get_interface (op, "org.project.NonExisting");
2d3b65
   g_assert (p == NULL);
2d3b65
   g_clear_object (&op);
2d3b65
 
2d3b65
   /* -- */
2d3b65
   op = g_dbus_object_manager_get_object (pm, "/managed/second");
2d3b65
   g_assert (op != NULL);
2d3b65
   g_assert (FOO_IGEN_IS_OBJECT_PROXY (op));
2d3b65
   g_assert_cmpstr (g_dbus_object_get_object_path (op), ==, "/managed/second");
2d3b65
   proxies = g_dbus_object_get_interfaces (op);
2d3b65
diff --git a/gio/tests/test-codegen.xml b/gio/tests/test-codegen.xml
2d3b65
index 885a21f77..39d8769c7 100644
2d3b65
--- a/gio/tests/test-codegen.xml
2d3b65
+++ b/gio/tests/test-codegen.xml
2d3b65
@@ -79,60 +79,66 @@
2d3b65
       <arg type="aay" name="array_of_bytestrings" />
2d3b65
       <arg type="a{s(ii)}" name="dict_s_to_pairs" />
2d3b65
     </signal>
2d3b65
 
2d3b65
     <signal name="AnotherSignal">
2d3b65
       <arg type="s" name="word" />
2d3b65
     </signal>
2d3b65
 
2d3b65
     <property name="y" type="y" access="readwrite">
2d3b65
       <annotation name="org.gtk.GDBus.DocString" value="<para>Property docs, yah...</para><para>Second paragraph.</para>"/>
2d3b65
     </property>
2d3b65
     <property name="b" type="b" access="readwrite"/>
2d3b65
     <property name="n" type="n" access="readwrite"/>
2d3b65
     <property name="q" type="q" access="readwrite"/>
2d3b65
     <property name="i" type="i" access="readwrite"/>
2d3b65
     <property name="u" type="u" access="readwrite"/>
2d3b65
     <property name="x" type="x" access="readwrite"/>
2d3b65
     <property name="t" type="t" access="readwrite"/>
2d3b65
     <property name="d" type="d" access="readwrite"/>
2d3b65
     <property name="s" type="s" access="readwrite"/>
2d3b65
     <property name="o" type="o" access="readwrite"/>
2d3b65
     <property name="g" type="g" access="readwrite"/>
2d3b65
     <property name="ay" type="ay" access="readwrite"/>
2d3b65
     <property name="as" type="as" access="readwrite"/>
2d3b65
     <property name="aay" type="aay" access="readwrite"/>
2d3b65
     <property name="ao" type="ao" access="readwrite"/>
2d3b65
     <property name="ag" type="ag" access="readwrite"/>
2d3b65
     <property name="FinallyNormalName" type="s" access="readwrite"/>
2d3b65
     <property name="ReadonlyProperty" type="s" access="read"/>
2d3b65
     <property name="WriteonlyProperty" type="s" access="write"/>
2d3b65
+    <property name="quiet" type="s" access="readwrite">
2d3b65
+      <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="false"/>
2d3b65
+    </property>
2d3b65
+    <property name="quiet_too" type="s" access="readwrite">
2d3b65
+      <annotation name="org.freedesktop.DBus.Property.EmitsChangedSignal" value="const"/>
2d3b65
+    </property>
2d3b65
 
2d3b65
     
2d3b65
     <property name="unset_i" type="i" access="readwrite"/>
2d3b65
     <property name="unset_d" type="d" access="readwrite"/>
2d3b65
     <property name="unset_s" type="s" access="readwrite"/>
2d3b65
     <property name="unset_o" type="o" access="readwrite"/>
2d3b65
     <property name="unset_g" type="g" access="readwrite"/>
2d3b65
     <property name="unset_ay" type="ay" access="readwrite"/>
2d3b65
     <property name="unset_as" type="as" access="readwrite"/>
2d3b65
     <property name="unset_ao" type="ao" access="readwrite"/>
2d3b65
     <property name="unset_ag" type="ag" access="readwrite"/>
2d3b65
     <property name="unset_struct" type="(idsogayasaoag)" access="readwrite"/>
2d3b65
   </interface> 
2d3b65
 
2d3b65
   
2d3b65
   <interface name="org.project.Bar.Frobnicator">
2d3b65
     <method name="RandomMethod"/>
2d3b65
   </interface>
2d3b65
 
2d3b65
   
2d3b65
   <interface name="org.project.Baz">
2d3b65
   </interface>
2d3b65
 
2d3b65
   
2d3b65
   <interface name="com.acme.Coyote">
2d3b65
     <method name="Run"/>
2d3b65
     <method name="Sleep"/>
2d3b65
     <method name="Attack"/>
2d3b65
     <signal name="Surprised"/>
2d3b65
     <property name="Mood" type="s" access="read"/>
2d3b65
-- 
2d3b65
2.21.0
2d3b65