// 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" type applicationEventResult struct { event atspi.Event adoptFocus bool suppress bool resetPresentation bool } // prepareApplicationEvent runs after security/foreground checks and before // generic focus and text filters. Application policy receives copied semantics; // the reader retains ownership, review, cancellation, and output services. // Application-specific state stays on the event loop with an explicit focus // lifetime, rather than hidden in native callbacks. func (handler *accessibilityEventHandler) prepareApplicationEvent(event atspi.Event) (atspi.Event, bool) { result := applicationEventResult{event: event} if atspi.IsLibreOfficeProcess(event.Source.ApplicationProcess) { result = prepareLibreOfficeEvent(event, handler.state.current, handler.state.owner) } if !result.suppress || event.Type == atspi.SelectionChangedEvent { handler.handleLibreOfficeSelection(result.event) } if result.adoptFocus { if handler.focusGate.Suspended() { return event, false } state := handler.state if !state.owner.follow(result.event) { return event, true } state.pending.caret.clear() state.focusedID = result.event.Source.ID() state.current = result.event.Source state.managedContainerID, state.managedDescendantID = atspi.ObjectID{}, atspi.ObjectID{} if result.event.Source.Role != "table" && result.event.Source.Role != "table cell" { // A late-created editor has no ordinary focus event to reset // row history. Returning to its old cell is a fresh focus. state.lastTable, state.lastTableCell = atspi.ObjectID{}, atspi.ObjectID{} } state.selection.reset(result.event.Source) if handler.objectReview != nil { handler.objectReview.SyncFocus(result.event.Source.ID()) } } if result.resetPresentation { handler.state.pending.caret.clear() handler.state.lastSpokenPath, handler.state.lastSpokenText, handler.state.lastSound = "", "", "" } if result.suppress { handler.logger.Debug("skipped application-specific accessibility event", "type", event.Type, "application_process", event.Source.ApplicationProcess, "role", event.Source.Role) } return result.event, result.suppress }