Tightly integrate tree updates with surface rendering, so there's always at least one tree update before each surface commit, as required by Newton, without the AccessKit context having to do extra surface commits itself. This should also prevent multiple tree updates per frame across platforms.

This commit is contained in:
Matt Campbell 2024-05-28 06:39:34 -05:00 committed by Matthias Clasen
parent 9131b28834
commit be7a23fa5e
7 changed files with 58 additions and 41 deletions

View File

@ -449,6 +449,14 @@ gdk_wayland_surface_commit (GdkSurface *surface)
wl_surface_commit (impl->display_server.wl_surface);
}
void
gdk_wayland_surface_force_next_commit (GdkSurface *surface)
{
GdkWaylandSurface *impl = GDK_WAYLAND_SURFACE (surface);
impl->has_pending_subsurface_commits = TRUE;
}
void
gdk_wayland_surface_notify_committed (GdkSurface *surface)
{

View File

@ -43,5 +43,8 @@ GType gdk_wayland_surface_get_type (void);
GDK_AVAILABLE_IN_ALL
struct wl_surface *gdk_wayland_surface_get_wl_surface (GdkSurface *surface);
GDK_AVAILABLE_IN_ALL
void gdk_wayland_surface_force_next_commit (GdkSurface *surface);
G_END_DECLS

View File

@ -1853,3 +1853,12 @@ gtk_accesskit_context_add_to_update (GtkAccessKitContext *self,
accesskit_tree_update_push_node (update, self->id,
accesskit_node_builder_build (builder));
}
void
gtk_accesskit_context_update_tree (GtkAccessKitContext *self)
{
if (!gtk_at_context_is_realized (GTK_AT_CONTEXT (self)))
return;
gtk_accesskit_root_update_tree (self->root);
}

View File

@ -42,4 +42,7 @@ void
gtk_accesskit_context_add_to_update (GtkAccessKitContext *self,
accesskit_tree_update *update);
void
gtk_accesskit_context_update_tree (GtkAccessKitContext *self);
G_END_DECLS

View File

@ -44,7 +44,7 @@ struct _GtkAccessKitRoot
GHashTable *contexts;
GArray *update_queue;
gboolean did_initial_update;
gint update_id;
gboolean requested_initial_tree;
#if defined(GDK_WINDOWING_WIN32)
accesskit_windows_subclassing_adapter *adapter;
@ -72,7 +72,6 @@ gtk_accesskit_root_finalize (GObject *gobject)
g_clear_pointer (&self->contexts, g_hash_table_destroy);
g_clear_pointer (&self->update_queue, g_array_unref);
g_clear_handle_id (&self->update_id, g_source_remove);
#if defined(GDK_WINDOWING_WIN32)
g_clear_pointer (&self->adapter, accesskit_windows_subclassing_adapter_free);
@ -223,6 +222,7 @@ request_initial_tree_main_thread (void *data)
{
GtkAccessKitRoot *self = data;
accesskit_tree_update *update = build_full_update (self);
self->requested_initial_tree = TRUE;
self->did_initial_update = TRUE;
return update;
}
@ -237,35 +237,21 @@ update_if_active (GtkAccessKitRoot *self, accesskit_tree_update_factory factory)
if (events)
accesskit_windows_queued_events_raise (events);
#elif defined(GDK_WINDOWING_WAYLAND)
/* TBD: Newton treats accessibility tree updates as double-buffered state,
meaning the surface has to be committed after the update is sent.
Can we more tightly integrate accessibility updates with rendering,
so everything happens atomically as intended, rather than queuing
an idle callback for the accessibility update, then committing
the surface? */
GdkSurface *surface = gtk_native_get_surface (GTK_NATIVE (self->root_widget));
struct wl_surface *wl_surface = gdk_wayland_surface_get_wl_surface (surface);
gdk_wayland_surface_force_next_commit (surface);
accesskit_newton_adapter_update_if_active (self->adapter, factory, self);
wl_surface_commit (wl_surface);
/* TODO: other platforms */
#endif
}
static gboolean
initial_update (gpointer data)
static void
queue_tree_update (GtkAccessKitRoot *self)
{
GtkAccessKitRoot *self = data;
if (!gtk_widget_get_mapped (GTK_WIDGET (self->root_widget)))
return;
self->update_id = 0;
if (gtk_widget_get_mapped (GTK_WIDGET (self->root_widget)))
{
update_if_active (self, build_full_update);
self->did_initial_update = TRUE;
}
return G_SOURCE_REMOVE;
GdkSurface *surface = gtk_native_get_surface (GTK_NATIVE (self->root_widget));
gdk_surface_queue_render (surface);
}
static accesskit_tree_update *
@ -273,8 +259,8 @@ request_initial_tree_other_thread (void *data)
{
GtkAccessKitRoot *self = data;
if (!self->update_id)
self->update_id = g_idle_add (initial_update, self);
queue_tree_update (self);
self->requested_initial_tree = TRUE;
return NULL;
}
@ -292,8 +278,8 @@ deactivate_accessibility (void *data)
/* TODO: Unrealize AT contexts. Which ones? */
g_clear_pointer (&self->update_queue, g_array_unref);
g_clear_handle_id (&self->update_id, g_source_remove);
self->did_initial_update = FALSE;
self->requested_initial_tree = FALSE;
}
static void
@ -485,19 +471,6 @@ build_incremental_update (void *data)
return update;
}
static gboolean
incremental_update (gpointer data)
{
GtkAccessKitRoot *self = data;
self->update_id = 0;
if (gtk_widget_get_mapped (GTK_WIDGET (self->root_widget)))
update_if_active (self, build_incremental_update);
return G_SOURCE_REMOVE;
}
void
gtk_accesskit_root_queue_update (GtkAccessKitRoot *self,
guint32 id,
@ -508,6 +481,17 @@ gtk_accesskit_root_queue_update (GtkAccessKitRoot *self,
add_to_update_queue (self, id, force_to_end);
if (!self->update_id)
self->update_id = g_idle_add (incremental_update, self);
queue_tree_update (self);
}
void
gtk_accesskit_root_update_tree (GtkAccessKitRoot *self)
{
if (self->did_initial_update && self->update_queue)
update_if_active (self, build_incremental_update);
else if (self->requested_initial_tree)
{
update_if_active (self, build_full_update);
self->did_initial_update = TRUE;
}
}

View File

@ -49,4 +49,7 @@ gtk_accesskit_root_queue_update (GtkAccessKitRoot *self,
guint32 id,
gboolean force_to_end);
void
gtk_accesskit_root_update_tree (GtkAccessKitRoot *self);
G_END_DECLS

View File

@ -71,6 +71,7 @@
#include "gtktestatcontextprivate.h"
#include "inspector/window.h"
#include "a11y/gtkaccesskitcontextprivate.h"
#include "gdk/gdkeventsprivate.h"
#include "gdk/gdkprofilerprivate.h"
@ -12077,6 +12078,7 @@ gtk_widget_render (GtkWidget *widget,
const cairo_region_t *region)
{
GtkWidgetPrivate *priv = gtk_widget_get_instance_private (widget);
GtkATContext *at_ctx;
GtkSnapshot *snapshot;
GskRenderer *renderer;
GskRenderNode *root;
@ -12090,6 +12092,11 @@ gtk_widget_render (GtkWidget *widget,
if (!GTK_IS_NATIVE (widget))
return;
at_ctx = gtk_accessible_get_at_context (GTK_ACCESSIBLE (widget));
if (GTK_IS_ACCESSKIT_CONTEXT (at_ctx))
gtk_accesskit_context_update_tree (GTK_ACCESSKIT_CONTEXT (at_ctx));
g_object_unref (at_ctx);
renderer = gtk_native_get_renderer (GTK_NATIVE (widget));
if (renderer == NULL)
return;