// 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" "errors" "time" "git.stormux.org/storm/cthulhu/internal/atspi" ) // ReadDocumentLine queues a caret-line capture and assembly request. func (worker *Worker) ReadDocumentLine(focus atspi.ObjectID, offset int) (uint64, error) { if !focus.Valid() { return 0, ErrInvalidDirection } sequence := worker.lineSequence.Add(1) if sequence == 0 { sequence = worker.lineSequence.Add(1) } select { case worker.requests <- request{documentEpoch: worker.documentEpoch.Load(), line: true, lineRequest: sequence, lineFocus: focus, lineOffset: offset, queuedAt: time.Now()}: return sequence, nil default: return 0, ErrRequestQueueFull } } func (worker *Worker) readDocumentLine(ctx context.Context, tree *atspi.AccessibleTree, request uint64, focus atspi.ObjectID, offset int) (Result, *atspi.AccessibleTree) { result := Result{DocumentLine: true, DocumentLineRequest: request, DocumentLineFocus: focus} if !focus.Valid() { result.Err = ErrNoCurrent return result, tree } if tree != nil { if reference, err := tree.Reference(focus); err == nil { if line, err := tree.DocumentLineAtOffset(ctx, reference, offset); err == nil { result.DocumentLineData = line result.Moved = true return result, tree } } } captureContext, cancel := context.WithTimeout(ctx, worker.captureTimeout) captureStarted := time.Now() tree, err := worker.capturer.CaptureDocumentText(captureContext, focus) result.CaptureDuration = time.Since(captureStarted) cancel() if err != nil { result.Err = err return result, tree } if tree == nil { result.Err = errors.New("capture document text tree: nil tree") return result, nil } reference, err := tree.Reference(focus) if err != nil { result.Err = err return result, tree } line, err := tree.DocumentLineAtOffset(ctx, reference, offset) if err != nil { result.Err = err return result, tree } result.DocumentLineData = line result.Moved = true return result, tree }