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,123 @@
|
||||
// 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"
|
||||
"log/slog"
|
||||
|
||||
"git.stormux.org/storm/cthulhu/internal/input"
|
||||
)
|
||||
|
||||
func processInputEvents(
|
||||
ctx context.Context,
|
||||
logger *slog.Logger,
|
||||
speech speechEnqueuer,
|
||||
listener InputListener,
|
||||
echoSettings input.EchoSettings,
|
||||
live *liveSettings,
|
||||
commands commandListener,
|
||||
ocrSessions ...*ocrSession,
|
||||
) {
|
||||
echo := newKeyEchoPolicy(echoSettings)
|
||||
var lastOverflow uint64
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case event, ok := <-listener.Events():
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if guarded, ok := speech.(gatedSpeech); ok && guarded.gate.Locked() {
|
||||
echo.Reset()
|
||||
continue
|
||||
}
|
||||
if modal, ok := commands.(interface{ InternalUIEchoActive() bool }); ok && modal.InternalUIEchoActive() {
|
||||
echo.Reset()
|
||||
continue
|
||||
}
|
||||
if learner, ok := commands.(interface{ LearnModeEchoActive() bool }); ok && learner.LearnModeEchoActive() {
|
||||
echo.Reset()
|
||||
continue
|
||||
}
|
||||
if overflow := listener.Overflow(); overflow > lastOverflow {
|
||||
logger.Warn("X11 event queue overflow", "dropped_events", overflow-lastOverflow)
|
||||
lastOverflow = overflow
|
||||
echo.Reset()
|
||||
}
|
||||
if len(ocrSessions) > 0 {
|
||||
ocrSessions[0].Input(event)
|
||||
}
|
||||
if input.ShouldInterruptSpeech(event) {
|
||||
if interrupt, ok := speech.(interface{ InterruptForInput(input.Event) bool }); ok {
|
||||
if !interrupt.InterruptForInput(event) {
|
||||
continue
|
||||
}
|
||||
} else if interrupt, ok := speech.(interface{ InterruptForUser() }); ok {
|
||||
interrupt.InterruptForUser()
|
||||
} else {
|
||||
speech.Interrupt()
|
||||
}
|
||||
logger.Debug("queued speech interrupted by key press", "input_timestamp", event.Timestamp,
|
||||
"input_keycode", event.Keycode, "input_repeat", event.Repeat)
|
||||
}
|
||||
custom := false
|
||||
if live != nil {
|
||||
settings := live.Load()
|
||||
echo.settings = inputEchoSettingsFromConfig(settings)
|
||||
custom = customEcho(settings, "key")
|
||||
}
|
||||
if text := echo.Text(event); text != "" {
|
||||
speech.Enqueue(speechRequest{Text: text, Role: "key", Cause: "key-echo", CustomEcho: custom})
|
||||
logger.Debug("queued non-printable key echo", "kind", event.Kind)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type keyEchoPolicy struct {
|
||||
settings input.EchoSettings
|
||||
cthulhuModifiers map[string]struct{}
|
||||
heldCthulhuKeys [256]bool
|
||||
}
|
||||
|
||||
func newKeyEchoPolicy(settings input.EchoSettings) *keyEchoPolicy {
|
||||
modifiers := make(map[string]struct{}, len(settings.CthulhuModifierKeys))
|
||||
for _, name := range settings.CthulhuModifierKeys {
|
||||
modifiers[name] = struct{}{}
|
||||
}
|
||||
return &keyEchoPolicy{settings: settings, cthulhuModifiers: modifiers}
|
||||
}
|
||||
|
||||
func (policy *keyEchoPolicy) Text(event input.Event) string {
|
||||
if _, isCthulhuModifier := policy.cthulhuModifiers[event.Name]; isCthulhuModifier {
|
||||
policy.heldCthulhuKeys[event.Keycode] = event.Pressed
|
||||
return ""
|
||||
}
|
||||
for _, held := range policy.heldCthulhuKeys {
|
||||
if held {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
return input.EchoText(event, policy.settings)
|
||||
}
|
||||
|
||||
func (policy *keyEchoPolicy) Reset() {
|
||||
clear(policy.heldCthulhuKeys[:])
|
||||
}
|
||||
Reference in new issue
Block a user