Reset master to testing content
This commit is contained in:
1 parent
0f2587b719
commit
42afa2a56c
1483 files changed
+145308
-938069
No files matched your search
@@ -0,0 +1,85 @@
|
||||
// Copyright (c) 2026 Storm Dragon
|
||||
// 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/input"
|
||||
"git.stormux.org/storm/cthulhu/internal/presentation"
|
||||
"git.stormux.org/storm/cthulhu/internal/sound"
|
||||
)
|
||||
|
||||
const radioFocusSoundDelay = 250 * time.Millisecond
|
||||
|
||||
// GTK can focus an unchecked radio before selecting it. Retain one state cue
|
||||
// in the event loop while speech and Braille update immediately. A checked
|
||||
// event replaces it; focus/input changes, lock and shutdown retire it.
|
||||
type pendingRadioSound struct {
|
||||
pendingPresentation
|
||||
inputSequence uint64
|
||||
}
|
||||
|
||||
func (presenter *eventPresenter) coalesce_radio_sound(event atspi.Event, cues []sound.Cue) []sound.Cue {
|
||||
pending := &presenter.state.pending.radioSound
|
||||
if isFocusGainEvent(event) && event.Source.ID() != pending.event.Source.ID() {
|
||||
pending.clear()
|
||||
}
|
||||
if event.Source.Role != "radio button" {
|
||||
return cues
|
||||
}
|
||||
if event.Type == "object:state-changed:checked" || event.Source.Checked {
|
||||
pending.clear()
|
||||
return cues
|
||||
}
|
||||
if !isFocusGainEvent(event) {
|
||||
return cues
|
||||
}
|
||||
for index, cue := range cues {
|
||||
if cue.Name != "radiobutton_unchecked" {
|
||||
continue
|
||||
}
|
||||
sequence := recentInput(presenter.inputSource).InputSequence
|
||||
if pending.event.Source.ID() != event.Source.ID() || pending.inputSequence != sequence || pending.ready == nil {
|
||||
pending.schedule(event, input.NoIntent, radioFocusSoundDelay)
|
||||
pending.inputSequence = sequence
|
||||
presenter.logger.Debug("deferred unchecked radio focus sound")
|
||||
}
|
||||
retained := append([]sound.Cue(nil), cues[:index]...)
|
||||
return append(retained, cues[index+1:]...)
|
||||
}
|
||||
return cues
|
||||
}
|
||||
|
||||
func (presenter *eventPresenter) flush_radio_sound(gate *focusGate) {
|
||||
pending := &presenter.state.pending.radioSound
|
||||
sequence := pending.inputSequence
|
||||
event, _ := pending.take()
|
||||
state := presenter.state
|
||||
if event.Type == "" || gate.Locked() || gate.Suspended() ||
|
||||
state.focusedID != event.Source.ID() || state.current.ID() != event.Source.ID() ||
|
||||
state.current.Defunct || state.owner.window != eventWindow(event) ||
|
||||
recentInput(presenter.inputSource).InputSequence != sequence {
|
||||
return
|
||||
}
|
||||
options := state.presentationOptions
|
||||
if presenter.live != nil {
|
||||
options = presentationSettingsFromConfig(presenter.live.Load(), true)
|
||||
options.SoundCueAvailable = state.presentationOptions.SoundCueAvailable
|
||||
}
|
||||
event.Type = "object:state-changed:checked"
|
||||
event.Source = state.current
|
||||
// Admission already verified the cue. Let the output queue provide its
|
||||
// normal speech fallback if the asset disappears before delayed playback.
|
||||
options.SoundCueAvailable = nil
|
||||
cues := presentation.EventSoundCues(event, options)
|
||||
if len(cues) == 0 {
|
||||
return
|
||||
}
|
||||
fallback := presentationFallbackSpeech(event, options)
|
||||
presenter.speech.Enqueue(speechRequest{Sound: cues, FallbackText: fallback.Text, FallbackChunks: fallback.Chunks,
|
||||
Role: event.Source.Role, Cause: "radio-focus-state"})
|
||||
presenter.logger.Debug("queued settled radio focus sound", "cue", cues[0].Name)
|
||||
}
|
||||
Reference in new issue
Block a user