[gtk/a11y/atcontext-realize] a11y: Move ATContext to an explicit realization model




commit 4766ac42d6ac5ff0756b956d49e6fa60dc60a365
Author: Emmanuele Bassi <ebassi gnome org>
Date:   Mon Oct 19 17:44:50 2020 +0100

    a11y: Move ATContext to an explicit realization model
    
    We are doing too much work during the construction phase of the
    AT-SPI backend for GtkATContext. Instead of having the AtSpiContext
    register itself at construction time, let's add explicit realize
    and unrealize operations, and connect the ATContext realization to the
    rooting operation of a GtkWidget.

 gtk/a11y/gtkatspicontext.c     | 85 ++++++++++++++++++++++++------------------
 gtk/a11y/gtkatspiroot.c        | 24 +++++++++++-
 gtk/a11y/gtkatspirootprivate.h |  3 ++
 gtk/gtkatcontext.c             | 45 ++++++++++++++++++++++
 gtk/gtkatcontextprivate.h      |  8 ++++
 gtk/gtkwidget.c                |  8 ++++
 6 files changed, 135 insertions(+), 38 deletions(-)
---
diff --git a/gtk/a11y/gtkatspicontext.c b/gtk/a11y/gtkatspicontext.c
index b030d1cfd7..38021e58fc 100644
--- a/gtk/a11y/gtkatspicontext.c
+++ b/gtk/a11y/gtkatspicontext.c
@@ -1154,6 +1154,10 @@ gtk_at_spi_context_register_object (GtkAtSpiContext *self)
     }
 
   self->interfaces = g_variant_ref_sink (g_variant_builder_end (&interfaces));
+
+  GTK_NOTE (A11Y, g_message ("Registered %d interfaces on object path '%s'",
+                             self->n_registered_objects,
+                             self->context_path));
 }
 
 static void
@@ -1169,19 +1173,6 @@ gtk_at_spi_context_unregister_object (GtkAtSpiContext *self)
 }
 /* }}} */
 /* {{{ GObject boilerplate */
