6.2 KiB
Qt control state reporting
Cthulhu preserves AT-SPI checkability independently of the native role. An ordinary menu-item role with CHECKABLE, CHECKED or INDETERMINATE is presented as a check menu item for speech, state sounds and Braille. A checked state-change event also establishes checkability when its new value is false. When a provider omits CHECKABLE, Cthulhu remembers that evidence on the native accessible object, so a subsequent unchecked value still has check-menu semantics. This applies to all applications without matching names or labels. An ordinary menu command without that evidence keeps its existing presentation.
Qt menu actions can change state without sending a checked-state event. Cthulhu refreshes their native cache on focus so reopening a menu reads the current state instead of a previously cached value.
Actions shared between a menu bar and a context menu can keep their original AT-SPI parent. Qt can emit focus-gained for the shared accessible while a state query reports it unfocused because its original menu is hidden. Cthulhu uses that explicit focus event for menu items, including check and radio menu items, before applying the existing foreground checks. The event must name the captured accessible itself; redirected descendants and defunct items do not inherit this evidence. No focus evidence is retained beyond the event.
Checkability evidence belongs to the AT-SPI accessible on the GLib owner thread. No extra accessible references are retained. It ends with that object's native lifetime, is cleared on defunct/name-change events or a changed role, and respects withdrawal of an explicitly exported CHECKABLE state. Replacement actions, including ones with the same label, do not inherit the evidence.
Automated checks
Run the generic Qt 5 (PyQt5) and Qt 6 (PySide6) fixture with:
/usr/bin/python3 tests/native_qt_controls.py
This uses private Xvfb, D-Bus and temporary configuration directories. It checks queued speech and Braille frames through Cthulhu's event loop for menu-bar and context-menu actions, checking and unchecking ordinary checkboxes and checkable buttons, an ordinary menu command, same-label action replacement, and explicit capability removal. It also traverses shared and context-only actions in both directions and after reopening the context menu. It also checks Left and Escape from nested menus, including providers which report the return only by hiding the submenu; see Audacious GUI checks. It requires both Python Qt bindings, xdotool and the native build dependencies. It does not replace the active reader or establish audible speech or physical Braille acceptance.
Existing package checks cover mixed states, state sounds, speech-only and sound-only modes, and repeated focus after checking and unchecking. The existing browser menu test checks ordinary and compound menu navigation:
go test ./internal/atspi ./internal/presentation ./internal/app -count=1
CTHULHU_NATIVE_BROWSER_TESTS=menu /usr/bin/python3 tests/native_browser.py
Providers which omit checkability
The tested Mumble 1.5.939 / Qt 5 installation exports Mute Self and Deafen Self
as ordinary menu items, with CHECKED only while checked and no CHECKABLE state.
Its toolbar equivalents use the check-box role. Qt 5's upstream
AT-SPI state mapping
omits state.checkable, although
menu accessibility
sets that field internally. Qt 6's
mapping
exports it.
No toolkit patch is required for Cthulhu to retain observed checkability.
The Qt 5 native fixture verifies the sequence unchecked, checked, unchecked
for both menu-bar and context-menu actions. After checking establishes the
capability, unchecking and reopening announces not checked, with the
corresponding Braille indicator. Native checked-state events establish the same
capability even when the first observed transition is to false.
The Python reference's generator._generateCheckedStateIfCheckable and Orca's
generator._generate_state_checked_if_checkable also treat CHECKED as sufficient
evidence even without CHECKABLE. Their menu generators do not assign an
unchecked state to an ordinary menu action without evidence.
On first encountering an unchecked menu action with neither the correct role nor CHECKABLE nor a checked-state event, its semantics are unknown. Cthulhu keeps ordinary menu presentation until evidence arrives, rather than announcing every menu command as unchecked. Retained evidence cannot detect an unreported capability removal by a provider that never exports CHECKABLE; object destruction, role changes and name changes still retire it.
Live acceptance
After reviewing the diff, rebuild and restart the intended Cthulhu binary:
- In Mumble's Self menu, navigate to Mute Self and Deafen Self. Toggle each, reopen the menu, and confirm both checked and not-checked states once the capability is established. Restore the original settings.
- On a server authorized for testing, open another user's context menu and exercise Local Mute in both states. Reopen the menu after each change. Restore the original local mute setting; do not change server mute.
- Test the toolbar mute/deafen controls and ordinary settings checkboxes in both states. Confirm Tab and Shift+Tab still reach adjacent controls.
- Navigate ordinary Mumble and browser menu commands. Confirm no invented checked state, duplicate announcements, or lost keyboard focus.
- Listen to actual speech and check a physical Braille display when available.
- Open your own user context menu. Arrow through every enabled item in both directions, including ordinary commands and checkable items, then close and reopen it and repeat. Confirm every stop reads once. Also test the menu-bar equivalents, Escape back to the user list, and switching away from Mumble while the popup is open; background menu events must remain silent.
Do not submit before human review and live testing, as required by the contribution policy.