Reset master to testing content

This commit is contained in:
Storm Dragon committed 2026-10-25 01:57:54 -04:00
1 parent 0f2587b719
commit 42afa2a56c
1483 files changed
+145308 -938069

No files matched your search

+330
View File
@@ -0,0 +1,330 @@
# LibreOffice integration
Current Orca is the primary behavioral reference for LibreOffice. Python
Cthulhu remains available on the `legacy` branch for historical comparisons:
`git show legacy:src/cthulhu/scripts/apps/soffice/script.py`.
Do not switch the active Go checkout to inspect that reference.
## Architecture
Application handling runs after screen-lock and foreground validation, before
generic focus and text filtering. `internal/app/application_behavior.go`
routes copied accessibility events by verified provider process identity.
The LibreOffice handler lives in `internal/app/libreoffice.go`.
It can normalize an event, request focus adoption, or suppress presentation.
The reader retains window ownership, input policy, pending-output cancellation,
review, and speech/Braille delivery. Ordinary applications keep their existing
event path. Steam's existing integration remains in place.
`internal/atspi/libreoffice_native.go` captures bounded Calc metadata on the
native owner thread. It does not enumerate a sheet. The controller and
presentation code never retain native objects or perform provider queries.
`internal/presentation/libreoffice.go` owns the corresponding speech
formatting; LibreOffice Braille formatting stays beside its controller.
Logical focus belongs to the existing event loop. Selection comparison keeps
one bounded snapshot for the focused sheet, keyed by its application bus and
accessible sheet path and admitted only within the owned document window.
It is retired on sheet/application focus changes and ordinary owned-focus
reset, including destruction, foreground loss, and screen lock. There are no
selection workers, timers, or retained native references. Dynamic header
choices live in the adapter's bounded, synchronized store of copied identities
and coordinates. Commands update that store without provider queries; the
native owner captures header text. Choices use application bus, document/view,
window and the provider's sheet name. Calc recreates table objects on tab
switches, so a table path cannot identify a sheet across those switches.
Focus loss cancels output while preserving document header choices.
Use the existing input machinery for additional Office commands.
Application code must not bypass security/foreground gates or manage its own
speech connection, key grabs, or background event loop.
Keep generic table operations and genuinely shared fixes in their existing
subsystems. Keep verified LibreOffice exceptions in the dedicated files.
Adding later Office features should extend these boundaries, without
reorganizing unrelated reader code or building a speculative plugin framework.
## First slice
- Calc active-cell events establish logical focus, including after editing
and when the sheet changes.
- LibreOffice frame activation and X11 foreground recovery establish window
ownership without the generic focused-descendant search. Calc's following
cell/editor events establish document focus. Searching the previous view
during workbook activation caused stale-object queries and timeouts on return;
leaving activation on the frame avoids that work. Dialogs retain normal
focused-control resolution.
- Cell contents and addresses are separate. Normal speech gives the value,
then the address; an empty value gives “blank.” Only-displayed-text mode
omits the address and the synthetic blank label.
- Recorded Braille preserves value spacing followed by the address. A cell
component is not an editable text route.
- F2 resolves a temporary editor's focused paragraph when available.
If the paragraph appears later, its first owned text/caret event establishes
editor focus before generic filtering.
- Post-editing sheet focus is adopted silently. Spreadsheet capacity and
cell/container state chatter are omitted as in Orca.
- Ancestor focus claims are suppressed when captured ancestry identifies the
existing focused spreadsheet object.
- Non-editor caret events do not displace a focused cell. Selection events do
not promote the first selected cell of a range into logical focus.
References inspected: Orca commit `cd58a88ed` (2026-10-14),
`scripts/apps/soffice/script.py`, `script_utilities.py`,
`speech_generator.py`, `braille_generator.py`, and shared
`speech_generator.py` / `ax_utilities_table.py`.
## Formula slice
- Normal cell speech appends “has formula” after the value and address.
Only-displayed-text mode continues to omit synthetic metadata.
- Basic Where Am I includes the formula after the cell information.
- **Cthulhu+=** presents the current cell's formula in the desktop layout,
following Orca's `where_am_i_presenter.py:present_cell_formula`. The semantic
command is `present-cell-formula`; it is listed in preferences and Learn Mode.
As in current Orca, the laptop layout leaves it unbound; assign a shortcut
in Key Bindings if desired.
- A cell without a formula gives its value, or “empty” for an empty cell.
The command currently applies to Calc cells; other focus reports “Not in a
table.” Temporary F2 editors are separate objects, so leave editing before
requesting the cell's formula.
- Native capture reads the uncached `formula` attribute (or `Formula`, as
accepted by Orca) alongside the existing bounded cell metadata. Formula
string-literal spacing is preserved. Expressions are bounded to the native
snapshot field limit, retaining the beginning of long expressions.
Commands consume the latest accepted focus snapshot; they never scan a sheet
or query a provider from the event loop. A provider change without an event
requires revisiting the cell to refresh its snapshot.
- Formula speech uses the existing command and speech queues, foreground and
screen-lock gates. Normal Braille retains the value and address, with no new
routes or formula suffix. The explicit formula command uses the existing
system-message flash path and its Braille preferences, as in Orca.
## Selection slice
- Shift+Arrow selection growth and shrinkage announce selected/unselected
coordinates. Adjacent cells form compact rectangles; disjoint cells retain
their gaps. The active anchor is left to ordinary cell presentation.
- Full rows and columns use row/column labels. Select All says “All cells
selected”; leaving a selection says “Selection cleared.” Ctrl+A can first
select the used area; press it again to select the entire sheet.
These announcements require a provider event. An independent AT-SPI observer
on LibreOffice 26.2.6.3 confirmed that Shift+Up after extending whole rows,
and Shift+Left after extending whole columns, change the selection without
emitting a selection event. Ctrl+Home can also clear those selections without
an event. Immediate announcements for those cases remain unsupported; the
next selection event refreshes the snapshot. The reader does not poll or
infer selection changes from keystrokes.
- Small selections capture at most 64 cells, rows, or columns. Messages with
more than eight rectangles use a count. Larger selections use a count and
two boundary samples for duplicate suppression, without treating endpoints
as proof of a filled rectangle. Overlapping count-only selections report
the new total rather than falsely announcing the entire old range unselected.
- The GTK bridge caps cell counts at 2147483647; partial-sheet selections at
that limit say “At least … cells selected.” Whole-sheet selection is checked
separately. Large selections with identical counts and boundary samples
cannot be distinguished by this bounded snapshot; interior-only changes may
therefore be suppressed. Full topology/delta reporting remains future work.
- Native capture uses a fixed number of count queries and at most 64 selected
positions. It requests no selected-row/column arrays. Ordinary small cell
selections skip full-row/column counts; LibreOffice itself scans its row
marks when larger selections need a selected-row count. Fixed RPC counts
do not guarantee a provider's response time.
- Selection events keep both logical and retained native focus on the active
cell. Formula commands, object review, cell Braille frames, and routing
continue to use that cell. Selection messages use the existing system-message
Braille flash path and preferences. Only-displayed-text mode suppresses them.
- Current Orca's selection callbacks and utilities guide the behavior. The
[LibreOffice spreadsheet provider](https://github.com/LibreOffice/core/blob/libreoffice-26-2/sc/source/ui/Accessibility/AccessibleSpreadsheet.cxx)
and [GTK selection bridge](https://github.com/LibreOffice/core/blob/libreoffice-26-2/vcl/unx/gtk3/a11y/atkselection.cxx)
were also inspected for selection ordering, row-count cost, and count limits.
## Dynamic headers slice
- In **Preferences → Key Bindings**, assign shortcuts to “Use the current row
as Calc column headers,” “Use the current column as Calc row headers,” and
the corresponding clear commands. All four are available in both layouts
and Learn Mode, and are unbound by default. Orca's Cthulhu+Shift+C equivalent
conflicts with Cthulhu's existing clipboard command; it is preserved.
Semantic commands are `set-column-headers-row`, `set-row-headers-column`,
`clear-column-headers-row` and `clear-row-headers-column`.
- Set column headers from any cell in the heading row, and row headers from
any cell in the label column. Each axis can be changed or cleared separately.
This follows Orca's shared `table_navigator.py` and `ax_utilities_table.py`,
rather than just its soffice script.
- Automatic speech prefixes the row heading when the row changes and the
column heading when the column changes. A cell does not repeat itself as
its own heading. Blank headings add no label. Basic Where Am I includes both
headings; normal Braille includes both through its existing context path.
Only-displayed-text suppresses automatic speech metadata. Formula commands,
active-cell focus, selections and editor routing retain their existing paths.
- Header text is read afresh with at most two direct cell lookups per snapshot,
bounded to the normal field limit. There is no row/sheet enumeration, polling,
worker or native reference retained by the header feature. Provider response
times still apply. Commands consume the accepted cell snapshot; a provider
mutation without a new event requires revisiting the cell.
- Choices survive sheet/workbook switches and lock/unlock for this reader
session. They are separate per document/view and sheet; they are not saved to
the workbook or settings. A reader restart or document/window destruction
clears them. The store accepts up to 64 sheets; clearing both axes frees a slot.
- Sheet names come from the verified provider's uncached `page-name` attribute,
preserving exact whitespace. Missing or oversized identity metadata disables
these commands. This metadata is exposed by LibreOffice's
[spreadsheet document provider](https://github.com/LibreOffice/core/blob/libreoffice-26-2/sc/source/ui/Accessibility/AccessibleDocument.cxx).
Tab-list child changes and tab-name events conservatively clear choices for
that document window. Reassign headers
after adding, removing or renaming sheets. This depends on provider events;
silent structural mutations cannot be detected. Tab selection alone preserves
choices. Merged headings and structural row/column edits remain unverified;
header choices are coordinates, not references that follow moved content.
## Next slices
1. Spreadsheet full-row reading needs a bounded,
explicitly defined extent; the generic setting is not full Calc support.
2. Writer paragraph navigation, selection, Say All, spell-check presentation,
and Braille panning. Compare each actual workflow with current Orca.
Merged cells, overflow announcements, large-sheet responsiveness, other
LibreOffice backends, and independent-workbook acceptance remain separate
checks. These slices do not establish complete Office support.
## Verification
```sh
dbus-run-session -- env GOCACHE=/tmp/cthulhu-go-cache go test -race ./... -count=1
dbus-run-session -- env GOCACHE=/tmp/cthulhu-go-cache go test -tags brlapi ./... -count=1
python3 tests/check_speech.py
/usr/bin/python3 tests/native_libreoffice.py
python3 tests/native_atspi.py --table-only
GOCACHE=/tmp/cthulhu-go-cache go build -o /tmp/cthulhu-libreoffice ./cmd/cthulhu
GOCACHE=/tmp/cthulhu-go-cache go test -c -o /tmp/cthulhu-libreoffice-xorg.test ./internal/platform/xorg
CTHULHU_BINARY=/tmp/cthulhu-libreoffice CTHULHU_XRECORD_TEST=/tmp/cthulhu-libreoffice-xorg.test python3 tests/native_x11.py
xvfb-run -a env CTHULHU_NATIVE_X11=1 /tmp/cthulhu-libreoffice-xorg.test -test.run '^TestNativeDispatchBeforeRelease$' -test.v
```
The Calc harness requires i3 as well as LibreOffice, Xvfb, xdotool, xprop,
the Python AT-SPI bindings and the normal native build dependencies. It launches
installed LibreOffice with a disposable profile, document, private i3 window
manager, virtual X11 display, and private session/accessibility buses.
It exercises the production native adapter and event loop; recorded speech
and Braille are asserted for cell movement and blanks, F2 typing, Escape
cancellation, Enter commit, caret movement inside an existing numeric cell,
and switching between two sheets. Formula assertions cover a numeric result,
an empty string result, literal spaces, replacement with a number, formula
and basic Where Am I requests, and non-formula cells on a second sheet.
Selection checks cover cell-range growth/shrinkage, clearing, row/column extension,
Select All, disjoint cells made through the provider selection
API, a 2097150-cell selection, and a partial-sheet selection above GTK's count
limit. Formula commands during selection verify preservation of the active cell.
The native listener tests check the desktop formula shortcut before key release;
the Calc harness sends semantic requests through the production event loop.
Dynamic-header checks exercise both axes, automatic axis-change speech,
Where Am I, complete Braille context, heading edits, independent clearing,
sheet round trips, and both return directions between workbooks with matching
sheet names and different headings, using normal navigation without Ctrl+F6
recovery. Recorded speech, Where Am I and Braille must retain each workbook's
own headings. Find and Replace typing and
cancellation check the ordinary focused-control path and return to the sheet.
Header command bindings are checked through
the shared binding resolver in both layouts.
No active reader is replaced and no normal Office profile is changed.
These automated checks do not establish audible speech, physical input, or
physical Braille. Before submission, review the diff and test a rebuilt binary:
1. In a disposable Calc workbook, populate text, numbers, formulas and blanks.
Arrow between them and listen for the value followed by its address.
2. Visit a formula cell: listen for “has formula,” then use **Cthulhu+=**
and basic Where Am I. With Braille flash messages enabled, verify that the
formula command flashes its result and restores the cell display. Try a formula returning an empty string, a formula
containing a string literal with repeated spaces, a number, and a blank.
Replace a formula with a number and revisit it; the formula cue must clear.
With only-displayed-text enabled, automatic speech should omit the cue.
3. Press F2, edit, cancel with Escape, then edit and commit with Enter. Check
character/word navigation in an existing cell editor and continued arrows.
4. Switch sheets and switch between two workbooks. Return from menus, dialogs,
the formula bar, and another application; check logical focus recovery.
5. Check the cell value/address, spacing, editor cursor and return to cell
display on physical Braille. Text routing applies inside the editor.
6. Use Shift+Arrows to grow and shrink a range; check selected/unselected
addresses and continued active-cell/formula output. Try Ctrl+Space,
Shift+Space, their Shift+Arrow extensions, and Ctrl+A twice. Clear each
selection with an arrow. Select disjoint areas with Ctrl+click and a large
area with Ctrl+Shift+Arrows; check gaps in small selections and count summaries
for large ones. Enable Braille flash messages and verify selection messages
restore the cell display. Enable only-displayed-text and verify selection
chatter is omitted. Repeat on another sheet and in a second workbook.
7. Assign the four header commands in Key Bindings. In a workbook with headings
across row 1 and labels down column A, set both axes. Move horizontally and
vertically; listen for the changed axis's heading, value and coordinate.
Use Where Am I to hear both headings. Try a blank heading and a formula
heading, edit a heading and revisit a data cell, then clear each axis
separately. Check complete Braille context and that command flashes restore
the updated cell frame. Verify assignment/persistence of shortcuts after
reopening preferences; header choices themselves last only for this session.
Switch sheets, switch to a second workbook with the same sheet names, leave
Calc and return, and lock/unlock. Check independent choices and resumed
headings. Add, remove and rename a disposable sheet; reassign headers after
the conservative reset. Test structural row/column edits and merged headings
separately; these cases have no acceptance claim yet.
8. Repeat ordinary GTK table movement/multiselection, browser typing and table
reading, and Steam navigation. Check lock/unlock and stale/background events.
Human review and testing remain required by `CONTRIBUTING.md`. Passing this
fixture alone is not sufficient for submission or live accessibility acceptance.
The tightening pass against LibreOffice 26.2.6.3 verifies the existing cell,
editor, formula, selection and dynamic-header slices and adds workbook return
coverage. Returning to the original workbook previously reproduced stale-object
timeouts through the production adapter. Keeping LibreOffice frame activation
out of the generic descendant search makes the recorded return checks pass;
header choices remain independent in both workbooks. Foreground recovery uses
the same policy, preventing raw sheet-state chatter before the first cell event.
The virtual-display driver uses window-manager activation and waits for the
heading cell before editing it.
Full default, race and BrlAPI-tagged Go suites, the speech regression gate,
vet/build, parity-ledger validation, ordinary GTK table/multiselection checks,
and the isolated X11 reader/listener harness pass. The former shutdown test
failure is fixed by waiting for completed runtime startup before cancellation.
A process-cleanup assertion also now accepts ESRCH when a killed task disappears
during a procfs read. Neither correction skips the underlying shutdown checks.
The earlier native probe linker failure no longer reproduces with the current
listener harness.
These results establish recorded private-session behavior. Audible output,
physical input/Braille, normal-profile workbook return, tab rename/add/remove
event delivery, merged/structurally edited headings, large-sheet responsiveness
and independent real-workbook acceptance still need their own checks. Earlier
browser checks recorded an editing-title failure and one non-reproducible mode
failure; this pass does not establish current live browser acceptance.
## Calc startup focus diagnosis
A normal-profile startup under i3 can leave Calc accepting arrow keys while
omitting active-cell accessibility events. In the observed case, cell events
were followed by focus claims from the floating Form Design and Form Controls
toolbars; subsequent arrows produced formula-bar text updates but no new cell
events. A separate AT-SPI observer reproduced the missing events without
Cthulhu. The affected workbook worked with a fresh profile, and a generic
workbook reproduced the failure with a disposable copy of the normal profile.
This establishes a profile-dependent LibreOffice focus/event problem; the
precise profile setting responsible has not been isolated.
Press **Ctrl+F6** to return focus to the document, then retry the arrows.
This directly restored active-cell events in the isolated reproduction.
LibreOffice documents this shortcut in its
[Calc accessibility keyboard guide](https://help.libreoffice.org/latest/en-GB/text/scalc/guide/keyboard.html).
Automatic focus stealing would interfere with intentional toolbar use and
is not part of the reader's handling.
The native Calc test activates its initial window, establishes document focus
with F6 followed by Ctrl+F6, and uses ordinary key sequences within each document.
Explicit window-manager activation switches workbooks and focuses the dialog.
It also checks later F6/Ctrl+F6 recovery and resumed cell speech/Braille.
Its fresh-profile private i3 display does not cover this normal-profile startup
failure. The same production test also passed under private i3 with the copied
normal profile after Ctrl+F6 recovery, asserting recorded cell speech and
Braille along with editing and sheet switching. Verify startup and recovery
under the actual window manager, plus audible speech and physical Braille,
separately.