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,125 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.stormux.org/storm/cthulhu/internal/braille"
|
||||
"git.stormux.org/storm/cthulhu/internal/review"
|
||||
)
|
||||
|
||||
// brailleWorker keeps all BrlAPI calls in a disposable child process. Only the
|
||||
// newest frame is retained; focus and input callbacks never wait for the display.
|
||||
// mu owns frames, flash generations, routing cancellation and session flags.
|
||||
// The session joins its child and pipe workers before reconnecting; routing
|
||||
// events retain their own bounded FIFO and validate the current frame identity.
|
||||
type brailleWorker struct {
|
||||
internalUIFrame *braille.Frame
|
||||
router brailleRouter
|
||||
document *review.Worker
|
||||
ocr *ocrSession
|
||||
sequence uint64
|
||||
routeCancel context.CancelFunc
|
||||
secure bool
|
||||
stopping bool
|
||||
connected bool
|
||||
logger *slog.Logger
|
||||
path string
|
||||
mu sync.Mutex
|
||||
frame braille.Frame
|
||||
baseFrame braille.Frame
|
||||
flashTimer *time.Timer
|
||||
flashGeneration uint64
|
||||
flashContext context.Context // A tentacle flash expires with its command/session.
|
||||
protectedFlash bool // Focus updates retain baseFrame until expiry or user input.
|
||||
contracted bool
|
||||
wordWrap bool
|
||||
table string
|
||||
wake chan struct{}
|
||||
cancel context.CancelFunc
|
||||
done chan struct{}
|
||||
}
|
||||
|
||||
func newBrailleWorker(logger *slog.Logger) (*brailleWorker, error) {
|
||||
executable, err := os.Executable()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &brailleWorker{logger: logger, path: filepath.Join(filepath.Dir(executable), "cthulhu-braille"),
|
||||
frame: braille.Frame{Cursor: -1}, baseFrame: braille.Frame{Cursor: -1}, wake: make(chan struct{}, 1)}, nil
|
||||
}
|
||||
|
||||
func (worker *brailleWorker) Start() error {
|
||||
worker.mu.Lock()
|
||||
defer worker.mu.Unlock()
|
||||
if worker.stopping {
|
||||
return errors.New("braille helper shutdown is still pending")
|
||||
}
|
||||
if worker.cancel != nil {
|
||||
return nil
|
||||
}
|
||||
info, err := os.Stat(worker.path)
|
||||
if err != nil || info.IsDir() || info.Mode()&0111 == 0 {
|
||||
return fmt.Errorf("braille helper %q is unavailable; build cthulhu-braille with -tags brlapi", worker.path)
|
||||
}
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
worker.cancel = cancel
|
||||
worker.done = make(chan struct{})
|
||||
go worker.run(ctx, worker.done)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (worker *brailleWorker) Stop(ctx context.Context) error {
|
||||
worker.mu.Lock()
|
||||
cancel, done := worker.cancel, worker.done
|
||||
if cancel == nil {
|
||||
worker.mu.Unlock()
|
||||
return nil
|
||||
}
|
||||
worker.stopping = true
|
||||
if worker.routeCancel != nil {
|
||||
worker.routeCancel()
|
||||
}
|
||||
worker.mu.Unlock()
|
||||
cancel()
|
||||
select {
|
||||
case <-done:
|
||||
worker.mu.Lock()
|
||||
if worker.done == done {
|
||||
worker.cancel, worker.done = nil, nil
|
||||
worker.stopping = false
|
||||
worker.connected = false
|
||||
worker.protectedFlash = false
|
||||
worker.flashContext = nil
|
||||
worker.frame = braille.Frame{Cursor: -1}
|
||||
worker.baseFrame = braille.Frame{Cursor: -1}
|
||||
if worker.flashTimer != nil {
|
||||
worker.flashTimer.Stop()
|
||||
worker.flashTimer = nil
|
||||
}
|
||||
}
|
||||
worker.mu.Unlock()
|
||||
return nil
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
func (worker *brailleWorker) Configure(ctx context.Context, enabled bool) error {
|
||||
if enabled {
|
||||
return worker.Start()
|
||||
}
|
||||
return worker.Stop(ctx)
|
||||
}
|
||||
|
||||
func (worker *brailleWorker) Running() bool {
|
||||
worker.mu.Lock()
|
||||
defer worker.mu.Unlock()
|
||||
return worker.cancel != nil && !worker.stopping && worker.connected
|
||||
}
|
||||
Reference in new issue
Block a user