// Cthulhu screen reader. // Copyright (c) 2026 Storm Dragon // Authors: Storm Dragon, Angus Kola // SPDX-License-Identifier: GPL-3.0-or-later package app import ( "git.stormux.org/storm/cthulhu/internal/atspi" "git.stormux.org/storm/cthulhu/internal/presentation" ) type promptPresentation struct { state *eventLoopState event atspi.Event activation bool focus bool } func newPromptPresentation(state *eventLoopState, event atspi.Event) promptPresentation { control := event.Source.Role == "button" || event.Source.Role == "tree table" || event.Source.Role == "table cell" return promptPresentation{ state: state, event: event, activation: event.Type == atspi.WindowActivatedEvent && control && event.WindowMessage != "", focus: isFocusGainEvent(event) && control && state.pendingPromptWindow.Valid() && event.Window == state.pendingPromptWindow, } } func (prompt promptPresentation) focusSpeech(spoken, fallback presentation.Speech) (presentation.Speech, presentation.Speech) { if prompt.focus && !prompt.state.pendingPromptTextAnnounced && prompt.event.WindowMessage != "" { spoken = spoken.Prepend(prompt.event.WindowMessage, ", ") fallback = spoken } return spoken, fallback } func (prompt promptPresentation) appendFocus() bool { return prompt.focus && (prompt.state.pendingPromptTextAnnounced || prompt.event.WindowMessage != "") } func (prompt promptPresentation) finishFocus() { if isFocusGainEvent(prompt.event) && (prompt.event.Source.Role == "button" || prompt.event.Source.Role == "tree table" || prompt.event.Source.Role == "table cell") { prompt.state.pendingPromptWindow = atspi.ObjectID{} prompt.state.pendingPromptTextAnnounced = false } } func (prompt promptPresentation) rememberWindow() { if (prompt.event.Type == atspi.WindowCreateEvent || prompt.event.Type == atspi.WindowActivatedEvent) && (prompt.event.Source.Role == "alert" || prompt.event.Source.Role == "frame" || prompt.activation) && prompt.event.Window.Valid() { prompt.state.pendingPromptWindow = prompt.event.Window prompt.state.pendingPromptTextAnnounced = prompt.event.Source.Text != "" || prompt.activation } }