From 4ea4dc9176a3f33cf2752cc65290d98810e0f2bd Mon Sep 17 00:00:00 2001 From: Emmanuele Bassi Date: Fri, 24 May 2024 19:15:09 +0100 Subject: [PATCH] a11y: Watch EventListenerRegistered/Deregistered signals Assistive technologies using AT-SPI typically register themselves on the accessibility bus through the org.a11y.atspi.Registry.RegisterEvent method, which will emit the EventListenerRegistered signal. We can use that signal (and its corresponding EventListenerDeregistered sibling) to know whether there is at least an AT on the other side of the accessibility bus. --- gtk/a11y/gtkatspiroot.c | 110 +++++++++++++++++++++++++++++++++ gtk/a11y/gtkatspirootprivate.h | 3 + 2 files changed, 113 insertions(+) diff --git a/gtk/a11y/gtkatspiroot.c b/gtk/a11y/gtkatspiroot.c index 8727851add..de4e875d1d 100644 --- a/gtk/a11y/gtkatspiroot.c +++ b/gtk/a11y/gtkatspiroot.c @@ -45,6 +45,7 @@ #define ATSPI_PATH_PREFIX "/org/a11y/atspi" #define ATSPI_ROOT_PATH ATSPI_PATH_PREFIX "/accessible/root" #define ATSPI_CACHE_PATH ATSPI_PATH_PREFIX "/cache" +#define ATSPI_REGISTRY_PATH ATSPI_PATH_PREFIX "/registry" struct _GtkAtSpiRoot { @@ -71,6 +72,9 @@ struct _GtkAtSpiRoot GtkAtSpiCache *cache; GListModel *toplevels; + + /* HashTable */ + GHashTable *event_listeners; }; enum @@ -90,6 +94,7 @@ 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_clear_pointer (&self->event_listeners, g_hash_table_unref); g_free (self->bus_address); g_free (self->base_path); @@ -541,6 +546,81 @@ on_registration_reply (GObject *gobject, g_free (data); } +static void +on_event_listener_registered (GDBusConnection *connection, + const char *sender_name, + const char *object_path, + const char *interface_name, + const char *signal_name, + GVariant *parameters, + gpointer user_data) +{ + GtkAtSpiRoot *self = user_data; + + if (g_strcmp0 (object_path, ATSPI_REGISTRY_PATH) == 0 && + g_strcmp0 (interface_name, "org.a11y.atspi.Registry") == 0 && + g_strcmp0 (signal_name, "EventListenerRegistered") == 0) + { + char *sender = NULL; + char *event_name = NULL; + char **event_types = NULL; + + if (self->event_listeners == NULL) + self->event_listeners = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, + NULL); + + g_variant_get (parameters, "(ssas)", &sender, &event_name, &event_types); + + GTK_DEBUG (A11Y, "Registering event listener (%s, %s) on the a11y bus", + sender, + event_name[0] != 0 ? event_name : "(none)"); + + g_hash_table_add (self->event_listeners, sender); + + g_free (event_name); + g_strfreev (event_types); + } +} + +static void +on_event_listener_deregistered (GDBusConnection *connection, + const char *sender_name, + const char *object_path, + const char *interface_name, + const char *signal_name, + GVariant *parameters, + gpointer user_data) +{ + GtkAtSpiRoot *self = user_data; + + if (g_strcmp0 (object_path, ATSPI_REGISTRY_PATH) == 0 && + g_strcmp0 (interface_name, "org.a11y.atspi.Registry") == 0 && + g_strcmp0 (signal_name, "EventListenerDeregistered") == 0) + { + const char *sender = NULL; + const char *event = NULL; + + if (self->event_listeners == NULL) + { + g_critical ("Received org.a11y.atspi.Registry::EventListenerDeregistered without " + "a corresponding EventListenerRegistered signal."); + return; + } + + g_variant_get (parameters, "(&s&s)", &sender, &event); + + if (g_hash_table_contains (self->event_listeners, sender)) + { + GTK_DEBUG (A11Y, "Deregistering event listener (%s, %s) on the a11y bus", + sender, + event[0] != 0 ? event : "(none)"); + + g_hash_table_remove (self->event_listeners, sender); + } + } +} + static gboolean root_register (gpointer user_data) { @@ -587,6 +667,27 @@ root_register (gpointer user_data) NULL, NULL); + g_dbus_connection_signal_subscribe (self->connection, + "org.a11y.atspi.Registry", + "org.a11y.atspi.Registry", + "EventListenerRegistered", + ATSPI_REGISTRY_PATH, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + on_event_listener_registered, + self, + NULL); + g_dbus_connection_signal_subscribe (self->connection, + "org.a11y.atspi.Registry", + "org.a11y.atspi.Registry", + "EventListenerDeregistered", + ATSPI_REGISTRY_PATH, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + on_event_listener_deregistered, + self, + NULL); + GTK_DEBUG (A11Y, "Registering (%s, %s) on the a11y bus", unique_name, self->root_path); @@ -818,3 +919,12 @@ gtk_at_spi_root_get_base_path (GtkAtSpiRoot *self) return self->base_path; } + +gboolean +gtk_at_spi_root_has_event_listeners (GtkAtSpiRoot *self) +{ + g_return_val_if_fail (GTK_IS_AT_SPI_ROOT (self), FALSE); + + return self->event_listeners != NULL && + g_hash_table_size (self->event_listeners) != 0; +} diff --git a/gtk/a11y/gtkatspirootprivate.h b/gtk/a11y/gtkatspirootprivate.h index 7b626fc28e..627cf3f5b1 100644 --- a/gtk/a11y/gtkatspirootprivate.h +++ b/gtk/a11y/gtkatspirootprivate.h @@ -63,4 +63,7 @@ gtk_at_spi_root_child_changed (GtkAtSpiRoot *self, GtkAccessibleChildChange change, GtkAccessible *child); +gboolean +gtk_at_spi_root_has_event_listeners (GtkAtSpiRoot *self); + G_END_DECLS