// Copyright (c) 2026 Storm Dragon // Authors: Storm Dragon, Angus Kola // SPDX-License-Identifier: GPL-3.0-or-later #include #include // 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); }