// Copyright (c) 2026 Storm Dragon // Authors: Storm Dragon, Angus Kola // SPDX-License-Identifier: GPL-3.0-or-later package preferences /* #include #include "native.h" */ import "C" import ( "sort" "strings" "unsafe" "git.stormux.org/storm/cthulhu/internal/config" ) func echoChecks(values *Values) []struct { id string value *bool } { return []struct { id string value *bool }{ {"keyEchoCheckButton", &values.EnableKeyEcho}, {"enableAlphabeticKeysCheckButton", &values.EnableAlphabeticKeys}, {"enableNumericKeysCheckButton", &values.EnableNumericKeys}, {"enablePunctuationKeysCheckButton", &values.EnablePunctuationKeys}, {"enableSpaceCheckButton", &values.EnableSpace}, {"enableModifierKeysCheckButton", &values.EnableModifierKeys}, {"enableFunctionKeysCheckButton", &values.EnableFunctionKeys}, {"enableActionKeysCheckButton", &values.EnableActionKeys}, {"enableNavigationKeysCheckButton", &values.EnableNavigationKeys}, {"enableDiacriticalKeysCheckButton", &values.EnableDiacriticalKeys}, {"enableEchoByCharacterCheckButton", &values.EnableEchoByCharacter}, {"enableEchoByWordCheckButton", &values.EnableEchoByWord}, {"enableEchoBySentenceCheckButton", &values.EnableEchoBySentence}, {"useCustomEchoVoiceCheckButton", &values.UseCustomEchoVoice}, {"useCustomEchoSpeechServerCheckButton", &values.UseCustomEchoSpeechServer}, {"useCustomEchoForKeyCheckButton", &values.UseCustomEchoForKey}, {"useCustomEchoForCharacterCheckButton", &values.UseCustomEchoForCharacter}, {"useCustomEchoForWordCheckButton", &values.UseCustomEchoForWord}, {"useCustomEchoForSentenceCheckButton", &values.UseCustomEchoForSentence}, } } func (session *uiSession) setEchoVoice(values Values) { session.echoModules = []string{""} for module := range session.catalog { if module != "" { session.echoModules = append(session.echoModules, module) } } sort.Strings(session.echoModules[1:]) selected := 0 if values.EchoSpeechServer != "" { selected = -1 for i, module := range session.echoModules { if module == values.EchoSpeechServer { selected = i } } if selected < 0 { selected = len(session.echoModules) session.echoModules = append(session.echoModules, values.EchoSpeechServer) } } labels := append([]string(nil), session.echoModules...) labels[0] = "Default" comboItems("echoSpeechServers", labels, selected) // The toggle state is installed by setValues before the next GTK iteration. module := "" if values.UseCustomEchoSpeechServer { module = values.EchoSpeechServer } session.populateEchoFamilies(module, values.EchoVoice.Family) for _, item := range []struct { id string value *float64 fallback float64 }{ {"echoRateScale", values.EchoVoice.Rate, voiceFallback(values.Rate, 50)}, {"echoPitchScale", values.EchoVoice.AveragePitch, voiceFallback(values.Pitch, 5)}, {"echoVolumeScale", values.EchoVoice.Gain, voiceFallback(values.Volume, 10)}, } { id := C.CString(item.id) C.prefs_range_set(id, C.double(voiceFallback(item.value, item.fallback))) C.free(unsafe.Pointer(id)) } } func voiceFallback(value *float64, fallback float64) float64 { if value != nil { return *value } return fallback } func (session *uiSession) populateEchoFamilies(module string, selected *config.VoiceFamily) { session.activeEchoModule = module session.echoFamilies = []*config.VoiceFamily{nil} labels := []string{"Default"} index := 0 for _, voice := range session.default_voices(module) { family := &config.VoiceFamily{Name: voice.Name, Lang: voice.Language, Variant: voice.Variant} session.echoFamilies = append(session.echoFamilies, family) labels = append(labels, voice.Name+" ("+voice.Language+")") if selected != nil && selected.Name == voice.Name && strings.EqualFold(strings.TrimSuffix(selected.Lang+"-"+selected.Dialect, "-"), voice.Language) { index = len(labels) - 1 } } if selected != nil && index == 0 { index = len(labels) session.echoFamilies = append(session.echoFamilies, selected) labels = append(labels, selected.Name+" ("+selected.Lang+")") } comboItems("echoSpeechFamilies", labels, index) } func (session *uiSession) selectedEchoModule() string { i := getCombo("echoSpeechServers") if i >= 0 && i < len(session.echoModules) { return session.echoModules[i] } return "" } func (session *uiSession) refreshEchoVoices() { module := "" if getCheck("useCustomEchoSpeechServerCheckButton") { module = session.selectedEchoModule() } if module != session.activeEchoModule { session.populateEchoFamilies(module, nil) } } func (session *uiSession) getEchoVoice() (config.Voice, string) { var voice config.Voice for _, item := range []struct { id string target **float64 }{ {"echoRateScale", &voice.Rate}, {"echoPitchScale", &voice.AveragePitch}, {"echoVolumeScale", &voice.Gain}, } { id := C.CString(item.id) value := float64(C.prefs_number_value(id)) C.free(unsafe.Pointer(id)) *item.target = &value } i := getCombo("echoSpeechFamilies") if i >= 0 && i < len(session.echoFamilies) { voice.Family = session.echoFamilies[i] } return voice, session.selectedEchoModule() } func set_echo_checks(values Values) { for _, check := range echoChecks(&values) { setCheck(check.id, *check.value) } } func get_echo_checks(values *Values) { for _, check := range echoChecks(values) { *check.value = getCheck(check.id) } }