Cthulhu is no longer a fork. Now it is a screen reader written in Golang.
This commit is contained in:
1 parent
a998fe67de
commit
069fc674dc
708 files changed
+112482
-532
No files matched your search
@@ -0,0 +1,106 @@
|
||||
// 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"
|
||||
"errors"
|
||||
"log/slog"
|
||||
"time"
|
||||
|
||||
"git.stormux.org/storm/cthulhu/internal/lifecycle"
|
||||
)
|
||||
|
||||
// runtimeShutdown owns the ordered teardown after the event loop returns.
|
||||
// The worker channels must close before preferences, braille, and speech stop.
|
||||
type runtimeShutdown struct {
|
||||
tentacles *tentacleRuntime
|
||||
ocr *ocrSession
|
||||
logger *slog.Logger
|
||||
cancel context.CancelFunc
|
||||
workers *runtimeWorkers
|
||||
preferences *preferencesWorker
|
||||
braille *brailleWorker
|
||||
speech *speechWorker
|
||||
focusGate *focusGate
|
||||
services *lifecycle.Runner
|
||||
}
|
||||
|
||||
func stopRuntime(runtime runtimeShutdown) error {
|
||||
runtime.cancel()
|
||||
var tentacleError error
|
||||
if runtime.tentacles != nil {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 6*time.Second)
|
||||
tentacleError = runtime.tentacles.Stop(ctx)
|
||||
cancel()
|
||||
}
|
||||
<-runtime.workers.commandDone
|
||||
if runtime.workers.internalUIDone != nil {
|
||||
<-runtime.workers.internalUIDone
|
||||
}
|
||||
<-runtime.workers.clipboardDone
|
||||
<-runtime.workers.inputDone
|
||||
<-runtime.workers.copyDone
|
||||
<-runtime.workers.autoClipboardDone
|
||||
if runtime.ocr != nil {
|
||||
ocrContext, cancelOCR := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
if err := runtime.ocr.Stop(ocrContext); err != nil {
|
||||
runtime.logger.Warn("stop OCR", "error", err)
|
||||
}
|
||||
cancelOCR()
|
||||
}
|
||||
// Join settings work before farewell and backend shutdown. The preferences
|
||||
// service also remains in the runner so failed cleanup is retried there.
|
||||
if runtime.preferences != nil {
|
||||
preferencesContext, cancelPreferences := context.WithTimeout(context.Background(), 7*time.Second)
|
||||
if err := runtime.preferences.Stop(preferencesContext); err != nil {
|
||||
runtime.logger.Warn("stop preferences", "error", err)
|
||||
}
|
||||
cancelPreferences()
|
||||
}
|
||||
brailleContext, cancelBraille := context.WithTimeout(context.Background(), shutdownTimeout)
|
||||
if err := runtime.braille.Stop(brailleContext); err != nil {
|
||||
runtime.logger.Warn("stop braille", "error", err)
|
||||
}
|
||||
cancelBraille()
|
||||
if runtime.speech != nil && !runtime.focusGate.Locked() {
|
||||
farewellContext, cancelFarewell := context.WithTimeout(context.Background(), farewellTimeout)
|
||||
runtime.speech.InterruptForUser()
|
||||
farewellError := runtime.speech.EnqueueAndWait(farewellContext, speechRequest{
|
||||
Text: random_string(shutdownMessages),
|
||||
Role: "system",
|
||||
Cause: "shutdown",
|
||||
})
|
||||
cancelFarewell()
|
||||
if farewellError != nil {
|
||||
runtime.logger.Warn("deliver shutdown message", "error", farewellError)
|
||||
} else {
|
||||
runtime.logger.Debug("shutdown message accepted")
|
||||
}
|
||||
}
|
||||
shutdownContext, cancelShutdown := context.WithTimeout(context.Background(), shutdownTimeout)
|
||||
defer cancelShutdown()
|
||||
runtime.logger.Debug("stopping runtime services")
|
||||
stopError := runtime.services.Stop(shutdownContext)
|
||||
if stopError != nil {
|
||||
runtime.logger.Warn("runtime services stopped with an error", "error", stopError)
|
||||
} else {
|
||||
runtime.logger.Debug("runtime services stopped")
|
||||
}
|
||||
return errors.Join(tentacleError, stopError)
|
||||
}
|
||||
Reference in new issue
Block a user