Filter out stale accessibility events.
This commit is contained in:
1 parent
9e15f8652d
commit
f23e89ed93
10 files changed
+469
-2
No files matched your search
@@ -0,0 +1,55 @@
|
||||
// Copyright (c) 2026 Storm Dragon
|
||||
// Authors: Storm Dragon, Angus Kola
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
#include <atspi/atspi.h>
|
||||
#include <dbus/dbus.h>
|
||||
|
||||
// Use GTK's actual accessibility connection, bypassing the ATK bridge's
|
||||
// producer-side event compression. The reader must handle a provider which
|
||||
// delivers every stale claim, as in the captured timeline refresh.
|
||||
static void emit_event(DBusConnection *bus, const char *path, const char *member,
|
||||
const char *detail, int detail1, const char *child) {
|
||||
DBusMessage *message = dbus_message_new_signal(path, "org.a11y.atspi.Event.Object", member);
|
||||
DBusMessageIter body, variant, reference, properties;
|
||||
int zero = 0;
|
||||
dbus_message_iter_init_append(message, &body);
|
||||
dbus_message_iter_append_basic(&body, DBUS_TYPE_STRING, &detail);
|
||||
dbus_message_iter_append_basic(&body, DBUS_TYPE_INT32, &detail1);
|
||||
dbus_message_iter_append_basic(&body, DBUS_TYPE_INT32, &zero);
|
||||
dbus_message_iter_open_container(&body, DBUS_TYPE_VARIANT, child != NULL ? "(so)" : "i", &variant);
|
||||
if (child != NULL) {
|
||||
const char *sender = dbus_bus_get_unique_name(bus);
|
||||
dbus_message_iter_open_container(&variant, DBUS_TYPE_STRUCT, NULL, &reference);
|
||||
dbus_message_iter_append_basic(&reference, DBUS_TYPE_STRING, &sender);
|
||||
dbus_message_iter_append_basic(&reference, DBUS_TYPE_OBJECT_PATH, &child);
|
||||
dbus_message_iter_close_container(&variant, &reference);
|
||||
} else {
|
||||
dbus_message_iter_append_basic(&variant, DBUS_TYPE_INT32, &zero);
|
||||
}
|
||||
dbus_message_iter_close_container(&body, &variant);
|
||||
dbus_message_iter_open_container(&body, DBUS_TYPE_ARRAY, "{sv}", &properties);
|
||||
dbus_message_iter_close_container(&body, &properties);
|
||||
dbus_connection_send(bus, message, NULL);
|
||||
dbus_message_unref(message);
|
||||
}
|
||||
|
||||
const char *refresh_bus_name(void) {
|
||||
return dbus_bus_get_unique_name(atspi_get_a11y_bus());
|
||||
}
|
||||
|
||||
void emit_refresh(const char *tablePath, const char *cellPaths) {
|
||||
DBusConnection *bus = atspi_get_a11y_bus();
|
||||
gchar **paths = g_strsplit(cellPaths, ",", -1);
|
||||
for (guint index = 0; paths[index] != NULL; index++) {
|
||||
emit_event(bus, paths[index], "StateChanged", "defunct", 1, NULL);
|
||||
}
|
||||
for (guint index = 0; paths[index] != NULL; index++) {
|
||||
emit_event(bus, paths[index], "StateChanged", "selected", 1, NULL);
|
||||
emit_event(bus, tablePath, "SelectionChanged", "", 0, NULL);
|
||||
emit_event(bus, paths[index], "StateChanged", "focused", 1, NULL);
|
||||
emit_event(bus, tablePath, "ActiveDescendantChanged", "", 1, paths[index]);
|
||||
}
|
||||
g_strfreev(paths);
|
||||
dbus_connection_flush(bus);
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env python3
|
||||
# Copyright (c) 2026 Storm Dragon
|
||||
# Authors: Storm Dragon, Angus Kola
|
||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||
"""Controlled GTK table replacement and obsolete cell event flood."""
|
||||
import ctypes
|
||||
import os
|
||||
import sys
|
||||
|
||||
import gi
|
||||
|
||||
gi.require_version("Atk", "1.0")
|
||||
gi.require_version("Gtk", "3.0")
|
||||
gi.require_version("GdkX11", "3.0")
|
||||
from gi.repository import Atk, GdkX11, GLib, Gtk
|
||||
|
||||
|
||||
window = Gtk.Window(title="Table refresh probe")
|
||||
window.set_default_size(400, 300)
|
||||
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||
window.add(box)
|
||||
store = Gtk.ListStore(str)
|
||||
for index in range(300):
|
||||
store.append(["Original row " + str(index)])
|
||||
table = Gtk.TreeView(model=store)
|
||||
table.append_column(Gtk.TreeViewColumn("Row", Gtk.CellRendererText(), text=0))
|
||||
table.get_accessible().set_name("Refresh results")
|
||||
scroll = Gtk.ScrolledWindow()
|
||||
scroll.add(table)
|
||||
box.pack_start(scroll, True, True, 0)
|
||||
entry = Gtk.Entry()
|
||||
entry.get_accessible().set_name("After table")
|
||||
box.pack_start(entry, False, False, 0)
|
||||
marker = Gtk.Label(label="Marker")
|
||||
marker.get_accessible().set_role(Atk.Role.ALERT)
|
||||
box.pack_start(marker, False, False, 0)
|
||||
window.show_all()
|
||||
table.set_cursor(0)
|
||||
table.grab_focus()
|
||||
pendingInput = ""
|
||||
signals = ctypes.CDLL(sys.argv[1])
|
||||
signals.emit_refresh.argtypes = [ctypes.c_char_p, ctypes.c_char_p]
|
||||
signals.emit_refresh.restype = None
|
||||
signals.refresh_bus_name.restype = ctypes.c_char_p
|
||||
|
||||
|
||||
def receive_command(_source, _condition):
|
||||
global pendingInput
|
||||
content = os.read(sys.stdin.fileno(), 4096)
|
||||
if not content:
|
||||
Gtk.main_quit()
|
||||
return False
|
||||
pendingInput += content.decode()
|
||||
while "\n" in pendingInput:
|
||||
command, pendingInput = pendingInput.split("\n", 1)
|
||||
if command.startswith("refresh "):
|
||||
_, tablePath, cellPaths = command.split()
|
||||
signals.emit_refresh(tablePath.encode(), cellPaths.encode())
|
||||
store.clear()
|
||||
for index in range(300):
|
||||
store.append(["Updated row " + str(index)])
|
||||
table.set_cursor(0)
|
||||
# End with a real selection, without giving GTK another focus
|
||||
# change to repair a lost deferred selection for us.
|
||||
table.get_selection().select_path(7)
|
||||
marker.set_text("Refresh complete")
|
||||
elif command.startswith("select "):
|
||||
table.set_cursor(int(command.split()[1]))
|
||||
elif command == "entry":
|
||||
entry.grab_focus()
|
||||
elif command == "barrier":
|
||||
entry.set_text("Focus boundary complete")
|
||||
elif command == "quit":
|
||||
Gtk.main_quit()
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
GLib.io_add_watch(sys.stdin, GLib.IO_IN | GLib.IO_HUP, receive_command)
|
||||
GLib.idle_add(lambda: print("ready", GdkX11.X11Window.get_xid(window.get_window()), signals.refresh_bus_name().decode(), flush=True) or False)
|
||||
Gtk.main()
|
||||
Reference in new issue
Block a user