// Cthulhu screen reader. // Copyright (c) 2026 Storm Dragon // Authors: Storm Dragon, Angus Kola // // This program is free software: you can redistribute it and/or modify it // under the terms of the GNU General Public License as published by the Free // Software Foundation, either version 3 of the License, or (at your option) // any later version. // // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for // more details. // // You should have received a copy of the GNU General Public License along with // this program. If not, see . package review import ( "context" "time" "git.stormux.org/storm/cthulhu/internal/atspi" ) // TreeCapturer performs native AT-SPI capture. Worker is the only runtime // caller so capture latency cannot block the event or command loops. type TreeCapturer interface { CaptureDesktop(context.Context) (*atspi.AccessibleTree, error) CaptureReviewWindow(context.Context, atspi.ObjectID) (*atspi.AccessibleTree, error) CaptureDocument(context.Context, atspi.ObjectID) (*atspi.AccessibleTree, error) CaptureDocumentText(context.Context, atspi.ObjectID) (*atspi.AccessibleTree, error) } // Fresh providers cannot reuse text from a previous review command. type FreshReviewWindowCapturer interface{ FreshReviewWindow(atspi.ObjectID) bool } // Resolve optional native operations without hiding the adapter's dynamic // capabilities behind a capture router. Reserved provider IDs return nil. type NativeCapturerResolver interface { NativeCapturerFor(atspi.ObjectID) TreeCapturer } func (worker *Worker) nativeCapturerFor(id atspi.ObjectID) TreeCapturer { if resolver, ok := worker.capturer.(NativeCapturerResolver); ok { return resolver.NativeCapturerFor(id) } return worker.capturer } // Result describes one requested navigation step. A boundary has Moved false // and no error; Position remains the current object in that case. type Result struct { ReadAllContext context.Context ReadAllLines []atspi.DocumentLine ReadAllProgress func(int) ReadAllPosition bool DocumentEpoch uint64 Elapsed time.Duration CaptureDuration time.Duration CaptureReason error DocumentTarget DocumentTarget DocumentCaretCommand DocumentCaretCommand DocumentActivate bool DocumentFragment bool DocumentCaretText string DocumentCaretRawText string DocumentCaretLine *atspi.DocumentLine DocumentTablePrefix string DocumentTableSuffix string DocumentTableRowHeader, DocumentTableColumnHeader string DocumentCaretStart int DocumentCaretEnd int DocumentCaretContext []atspi.Snapshot DocumentBrailleLine *atspi.DocumentCaretText DocumentBrailleErr error DocumentSoundErr error DocumentBackwards bool DocumentLevel int DocumentWrapped bool DocumentNativeCaret bool DocumentBrailleRoute bool DocumentPosition DocumentPosition DocumentFocus atspi.ObjectID Direction Direction Position Position Moved bool Err error CaretErr error FlatUnit FlatUnit FlatSpeech FlatSpeech FlatStep int FlatPosition FlatPosition FlatFocus atspi.ObjectID DocumentLine bool DocumentLineRequest uint64 DocumentLineData atspi.DocumentLine DocumentLineFocus atspi.ObjectID DocumentLineText string } // request uses the existing ordered dispatch contract: native caret, Say All // progress, Say All capture, document line, structural target, caret command, // activation, fragment, flat unit, then object direction. Flags are deliberately // retained; epoch and timeout retirement depend on their distinct combinations. type request struct { brailleRoute *documentBrailleRequest interest *interestRequest nativeCaret bool nativeCaretFocus atspi.ObjectID nativeCaretOffset int readAll context.Context readPosition *DocumentPosition readTree *atspi.AccessibleTree readFocus atspi.ObjectID documentEpoch uint64 queuedAt time.Time target DocumentTarget caretCommand DocumentCaretCommand activate bool fragment string backwards bool level int direction Direction unit FlatUnit flatSpeech FlatSpeech step int line bool lineRequest uint64 lineFocus atspi.ObjectID lineOffset int }