Files

99 lines
2.7 KiB
Go

// Copyright (c) 2026 Storm Dragon
// Authors: Storm Dragon, Angus Kola
// SPDX-License-Identifier: GPL-3.0-or-later
package app
import (
"time"
"git.stormux.org/storm/cthulhu/internal/atspi"
"git.stormux.org/storm/cthulhu/internal/config"
"git.stormux.org/storm/cthulhu/internal/notification"
"git.stormux.org/storm/cthulhu/internal/webscripts"
)
// webScriptController is owned by processEventsWithInput. Delayed presentation
// runs in that loop, preserving event order without timer callback goroutines.
type webScriptController struct {
registry webscripts.Registry
timer *time.Timer
due <-chan time.Time
speech speechEnqueuer
notifications *notification.Presenter
braille interface {
Flash(string, config.BrailleOptions) bool
}
live *liveSettings
gate *focusGate
}
func (controller *webScriptController) handle(event atspi.Event) bool {
retiring := event.Type == atspi.WindowDestroyedEvent || event.Type == atspi.DefunctStateEvent && event.Detail1 != 0
if retiring {
id := event.Source.ID()
if event.Type == atspi.WindowDestroyedEvent && event.Window.Valid() {
id = event.Window
}
controller.registry.Retire(id.BusName, id.Path)
}
messages, handled := controller.registry.Receive(event.Source.ApplicationProcess,
event.Type, event.Detail1, event.WebNotification, time.Now())
controller.present(messages)
if handled || retiring {
controller.schedule()
}
return handled
}
func (controller *webScriptController) schedule() {
deadline := controller.registry.Deadline()
controller.due = nil
if controller.timer != nil {
controller.timer.Stop()
}
if deadline.IsZero() {
return
}
if controller.timer == nil {
controller.timer = time.NewTimer(time.Until(deadline))
} else {
controller.timer.Reset(time.Until(deadline))
}
controller.due = controller.timer.C
}
func (controller *webScriptController) flush() {
if controller.gate.Locked() {
controller.reset()
return
}
controller.present(controller.registry.Flush(time.Now()))
controller.schedule()
}
func (controller *webScriptController) present(messages []string) {
if controller.gate.Locked() {
return
}
for _, message := range messages {
controller.notifications.Save(message, notification.SaveOptions{AppName: "Steam", Source: "steam"})
if controller.braille != nil {
options := config.DefaultSettings().Braille
if controller.live != nil {
options = controller.live.Load().Braille
}
controller.braille.Flash(message, options)
}
controller.speech.Interrupt()
controller.speech.Enqueue(speechRequest{
Text: message, Role: "notification", Cause: "steam-notification", BypassFocusGate: true,
})
}
}
func (controller *webScriptController) reset() {
controller.registry.Reset()
controller.schedule()
}