-static void
-gtk_at_spi_context_dispose (GObject *gobject)
-{
-  GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (gobject);
-  GtkAccessible *accessible = gtk_at_context_get_accessible (GTK_AT_CONTEXT (self));
-
-  gtk_at_spi_context_unregister_object (self);
-  gtk_atspi_disconnect_text_signals (accessible);
-  gtk_atspi_disconnect_selection_signals (accessible);
-
-  G_OBJECT_CLASS (gtk_at_spi_context_parent_class)->dispose (gobject);
-}
-
 static void
 gtk_at_spi_context_finalize (GObject *gobject)
 {
@@ -1236,29 +1227,10 @@ static void
 gtk_at_spi_context_constructed (GObject *gobject)
 {
   GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (gobject);
-  GdkDisplay *display;
+  GdkDisplay *display = gtk_at_context_get_display (GTK_AT_CONTEXT (self));
 
   g_assert (self->bus_address);
 
-  /* Every GTK application has a single root AT-SPI object, which
-   * handles all the global state, including the cache of accessible
-   * objects. We use the GdkDisplay to store it, so it's guaranteed
-   * to be unique per-display connection
-   */
-  display = gtk_at_context_get_display (GTK_AT_CONTEXT (self));
-  self->root =
-    g_object_get_data (G_OBJECT (display), "-gtk-atspi-root");
-
-  if (self->root == NULL)
-    {
-      self->root = gtk_at_spi_root_new (self->bus_address);
-      g_object_set_data_full (G_OBJECT (display), "-gtk-atspi-root",
-                              self->root,
-                              g_object_unref);
-    }
-
-  self->connection = gtk_at_spi_root_get_connection (self->root);
-
   /* We use the application's object path to build the path of each
    * accessible object exposed on the accessibility bus; the path is
    * also used to access the object cache
@@ -1298,7 +1270,36 @@ gtk_at_spi_context_constructed (GObject *gobject)
   g_free (base_path);
   g_free (uuid);
 
-  GtkAccessible *accessible = gtk_at_context_get_accessible (GTK_AT_CONTEXT (self));
+  /* Every GTK application has a single root AT-SPI object, which
+   * handles all the global state, including the cache of accessible
+   * objects. We use the GdkDisplay to store it, so it's guaranteed
+   * to be unique per-display connection
+   */
+  self->root =
+    g_object_get_data (G_OBJECT (display), "-gtk-atspi-root");
+
+  if (self->root == NULL)
+    {
+      self->root = gtk_at_spi_root_new (self->bus_address);
+      gtk_at_spi_root_register (self->root);
+      g_object_set_data_full (G_OBJECT (display), "-gtk-atspi-root",
+                              self->root,
+                              g_object_unref);
+    }
+
+  G_OBJECT_CLASS (gtk_at_spi_context_parent_class)->constructed (gobject);
+}
+
+static void
+gtk_at_spi_context_realize (GtkATContext *context)
+{
+  GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (context);
+
+  GTK_NOTE (A11Y, g_message ("Realizing ATSPI context at '%s'", self->context_path));
+
+  self->connection = gtk_at_spi_root_get_connection (self->root);
+
+  GtkAccessible *accessible = gtk_at_context_get_accessible (context);
   gtk_atspi_connect_text_signals (accessible,
                                   (GtkAtspiTextChangedCallback *)emit_text_changed,
                                   (GtkAtspiTextSelectionCallback *)emit_text_selection_changed,
@@ -1307,8 +1308,19 @@ gtk_at_spi_context_constructed (GObject *gobject)
                                        (GtkAtspiSelectionCallback *)emit_selection_changed,
                                        self);
   gtk_at_spi_context_register_object (self);
+}
 
-  G_OBJECT_CLASS (gtk_at_spi_context_parent_class)->constructed (gobject);
+static void
+gtk_at_spi_context_unrealize (GtkATContext *context)
+{
+  GtkAtSpiContext *self = GTK_AT_SPI_CONTEXT (context);
+  GtkAccessible *accessible = gtk_at_context_get_accessible (context);
+
+  GTK_NOTE (A11Y, g_message ("Unrealizing ATSPI context at '%s'", self->context_path));
+
+  gtk_atspi_disconnect_text_signals (accessible);
+  gtk_atspi_disconnect_selection_signals (accessible);
+  gtk_at_spi_context_unregister_object (self);
 }
 
 static void
@@ -1321,8 +1333,9 @@ gtk_at_spi_context_class_init (GtkAtSpiContextClass *klass)
   gobject_class->set_property = gtk_at_spi_context_set_property;
   gobject_class->get_property = gtk_at_spi_context_get_property;
   gobject_class->finalize = gtk_at_spi_context_finalize;
-  gobject_class->dispose = gtk_at_spi_context_dispose;
 
+  context_class->realize = gtk_at_spi_context_realize;
+  context_class->unrealize = gtk_at_spi_context_unrealize;
   context_class->state_change = gtk_at_spi_context_state_change;
   context_class->platform_change = gtk_at_spi_context_platform_change;
   context_class->bounds_change = gtk_at_spi_context_bounds_change;
diff --git a/gtk/a11y/gtkatspiroot.c b/gtk/a11y/gtkatspiroot.c
index 75290623ea..239b17aa2d 100644
--- a/gtk/a11y/gtkatspiroot.c
+++ b/gtk/a11y/gtkatspiroot.c
@@ -61,6 +61,7 @@ struct _GtkAtSpiRoot
   char *desktop_path;
 
   gint32 application_id;
+  guint register_id;
 
   GtkAtSpiCache *cache;
 
@@ -83,6 +84,8 @@ gtk_at_spi_root_finalize (GObject *gobject)
 {
   GtkAtSpiRoot *self = GTK_AT_SPI_ROOT (gobject);
 
+  g_clear_handle_id (&self->register_id, g_source_remove);
+
   g_free (self->bus_address);
   g_free (self->desktop_name);
   g_free (self->desktop_path);
@@ -441,9 +444,11 @@ on_registration_reply (GObject      *gobject,
   self->toplevels = gtk_window_get_toplevels ();
 }
 
-static void
-gtk_at_spi_root_register (GtkAtSpiRoot *self)
+static gboolean
+root_register (gpointer data)
 {
+  GtkAtSpiRoot *self = data;
+
   /* Register the root element; every application has a single root, so we only
    * need to do this once.
    *
@@ -499,6 +504,21 @@ gtk_at_spi_root_register (GtkAtSpiRoot *self)
                           NULL,
                           on_registration_reply,
                           self);
+
+  return G_SOURCE_REMOVE;
+}
+
+void
+gtk_at_spi_root_register (GtkAtSpiRoot *self)
+{
+  if (self->register_id != 0)
+    return;
+
+  if (self->cache != NULL)
+    return;
+
+  self->register_id = g_idle_add (root_register, self);
+  g_source_set_name_by_id (self->register_id, "[gtk] ATSPI root registration");
 }
 
 static void
diff --git a/gtk/a11y/gtkatspirootprivate.h b/gtk/a11y/gtkatspirootprivate.h
index 0ce5e6b693..6be66dc958 100644
--- a/gtk/a11y/gtkatspirootprivate.h
+++ b/gtk/a11y/gtkatspirootprivate.h
@@ -33,6 +33,9 @@ G_DECLARE_FINAL_TYPE (GtkAtSpiRoot, gtk_at_spi_root, GTK, AT_SPI_ROOT, GObject)
 GtkAtSpiRoot *
 gtk_at_spi_root_new (const char *bus_address);
 
+void
+gtk_at_spi_root_register (GtkAtSpiRoot *self);
+
 GDBusConnection *
 gtk_at_spi_root_get_connection (GtkAtSpiRoot *self);
 
diff --git a/gtk/gtkatcontext.c b/gtk/gtkatcontext.c
index dd771bc10c..9cca9574d2 100644
--- a/gtk/gtkatcontext.c
+++ b/gtk/gtkatcontext.c
@@ -155,6 +155,16 @@ gtk_at_context_real_bounds_change (GtkATContext *self)
 {
 }
 
+static void
+gtk_at_context_real_realize (GtkATContext *self)
+{
+}
+
+static void
+gtk_at_context_real_unrealize (GtkATContext *self)
+{
+}
+
 static void
 gtk_at_context_class_init (GtkATContextClass *klass)
 {
@@ -164,6 +174,8 @@ gtk_at_context_class_init (GtkATContextClass *klass)
   gobject_class->get_property = gtk_at_context_get_property;
   gobject_class->finalize = gtk_at_context_finalize;
 
+  klass->realize = gtk_at_context_real_realize;
+  klass->unrealize = gtk_at_context_real_unrealize;
   klass->state_change = gtk_at_context_real_state_change;
   klass->platform_change = gtk_at_context_real_platform_change;
   klass->bounds_change = gtk_at_context_real_bounds_change;
@@ -503,6 +515,36 @@ gtk_at_context_create (GtkAccessibleRole  accessible_role,
   return res;
 }
 
+gboolean
+gtk_at_context_is_realized (GtkATContext *self)
+{
+  return self->realized;
+}
+
+void
+gtk_at_context_realize (GtkATContext *self)
+{
+  if (self->realized)
+    return;
+
+  GTK_NOTE (A11Y, g_message ("Realizing AT context '%s'", G_OBJECT_TYPE_NAME (self)));
+  GTK_AT_CONTEXT_GET_CLASS (self)->realize (self);
+
+  self->realized = TRUE;
+}
+
+void
+gtk_at_context_unrealize (GtkATContext *self)
+{
+  if (!self->realized)
+    return;
+
+  GTK_NOTE (A11Y, g_message ("Unrealizing AT context '%s'", G_OBJECT_TYPE_NAME (self)));
+  GTK_AT_CONTEXT_GET_CLASS (self)->unrealize (self);
+
+  self->realized = FALSE;
+}
+
 /*< private >
  * gtk_at_context_update:
  * @self: a #GtkATContext
@@ -515,6 +557,9 @@ gtk_at_context_update (GtkATContext *self)
 {
   g_return_if_fail (GTK_IS_AT_CONTEXT (self));
 
+  if (!self->realized)
+    return;
+
   /* There's no point in notifying of state changes if there weren't any */
   if (self->updated_properties == 0 &&
       self->updated_relations == 0 &&
diff --git a/gtk/gtkatcontextprivate.h b/gtk/gtkatcontextprivate.h
index edc7fa3c94..20db8e554b 100644
--- a/gtk/gtkatcontextprivate.h
+++ b/gtk/gtkatcontextprivate.h
@@ -106,6 +106,8 @@ struct _GtkATContext
   GtkAccessiblePropertyChange updated_properties;
   GtkAccessibleRelationChange updated_relations;
   GtkAccessiblePlatformChange updated_platform;
+
+  guint realized : 1;
 };
 
 struct _GtkATContextClass
@@ -124,10 +126,16 @@ struct _GtkATContextClass
                             GtkAccessiblePlatformChange  changed_platform);
 
   void (* bounds_change) (GtkATContext                *self);
+  void (* realize)       (GtkATContext                *self);
+  void (* unrealize)     (GtkATContext                *self);
 };
 
 GdkDisplay *            gtk_at_context_get_display              (GtkATContext          *self);
 
+void                    gtk_at_context_realize                  (GtkATContext          *self);
+void                    gtk_at_context_unrealize                (GtkATContext          *self);
+gboolean                gtk_at_context_is_realized              (GtkATContext          *self);
+
 void                    gtk_at_context_update                   (GtkATContext          *self);
 
 void                    gtk_at_context_set_accessible_state     (GtkATContext          *self,
diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c
index 49a16937cb..fdaaa01c43 100644
--- a/gtk/gtkwidget.c
+++ b/gtk/gtkwidget.c
@@ -818,6 +818,7 @@ static void
 gtk_widget_real_root (GtkWidget *widget)
 {
   GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget);
+  GtkATContext *context;
   GList *l;
 
   gtk_widget_forall (widget, (GtkCallback) gtk_widget_root, NULL);
@@ -827,6 +828,10 @@ gtk_widget_real_root (GtkWidget *widget)
       if (GTK_IS_SHORTCUT_CONTROLLER (l->data))
         gtk_shortcut_controller_root (GTK_SHORTCUT_CONTROLLER (l->data));
     }
+
+  context = gtk_accessible_get_at_context (GTK_ACCESSIBLE (widget));
+  if (context != NULL)
+    gtk_at_context_realize (context);
 }
 
 static void
@@ -835,6 +840,9 @@ gtk_widget_real_unroot (GtkWidget *widget)
   GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget);
   GList *l;
 
+  if (priv->at_context != NULL)
+    gtk_at_context_unrealize (priv->at_context);
+
   for (l = priv->event_controllers; l; l = l->next)
     {
       if (GTK_IS_SHORTCUT_CONTROLLER (l->data))


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