Observe authorized local speech for Remote sessions

This commit is contained in:
ak47 committed 2026-11-04 16:53:16 -05:00
1 parent 9bc74dbb13
commit b60cf5d5f2
12 files changed
+927 -12

No files matched your search

+81
View File
@@ -0,0 +1,81 @@
// Copyright (c) 2026 Storm Dragon
// SPDX-License-Identifier: GPL-3.0-or-later
package app
import (
"context"
"git.stormux.org/storm/cthulhu/internal/atspi"
"git.stormux.org/storm/cthulhu/internal/tentacle"
)
// Unclassified output is private. Origins are core metadata, never inferred
// from network text, a Tentacle's request, or a user-visible cause string.
type speechOrigin uint8
const (
speechOriginUnknown speechOrigin = iota
speechOriginLocal
speechOriginRemote
speechOriginStatus
speechOriginSecret
)
func accessibility_speech_origin(source atspi.Snapshot) speechOrigin {
if source.Role == "password text" {
return speechOriginSecret
}
// Presentation may filter/reset context. Classify the original accessible
// ancestry before doing that, even when its labels will not be spoken.
for _, ancestor := range source.SpeechContext {
if ancestor.Role == "password text" {
return speechOriginSecret
}
}
return speechOriginLocal
}
func exportable_speech(request speechRequest) bool {
if request.origin != speechOriginLocal || request.Role == "password text" ||
request.ExcludeFromHistory || request.Confirmation || request.Uninterruptible || request.ForceSpeech {
return false
}
for _, ancestor := range request.Context {
if ancestor.Role == "password text" {
return false
}
}
return true
}
func (worker *speechWorker) observe_remote_output(manager *tentacle.Manager) {
worker.mutex.Lock()
defer worker.mutex.Unlock()
worker.remoteOutput = manager.CaptureRemoteSpeechOutput
}
// Serialize observation with user interruption. No event may be published after
// that interruption's cancel using an older delivery generation. This performs
// bounded IPC queue admission only, never pipe, relay or backend I/O.
func (worker *speechWorker) observe_speech(ctx context.Context, request speechRequest, text string, characters bool) {
if request.observation == nil || !exportable_speech(request) || ctx.Err() != nil {
return
}
worker.mutex.Lock()
defer worker.mutex.Unlock()
if !worker.running || !worker.enabled || worker.secure || worker.generation != request.deliveryGeneration ||
worker.mode != speechModeSpeech || ctx.Err() != nil {
return
}
// Failures retire only the remote stream. Local speech remains available.
_ = request.observation.PublishText(text, characters)
}
func (worker *speechWorker) observe_cancel_locked() {
if worker.remoteOutput != nil && !worker.secure {
if output := worker.remoteOutput(); output != nil {
_ = output.Cancel()
}
}
}