Files
cthulhu/internal/app/current_line.go

70 lines
2.9 KiB
Go

// Copyright (c) 2026 Storm Dragon
// SPDX-License-Identifier: GPL-3.0-or-later
package app
import (
"git.stormux.org/storm/cthulhu/internal/atspi"
"git.stormux.org/storm/cthulhu/internal/input"
"git.stormux.org/storm/cthulhu/internal/presentation"
"git.stormux.org/storm/cthulhu/internal/review"
)
func (handler *eventReviewHandler) current_line(command input.Command) {
if handler.focusGate.Locked() || handler.focusGate.Suspended() {
return
}
handler.syncForeground()
if handler.focusGate.Locked() || handler.focusGate.Suspended() || handler.objectReview == nil || !handler.state.current.ID().Valid() {
return
}
handler.state.pending.terminalFocus.clear()
handler.state.pending.terminalDeletion.clear()
handler.state.pending.terminalRepaint.clear()
style := review.FlatSpeak
if command == input.SpellCurrentLine {
style = review.FlatSpell
} else if command == input.PhoneticCurrentLine {
style = review.FlatPhonetic
}
browse := handler.browser != nil && handler.browser.ready && handler.browser.mode == review.BrowseMode
if err := handler.objectReview.ReadCurrentLine(browse, style); err != nil {
handler.logger.Warn("queue current line", "error", err)
}
}
func (handler *eventReviewHandler) present_current_line(result review.Result) {
if !handler.objectReview.CurrentDocumentResult(result) || result.DocumentFocus != handler.objectReview.Focus() || handler.focusGate.Locked() || handler.focusGate.Suspended() {
return
}
if result.CurrentLineBrowse && (handler.browser == nil || !handler.browser.acceptsDocumentResult(review.Result{
DocumentFocus: result.DocumentFocus, DocumentPosition: result.DocumentPosition, DocumentCaretCommand: review.CaretReadLine, Err: result.Err,
})) {
return
}
request := speechRequest{Text: "Could not read current line.", Role: "system", Cause: "current-line"}
if result.Err == nil {
// Native reports require the matching focused source and an unbroken
// capture through the application. Missing privacy data stays local.
context := result.DocumentCaretContext
nativeSource := len(context) > 0 && result.DocumentFocus.Valid() &&
result.DocumentPosition.Snapshot.ID() == result.DocumentFocus && context[0].ID() == result.DocumentFocus &&
context[len(context)-1].Role == "application"
if result.CurrentLineBrowse || nativeSource {
sources := atspi.DocumentLine{Contents: []atspi.DocumentContent{{Snapshot: result.DocumentPosition.Snapshot}},
SourceContexts: [][]atspi.Snapshot{result.DocumentCaretContext}}
request.origin = document_line_speech_origin(sources)
if result.DocumentCaretLine != nil {
if origin := document_line_speech_origin(*result.DocumentCaretLine); origin != speechOriginLocal {
request.origin = origin
}
}
}
text := result.DocumentCaretText
request.Text = presentation.FlatReviewText(text, false)
request.Role = result.DocumentPosition.Snapshot.Role
set_review_spelling(&request, text, result.FlatSpeech)
}
handler.speech.Enqueue(request)
}