97 lines
4.0 KiB
Go
97 lines
4.0 KiB
Go
// Cthulhu screen reader.
|
|
// Copyright (c) 2026 Storm Dragon
|
|
// Authors: Storm Dragon, Angus Kola
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify it
|
|
// under the terms of the GNU General Public License as published by the Free
|
|
// Software Foundation, either version 3 of the License, or (at your option)
|
|
// any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful, but WITHOUT
|
|
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
// more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License along with
|
|
// this program. If not, see <https://www.gnu.org/licenses/gpl-3.0.html>.
|
|
|
|
package app
|
|
|
|
import (
|
|
"context"
|
|
|
|
"git.stormux.org/storm/cthulhu/internal/config"
|
|
"git.stormux.org/storm/cthulhu/internal/input"
|
|
)
|
|
|
|
// runtimeWorkers owns the event-loop companion workers. Only the event loop
|
|
// consumes reviewRequests; command and clipboard workers send to bounded queues.
|
|
// Each done channel closes after its worker exits. commandError is read after
|
|
// commandDone closes, so no additional synchronization is needed.
|
|
type runtimeWorkers struct {
|
|
reviewRequests <-chan input.Command
|
|
attributeRequests <-chan struct{}
|
|
commandDone <-chan struct{}
|
|
clipboardDone <-chan struct{}
|
|
inputDone <-chan struct{}
|
|
copyDone <-chan struct{}
|
|
autoClipboardDone <-chan struct{}
|
|
commandError error
|
|
internalUIDone <-chan struct{}
|
|
internalUI *internalUI
|
|
}
|
|
|
|
// startRuntimeWorkers preserves startup order: clipboard, command, input, copy.
|
|
// The caller cancels ctx and joins these workers before stopping output services.
|
|
func startRuntimeWorkers(ctx context.Context, command commandRuntime, inputListener InputListener, settings config.Settings, copyChanges <-chan struct{}, braille *brailleWorker) *runtimeWorkers {
|
|
speech := gatedSpeech{speech: command.speechOutput, gate: command.focusGate}
|
|
learn := &learnModePresenter{tentacles: command.tentacles, ctx: ctx, logger: command.logger, speech: speech, braille: braille, live: command.live}
|
|
if owner, ok := command.commands.(interface{ BrowseNavigationEnabled() bool }); ok {
|
|
learn.browse = owner
|
|
}
|
|
command.learn = learn
|
|
clipboardRequests := make(chan struct{}, 1)
|
|
clipboardDone := runClipboardWorker(ctx, clipboardRequests, speech, readClipboardMessage)
|
|
command.clipboardRequests = clipboardRequests
|
|
reviewRequests := make(chan input.Command, 1)
|
|
command.reviewRequests = reviewRequests
|
|
attributeRequests := make(chan struct{}, 1)
|
|
command.attributeRequests = attributeRequests
|
|
commandDone := make(chan struct{})
|
|
workers := &runtimeWorkers{
|
|
reviewRequests: reviewRequests,
|
|
attributeRequests: attributeRequests,
|
|
commandDone: commandDone,
|
|
clipboardDone: clipboardDone,
|
|
}
|
|
if keys, ok := command.commands.(internalUIKeys); ok {
|
|
ui := &internalUI{notices: make(chan internalUINotice, 4), keys: keys, actions: command.actionSource, interest: command.objectReview, speech: speech, braille: braille, gate: command.focusGate, events: make(chan input.CommandEvent, 32), requests: make(chan internalUIRequest, 1), done: make(chan struct{})}
|
|
command.internalUI = ui
|
|
if command.tentacles != nil {
|
|
command.tentacles.forms = ui
|
|
command.tentacles.focusedItem = focusedTentacleItem(command.actionSource, ui)
|
|
}
|
|
workers.internalUI = ui
|
|
workers.internalUIDone = ui.done
|
|
go ui.run(ctx)
|
|
}
|
|
go func() {
|
|
defer close(commandDone)
|
|
workers.commandError = command.run(ctx)
|
|
}()
|
|
|
|
inputDone := make(chan struct{})
|
|
if inputListener != nil {
|
|
go func() {
|
|
defer close(inputDone)
|
|
processInputEvents(ctx, command.logger, speech, inputListener, inputEchoSettingsFromConfig(settings), command.live, command.commands, command.ocr)
|
|
}()
|
|
} else {
|
|
close(inputDone)
|
|
}
|
|
workers.inputDone = inputDone
|
|
workers.copyDone = runCopyFeedback(ctx, copyChanges, inputListener, speech, command.live, command.ocr)
|
|
workers.autoClipboardDone = run_automatic_clipboard(ctx, command.logger, command.live, command.focusGate, speech)
|
|
return workers
|
|
}
|