Reset master to testing content
This commit is contained in:
1 parent
0f2587b719
commit
42afa2a56c
1483 files changed
+145308
-938069
No files matched your search
@@ -0,0 +1,263 @@
|
||||
// Copyright (c) 2026 Storm Dragon
|
||||
// Authors: Storm Dragon, Angus Kola
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
|
||||
package app
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"git.stormux.org/storm/cthulhu/internal/config"
|
||||
"git.stormux.org/storm/cthulhu/internal/input"
|
||||
"git.stormux.org/storm/cthulhu/internal/preferences"
|
||||
"git.stormux.org/storm/cthulhu/internal/tentaclewire"
|
||||
)
|
||||
|
||||
func (worker *preferencesWorker) response(message string, accepted bool) preferences.Response {
|
||||
settings := worker.settings
|
||||
if worker.editingActive {
|
||||
settings = worker.editing
|
||||
}
|
||||
store, err := config.NewStore(settings.Path)
|
||||
if err == nil {
|
||||
_, err = store.Load()
|
||||
}
|
||||
return preferences.Response{Values: preferences.FromSettings(settings), ExtensionPages: worker.extension_pages(), CanSave: err == nil,
|
||||
Message: message, Accepted: accepted}
|
||||
}
|
||||
|
||||
func (worker *preferencesWorker) apply(ctx context.Context, request preferences.Request) preferences.Response {
|
||||
base := worker.settings
|
||||
if worker.editingActive {
|
||||
base = worker.editing
|
||||
}
|
||||
next, err := request.Values.Merge(base)
|
||||
if err != nil {
|
||||
return worker.response("Settings were not applied: "+err.Error(), false)
|
||||
}
|
||||
transaction, err := worker.prepare_extension_pages(ctx, request.ExtensionPages)
|
||||
if err != nil {
|
||||
return worker.response("Tentacle settings were not applied: "+err.Error(), false)
|
||||
}
|
||||
worker.stagedReader = &transaction
|
||||
defer func() { worker.stagedReader = nil }()
|
||||
response := worker.applyNext(ctx, next, request.Save, request.Close)
|
||||
if response.Accepted && request.Save && len(request.ExtensionPages) > 0 {
|
||||
if err := worker.save_extension_pages(); err != nil {
|
||||
response.Message = "Applied for this session, but tentacle preferences could not be saved: " + err.Error()
|
||||
response.Close = false
|
||||
}
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
func (worker *preferencesWorker) applyNext(ctx context.Context, next config.Settings, save, close bool) preferences.Response {
|
||||
scopeApp := worker.activeApp
|
||||
if worker.editingActive {
|
||||
scopeApp = worker.editApp
|
||||
}
|
||||
if err := config.Validate(next); err != nil {
|
||||
return worker.response("Settings were not applied: "+err.Error(), false)
|
||||
}
|
||||
if save && !worker.response("", false).CanSave {
|
||||
return worker.response("This settings file cannot be saved by the Go preview. Use Apply for this session.", false)
|
||||
}
|
||||
if err := worker.applyRuntime(ctx, next); err != nil {
|
||||
return worker.response(err.Error(), false)
|
||||
}
|
||||
if worker.editingActive {
|
||||
worker.editing = next
|
||||
}
|
||||
if scopeApp != "" {
|
||||
worker.appSettings[scopeApp] = next
|
||||
} else {
|
||||
oldGlobal := worker.globalSettings
|
||||
for application, settings := range worker.appSettings {
|
||||
worker.appSettings[application] = config.RebaseApplicationSettings(oldGlobal, next, settings)
|
||||
}
|
||||
worker.globalSettings = next
|
||||
}
|
||||
message := "Settings applied for this session."
|
||||
if save {
|
||||
store, err := config.NewStore(next.Path)
|
||||
if err == nil {
|
||||
if scopeApp != "" {
|
||||
err = store.SaveForApplication(scopeApp, next)
|
||||
} else {
|
||||
err = store.Save(next)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
message = "Applied for this session, but could not save: " + err.Error()
|
||||
} else {
|
||||
message = "Settings applied and saved."
|
||||
}
|
||||
}
|
||||
response := worker.response(message, true)
|
||||
response.Close = close && save && message == "Settings applied and saved."
|
||||
if worker.editingActive && worker.editApp != worker.activeApp {
|
||||
previousApp := worker.activeApp
|
||||
worker.activeApp = ""
|
||||
if previousApp != "" {
|
||||
worker.switchApplication(ctx, previousApp)
|
||||
}
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
func (worker *preferencesWorker) applyRuntime(ctx context.Context, next config.Settings) error {
|
||||
keyboardChanged := next.KeyboardLayout != worker.settings.KeyboardLayout || !reflect.DeepEqual(next.CthulhuModifierKeys, worker.settings.CthulhuModifierKeys)
|
||||
bindingsChanged := keyboardChanged || !reflect.DeepEqual(next.KeyBindings, worker.settings.KeyBindings)
|
||||
bindingsCommitted := false
|
||||
if bindingsChanged {
|
||||
if worker.bindings == nil {
|
||||
return errors.New("Key bindings cannot be changed by this input backend.")
|
||||
}
|
||||
if err := worker.apply_keyboard(ctx, next, keyboardChanged); err != nil {
|
||||
return errors.New("Key bindings were not applied: " + err.Error())
|
||||
}
|
||||
defer func() {
|
||||
if !bindingsCommitted {
|
||||
rollbackCtx, cancel := context.WithTimeout(context.Background(), time.Second)
|
||||
defer cancel()
|
||||
if err := worker.apply_keyboard(rollbackCtx, worker.settings, keyboardChanged); err != nil {
|
||||
worker.logger.Error("restore key bindings after failed preferences apply", "error", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
effectiveNext := worker.effective_settings(next)
|
||||
effectiveCurrent := worker.effective_settings(worker.settings)
|
||||
if worker.stagedReader != nil {
|
||||
for _, extension := range worker.stagedReader.extensions {
|
||||
if err := worker.reader_allowed(ctx, extension.session); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
effectiveNext = next
|
||||
if worker.stagedReader.override != nil {
|
||||
effectiveNext = worker.stagedReader.override.Apply(next)
|
||||
}
|
||||
}
|
||||
speechChanged := !reflect.DeepEqual(tentaclewire.SpeechFromSettings(effectiveNext), tentaclewire.SpeechFromSettings(effectiveCurrent))
|
||||
speechApplied := false
|
||||
defer func() {
|
||||
if speechApplied && !bindingsCommitted {
|
||||
restoreCtx, cancel := context.WithTimeout(context.Background(), 6*time.Second)
|
||||
defer cancel()
|
||||
if err := worker.updateSpeech(restoreCtx, effectiveCurrent); err != nil {
|
||||
worker.logger.Error("restore speech after failed preferences apply", "error", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
if next.EnableSpeech && (speechChanged || !worker.settings.EnableSpeech) {
|
||||
if message := worker.apply_speech_settings(ctx, effectiveNext); message != "" {
|
||||
return errors.New(message)
|
||||
}
|
||||
speechApplied = true
|
||||
}
|
||||
if next.EnableBraille != worker.settings.EnableBraille {
|
||||
if worker.braille == nil {
|
||||
return errors.New("Braille is unavailable in this build.")
|
||||
}
|
||||
if err := worker.braille.Configure(ctx, next.EnableBraille); err != nil {
|
||||
return errors.New("Braille settings were not applied: " + err.Error())
|
||||
}
|
||||
}
|
||||
if next.EnableContractedBraille != worker.settings.EnableContractedBraille ||
|
||||
next.BrailleContractionTable != worker.settings.BrailleContractionTable {
|
||||
if updater, ok := worker.braille.(interface{ SetContraction(bool, string) }); ok {
|
||||
updater.SetContraction(next.EnableContractedBraille, next.BrailleContractionTable)
|
||||
}
|
||||
}
|
||||
if worker.stagedReader != nil {
|
||||
for _, extension := range worker.stagedReader.extensions {
|
||||
if err := worker.reader_allowed(ctx, extension.session); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
worker.extensions = worker.stagedReader.extensions
|
||||
worker.speechOverride, worker.speechOverrideOwner = worker.stagedReader.override, worker.stagedReader.owner
|
||||
}
|
||||
bindingsCommitted = true
|
||||
worker.commit_settings(next)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (worker *preferencesWorker) apply_keyboard(ctx context.Context, settings config.Settings, keyboardChanged bool) error {
|
||||
if updater, ok := worker.bindings.(interface {
|
||||
SetKeyboardConfiguration(context.Context, input.KeyboardLayout, []string, []input.BindingOverride) error
|
||||
}); ok {
|
||||
return updater.SetKeyboardConfiguration(ctx, input.KeyboardLayout(settings.KeyboardLayout), settings.CthulhuModifierKeys, settings.KeyBindings)
|
||||
}
|
||||
if keyboardChanged {
|
||||
return errors.New("this input backend cannot change Cthulhu keys or keyboard layout")
|
||||
}
|
||||
return worker.bindings.SetKeyBindings(ctx, settings.KeyBindings)
|
||||
}
|
||||
|
||||
func (worker *preferencesWorker) apply_speech_settings(ctx context.Context, next config.Settings) string {
|
||||
var err error
|
||||
|
||||
if worker.updater == nil {
|
||||
return "Speech is disabled or this backend cannot change settings."
|
||||
}
|
||||
if updater, ok := worker.updater.(interface {
|
||||
UpdateConfiguration(context.Context, config.Settings) error
|
||||
}); ok {
|
||||
err = updater.UpdateConfiguration(ctx, next)
|
||||
} else if updater, ok := worker.updater.(speechSettingsUpdater); ok {
|
||||
if next.UseCustomEchoVoice {
|
||||
return "This speech backend cannot use a custom echo voice."
|
||||
}
|
||||
err = updater.UpdateSettings(ctx, speechSettingsFromConfig(next))
|
||||
} else {
|
||||
return "This backend cannot change speech settings."
|
||||
}
|
||||
if err != nil {
|
||||
return "Settings were not applied: " + err.Error()
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Commit follows the existing backend updates and keyboard rollback boundary.
|
||||
// It publishes settings to runtime consumers without changing their ownership.
|
||||
func (worker *preferencesWorker) commit_settings(next config.Settings) {
|
||||
soundChanged := soundConfiguration(next) != soundConfiguration(worker.settings)
|
||||
worker.settings = next
|
||||
if updater, ok := worker.braille.(interface{ SetWordWrap(bool) }); ok {
|
||||
updater.SetWordWrap(next.Braille.WordWrap)
|
||||
}
|
||||
if worker.speechWorker != nil && !next.EnableSpeech {
|
||||
worker.speechWorker.SetEnabled(false)
|
||||
}
|
||||
if worker.history != nil {
|
||||
worker.history.setLimit(next.SpeechHistoryLimit)
|
||||
if worker.refreshHistory != nil {
|
||||
worker.refreshHistory()
|
||||
}
|
||||
}
|
||||
if worker.documentReview != nil {
|
||||
worker.documentReview.SetDocumentBrailleEnabled(next.EnableBraille)
|
||||
}
|
||||
// Configure cancels playback. An application switch or unrelated setting
|
||||
// must leave the startup cue running when its sound policy is unchanged.
|
||||
if worker.sound != nil && soundChanged {
|
||||
worker.sound.Configure(soundConfiguration(next))
|
||||
}
|
||||
if worker.geometry != nil {
|
||||
worker.geometry.SetCaptureSoundGeometry(captureSoundGeometry(next))
|
||||
}
|
||||
if worker.textAttributes != nil {
|
||||
worker.textAttributes.SetCaptureTextAttributes(captureTextAttributes(next))
|
||||
}
|
||||
if worker.live != nil {
|
||||
worker.live.Store(worker.effective_settings(next))
|
||||
}
|
||||
if worker.speechWorker != nil && next.EnableSpeech {
|
||||
worker.speechWorker.SetEnabled(true)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user