Files
cthulhu/internal/app/phonetic_hints.go

68 lines
2.3 KiB
Go

// Copyright (c) 2026 Storm Dragon
// Authors: Storm Dragon, Angus Kola
// SPDX-License-Identifier: GPL-3.0-or-later
package app
import (
"context"
"time"
"unicode/utf8"
)
const phoneticHintDelay = 300 * time.Millisecond
// The speech owner retains the hint with its character request. Waiting here
// keeps output ordered without blocking input or accessibility event handling;
// the normal input, settings, secure-session and shutdown barriers cancel it.
func (worker *speechWorker) deliver_phonetic_hint(ctx context.Context, request speechRequest) (bool, error) {
if !request.PhoneticHint || !request.Character || request.Role == "password text" ||
worker.reading == nil || !worker.reading().PhoneticCharacterHints || worker.inputActivity == nil {
return false, nil
}
activity := worker.inputActivity()
letter, size := utf8.DecodeRuneInString(request.Text)
if !activity.PlainCharacterArrow || size != len(request.Text) || phoneticLetter(letter) == "" {
return false, nil
}
request.PhoneticHint = false
if err := worker.deliver(ctx, request); err != nil {
return true, err
}
hintContext, cancelHint := context.WithCancel(ctx)
defer cancelHint()
worker.mutex.Lock()
if worker.generation != request.deliveryGeneration {
worker.mutex.Unlock()
return true, context.Canceled
}
worker.hintCancel = cancelHint
worker.mutex.Unlock()
defer func() {
worker.mutex.Lock()
worker.hintCancel = nil
worker.mutex.Unlock()
}()
timer := time.NewTimer(phoneticHintDelay)
defer timer.Stop()
select {
case <-hintContext.Done():
return true, hintContext.Err()
case <-timer.C:
}
worker.mutex.Lock()
interrupted := worker.generation != request.deliveryGeneration
worker.mutex.Unlock()
if interrupted || worker.inputActivity().InputSequence != activity.InputSequence || !worker.reading().PhoneticCharacterHints {
return true, nil
}
hint := speechRequest{
// The character already presented its capital cues. This hint only
// clarifies its identity, using the normal voice even in links or Wine.
Text: phoneticLetter(letter), VoiceType: "default", Role: request.Role, Cause: "phonetic-character-hint", Append: true,
Window: request.Window, ExcludeFromHistory: request.ExcludeFromHistory,
deliveryGeneration: request.deliveryGeneration,
}
return true, worker.deliver(hintContext, hint)
}