7.4 KiB
Background accessibility events
On Xorg, Cthulhu rejects known background control updates before resolving selected descendants or capturing their text, values, attributes and geometry. The native adapter owns this decision on the GLib thread. It uses the existing foreground window and epoch; there is no additional window ownership cache.
Events from another provider first resolve their containing window: GTK socket children can belong to the foreground host while using a different provider bus. Unrelated providers are then rejected using the resolved window and copied bus identities, before any content queries. Events from another window on the same provider undergo the existing window correlation before content capture. Lifecycle invalidation still runs, but a background provider no longer clears another provider's cached document lines.
Window creation, activation, deactivation, destruction and defunct events keep
their existing handling. Announcement events are explicit requests to speak
and also bypass the filter; see announcements. Native alerts, notification daemons and supported
Steam toasts retain their notification paths. Menu popups retain existing
foreground checks. Verified active/showing mate-panel menu windows may pass
across provider boundaries, including over an empty desktop. Embedded panel
controls use their host's normal X11 correlation; unrelated panel windows and
hidden menus remain filtered. See MATE desktop.
Same-provider autocomplete relations and tooltip candidates
continue through the existing event-loop ownership checks; they do not acquire
focus merely by passing the early filter.
When notification metadata or a detached document's containing window is unknown, the adapter retains the existing validation path. The optimization does not introduce an X11 ownership requirement for native Wayland sessions.
Orca's src/orca/event_manager.py was used as a reference for keeping global
window monitoring and filtering noisy content events before presentation.
Automated checks
Run from the repository root with the normal native build dependencies:
go test ./... -count=1
go test -race ./internal/atspi ./internal/app ./internal/review -count=1
python3 tests/native_test_session.py env CTHULHU_NATIVE_BACKGROUND_EVENTS=1 CTHULHU_NATIVE_WAYLAND_POLICY=1 go test -race ./internal/atspi -run '^TestNative(BackgroundEventFiltering|WaylandForegroundPolicy)$' -count=1 -v -timeout=40s
python3 tests/native_webscripts.py
CTHULHU_NATIVE_BROWSER_TESTS=browser,tabs,live,tooltips python3 tests/native_browser.py
go build -o /tmp/cthulhu-background-events ./cmd/cthulhu
CTHULHU_BINARY=/tmp/cthulhu-background-events python3 tests/native_ownership.py
The flood regression uses two real GTK providers, then two windows on one provider. Each burst changes the table selection 300 times. A private D-Bus monitor asserts that the rejected updates cause no Selection, Table, Text or Value interface calls. The explicit alert used as a completion marker is excluded from this content-query assertion. Returning to the noisy window must deliver its current table selection. The separate cache regression verifies that invalidation preserves other providers' document lines.
All native checks use disposable X servers and private D-Bus/runtime sockets. They do not replace the active desktop reader. Browser checks cover address suggestions, page controls, tabs, live regions and tooltips. Ownership checks cover GTK/Qt menus, a GTK menu popup, dialogs, same-process windows, empty workspaces and Brave window recovery. Notification checks preserve Steam toast capture while rejecting ordinary background Chromium text changes.
Live acceptance
Private fixtures do not establish audible speech, physical Braille or actual FastSMRW update latency. After reviewing the diff, preserve the current debug log and rebuild/start the candidate through the normal local launcher when ready to replace the reader.
- Read headings and lines in Brave while FastSMRW receives new items. Compare responsiveness with quiet periods. Background selection changes should not delay reading or take speech/focus ownership.
- Switch to FastSMRW and move through results. Confirm the current selection is spoken; open its Open Links popup, move between links, and close it.
- Switch between two windows from the same application. Open and close a dialog and a menu; confirm speech follows the active control.
- In Brave, type into the address bar and move through suggestions. Return to the page, switch tabs, and verify normal editing and reading still work.
- Check supported background notifications. If using Braille, confirm it follows the foreground control and still displays supported notifications.
Preserve a fresh log if lag or a missing announcement remains, with the time and the key or update that triggered it. Debug logs can contain private text.
Window frame geometry
GTK can report the window manager's enclosing frame while X11's active client
reports only its content area. Slint/i3 exposed this as an active, showing
1916×994 accessible frame at (0,0) rejected against a 1912×974 client at (2,18).
Missing or stale _NET_FRAME_EXTENTS can make the advertised outer bounds
identical to those client bounds.
Foreground matching also accepts the actual immediate enclosing X11 frame.
It must contain the client, have bounded decoration edges, and contain no other
managed client. The desktop root, a parent that is itself a managed client,
and shared containers are excluded. Existing process, active/showing state,
iconification, foreground epoch and same-process sibling checks still apply.
This covers frame/client discrepancies across applications; arbitrary incorrect
AT-SPI geometry still requires diagnosis. Rejected activation diagnostics now
include x_frame_bounds alongside client and advertised outer bounds.
i3 marks its decoration frames override_redirect; these frames must pass
the same tree and containment checks. Rejecting that flag discarded the real
frame even after frame lookup was added. A later Slint log showed the Run
dialog and application menu at (0,24), 1916×989, against a client at (2,24),
1912×987, with no recorded frame. This caused control events to be filtered,
while explicit suggestion-count announcements still passed.
python3 tests/native_test_session.py env CTHULHU_NATIVE_FRAME_FOREGROUND=1 CTHULHU_NATIVE_GRAB_FOREGROUND=1 go test ./internal/platform/xorg -run '^TestNative(ReparentedFrameForeground|InputOnlyGrabForeground)$' -count=1 -v
The frame fixture covers i3 override-redirect frames (including the reported Run dialog geometry), missing, zero and stale extents, borders, moving, resizing, switching to a sibling frame, shared containers and empty workspaces. On Slint, listen while switching between two windows from one application, another application's window, dialogs and menus. Move and resize them, switch away to an empty workspace and return, and check preferences again. Confirm speech and Braille follow the foreground control and background activity does not take focus. Keep the first rejected activation from a fresh debug log if a window remains silent; private X11 results do not prove affected-host acceptance.
On Slint/I38, open the Run dialog, type a command, and use Left/Right, Home/End and Control+Left/Right to review it. Check suggestion navigation as well as counts. Open Alt+F1 and move through categories and applications, then close it and verify speech returns to the previous window.