68 lines
2.3 KiB
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)
|
|
}
|