Observe authorized local speech for Remote sessions
This commit is contained in:
1 parent
9bc74dbb13
commit
b60cf5d5f2
12 files changed
+927
-12
No files matched your search
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user