Blame SOURCES/0002-gdbus-codegen-honor-Property.EmitsChangedSignal-anno.patch

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