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,153 @@
|
||||
// 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 (
|
||||
"slices"
|
||||
"strings"
|
||||
"unicode/utf8"
|
||||
|
||||
"git.stormux.org/storm/cthulhu/internal/remote"
|
||||
"git.stormux.org/storm/cthulhu/internal/wine"
|
||||
"github.com/godbus/dbus/v5"
|
||||
)
|
||||
|
||||
type remoteSpeech struct {
|
||||
speech speechEnqueuer
|
||||
braille interface {
|
||||
EnqueueMessage(string) bool
|
||||
}
|
||||
wine *wineEventPresenter
|
||||
wineReview *wine.ReviewRegistry
|
||||
showPreferences func()
|
||||
quit func()
|
||||
runtimeSnapshot func() string
|
||||
live *liveSettings
|
||||
preferences *preferencesWorker
|
||||
}
|
||||
|
||||
func (presenter remoteSpeech) RegisterProvider(sender string, path dbus.ObjectPath, version uint32) bool {
|
||||
return presenter.wineReview != nil && presenter.wineReview.RegisterProvider(sender, path, version)
|
||||
}
|
||||
func (presenter remoteSpeech) PresentFocus(sender string, notice wine.ReviewFocusNotice) bool {
|
||||
return presenter.wineReview != nil && presenter.wineReview.PresentFocus(sender, notice)
|
||||
}
|
||||
func (presenter remoteSpeech) BindWineReviewConnection(connection *dbus.Conn) {
|
||||
if presenter.wineReview != nil {
|
||||
presenter.wineReview.BindWineReviewConnection(connection)
|
||||
}
|
||||
}
|
||||
|
||||
func (presenter remoteSpeech) ShowPreferences() bool {
|
||||
if presenter.showPreferences == nil {
|
||||
return false
|
||||
}
|
||||
presenter.showPreferences()
|
||||
return true
|
||||
}
|
||||
|
||||
func (presenter remoteSpeech) Quit() bool {
|
||||
if presenter.quit == nil {
|
||||
return false
|
||||
}
|
||||
presenter.quit()
|
||||
return true
|
||||
}
|
||||
|
||||
func (presenter remoteSpeech) RuntimeStateSnapshot() string {
|
||||
if presenter.runtimeSnapshot == nil {
|
||||
return ""
|
||||
}
|
||||
return presenter.runtimeSnapshot()
|
||||
}
|
||||
|
||||
func (presenter remoteSpeech) PresentAccessibleEvent(event remote.AccessibleEvent) bool {
|
||||
return presenter.wine != nil && presenter.wine.present_event(event, presenter.presentWineEditor)
|
||||
}
|
||||
|
||||
func (presenter remoteSpeech) PresentMessage(message string) bool {
|
||||
return presenter.presentMessage(message, "remote-present-message")
|
||||
}
|
||||
|
||||
func (presenter remoteSpeech) presentMessage(message, cause string) bool {
|
||||
return presenter.presentRequest(speechRequest{
|
||||
Text: message, Role: "system", Cause: cause, Append: true,
|
||||
})
|
||||
}
|
||||
|
||||
func (presenter remoteSpeech) presentWineEditor(event remote.AccessibleEvent) bool {
|
||||
// These action kinds are part of the helper editor protocol. Keep
|
||||
// character identity for navigation and Backspace as well as typing.
|
||||
kinds := strings.Fields(event.States)
|
||||
inserted := slices.Contains(kinds, "kind=8")
|
||||
character := inserted || utf8.RuneCountInString(event.Name) == 1
|
||||
custom := false
|
||||
if character && presenter.live != nil && (inserted || slices.Contains(kinds, "kind=7")) {
|
||||
custom = customEcho(presenter.live.Load(), "character")
|
||||
}
|
||||
return presenter.presentRequest(speechRequest{
|
||||
Text: event.Name, Role: "system", Cause: "wine-accessible-event", Append: true,
|
||||
Character: character, CustomEcho: custom,
|
||||
})
|
||||
}
|
||||
|
||||
func (presenter remoteSpeech) presentRequest(request speechRequest) bool {
|
||||
if request.Text == "" {
|
||||
return false
|
||||
}
|
||||
accepted := false
|
||||
if presenter.speech != nil {
|
||||
presenter.speech.Enqueue(request)
|
||||
accepted = true
|
||||
}
|
||||
if presenter.braille != nil && presenter.braille.EnqueueMessage(request.Text) {
|
||||
accepted = true
|
||||
}
|
||||
return accepted
|
||||
}
|
||||
|
||||
func (presenter remoteSpeech) SpeakText(text string, interrupt bool) bool {
|
||||
if presenter.speech == nil || text == "" {
|
||||
return false
|
||||
}
|
||||
if interrupt {
|
||||
presenter.speech.Interrupt()
|
||||
}
|
||||
presenter.speech.Enqueue(speechRequest{
|
||||
Text: text,
|
||||
Role: "system",
|
||||
Cause: "remote-speak-text",
|
||||
Append: true,
|
||||
})
|
||||
return true
|
||||
}
|
||||
|
||||
func (presenter remoteSpeech) BrailleMessage(text string) bool {
|
||||
if presenter.braille == nil || text == "" {
|
||||
return false
|
||||
}
|
||||
return presenter.braille.EnqueueMessage(text)
|
||||
}
|
||||
|
||||
func (presenter remoteSpeech) CancelSpeech() bool {
|
||||
if presenter.speech == nil {
|
||||
return false
|
||||
}
|
||||
presenter.speech.Interrupt()
|
||||
return true
|
||||
}
|
||||
Reference in new issue
Block a user