Files
cthulhu/docs/testing/announcements.md

3.6 KiB

AT-SPI announcements

Providers can ask the reader to speak a message without moving focus by emitting the AT-SPI object:announcement event. The event's any_data carries the text and detail1 carries the politeness (AtspiLive: 1 is polite, 2 is assertive). Orca presents the text verbatim; Cthulhu now does the same, with a Braille flash through the system-role output queue.

The motivating provider is the Niri compositor (25.08+), which exposes its own UI through AccessKit. A capture from Niri 26.04 on 2026-10-31 shows a workspace switch emits object:property-change:accessible-name and then object:announcement (polite) with text such as Workspace 2 Example on a label in the niri application, about 10 ms before the newly focused window activates. Niri alternates live-node identities (and may append a space) so repeated messages still emit; Cthulhu trims the payload and dedupes one provider's repeat inside 300 ms.

Announcements bypass background-event filtering and foreground epochs: the provider explicitly requested speech and may never own focus. They do not move focus, review, or reading state. Screen-lock and secure-input gates still suppress them. Both polite and assertive announcements queue under Cthulhu's input-driven interruption policy. Politeness metadata is preserved by the adapter. Payload text is bounded by the adapter's existing snapshot sanitization.

Automated checks

Run from the repository root with the normal native build dependencies:

go test ./internal/atspi ./internal/app -run Announcement -count=1
python3 tests/native_test_session.py env CTHULHU_NATIVE_ANNOUNCEMENT=1 go test ./internal/atspi -run '^TestNativeAnnouncementEventDelivered$' -count=1 -v -timeout=40s

The native check uses tests/gtk_announce.py, a GTK4 fixture whose announce calls must arrive as adapter events with the exact payload and politeness (medium priority arrives polite, high arrives assertive).

On hosts without a systemd user manager, D-Bus activation of the AT-SPI registry can fail inside the private session (the pre-existing native checks fail the same way). Starting the a11y bus and registry manually inside the session works around it:

python3 tests/native_test_session.py env CTHULHU_NATIVE_ANNOUNCEMENT=1 sh -c '/usr/lib/at-spi-bus-launcher & sleep 1; ADDR=$(busctl --user call org.a11y.Bus /org/a11y/bus org.a11y.Bus GetAddress | sed "s/^s //;s/\"//g"); AT_SPI_BUS_ADDRESS=$ADDR /usr/lib/at-spi2-registryd & sleep 1; go test ./internal/atspi/ -run "^TestNativeAnnouncementEventDelivered$" -count=1 -v -timeout=40s'

Live acceptance (Niri)

Requires the maintainer's authorization to replace the running reader. Report automated, audible-speech, and physical-Braille results separately.

  1. Start the built reader in a full Niri session (started via a display manager or niri-session, so Niri exposes its accessibility tree).
  2. Switch between named, unnamed, and the trailing empty workspace with Niri's own binds. Each switch speaks the workspace label once, before the newly focused window, and focus presentation still follows.
  3. Burst-switch several times quickly. Announcements queue in arrival order; fresh keyboard input may cancel earlier output. No duplicates should come from the paired accessible-name change.
  4. Cycle the AltTab switcher and open the exit dialog (Super+Shift+E) and the hotkeys list (Super+Shift+/). Selected windows and dialog text are announced as with Orca on the same session.
  5. Introduce a temporary config parse error and reload. The error is announced.
  6. Lock the screen and switch workspaces. Nothing is spoken or flashed until unlock.