Reset master to testing content
This commit is contained in:
1 parent
0f2587b719
commit
42afa2a56c
1483 files changed
+145308
-938069
No files matched your search
@@ -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.
|
||||
Reference in new issue
Block a user