115 lines
3.5 KiB
Go
115 lines
3.5 KiB
Go
// Copyright (c) 2026 Storm Dragon
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
package preferences
|
|
|
|
import (
|
|
"fmt"
|
|
"path/filepath"
|
|
"slices"
|
|
|
|
"git.stormux.org/storm/fenrir/internal/config"
|
|
"git.stormux.org/storm/fenrir/internal/input"
|
|
"git.stormux.org/storm/fenrir/internal/interaction"
|
|
)
|
|
|
|
type modifierOption struct{ name, Label string }
|
|
|
|
var modifierOptions = []modifierOption{
|
|
{name: "KEY_KP0", Label: "Keypad Insert"},
|
|
{name: "KEY_INSERT", Label: "Insert"},
|
|
{name: "KEY_CAPSLOCK", Label: "Caps Lock"},
|
|
{name: "KEY_META", Label: "Super"},
|
|
}
|
|
|
|
func reader_names(settings config.Settings) []string {
|
|
if settings.Keyboard.FenrirKeys != nil {
|
|
return settings.Keyboard.FenrirKeys
|
|
}
|
|
// Running applications supply the resolved layout's keys at startup.
|
|
// Standalone forms use the built-in layout defaults.
|
|
var names []string
|
|
switch filepath.Base(settings.Keyboard.Layout) {
|
|
case "laptop", "laptop.toml":
|
|
names = []string{"KEY_KP0", "KEY_INSERT", "KEY_CAPSLOCK"}
|
|
default:
|
|
names = []string{"KEY_KP0", "KEY_INSERT", "KEY_META"}
|
|
}
|
|
available, err := input.Available_fenrir_keys(names, settings.General.ScriptKeys)
|
|
if err != nil {
|
|
return []string{}
|
|
}
|
|
return available
|
|
}
|
|
|
|
func modifier_checked(names []string, name string) bool {
|
|
keys, _ := input.Modifier_codes(names)
|
|
option, _ := input.Modifier_codes([]string{name})
|
|
for key := range option {
|
|
if keys[key] {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func modifier_controls(settings config.Settings) []interaction.Control {
|
|
var controls []interaction.Control
|
|
for _, role := range []struct {
|
|
id, Label string
|
|
names []string
|
|
}{
|
|
{id: "fenrir_key.", Label: "Fenrir keys", names: reader_names(settings)},
|
|
{id: "script_key.", Label: "Script keys", names: settings.General.ScriptKeys},
|
|
} {
|
|
options := slices.Clone(modifierOptions)
|
|
if role.id == "script_key." {
|
|
options = append(options, modifierOption{name: "KEY_COMPOSE", Label: "Compose"})
|
|
}
|
|
for _, option := range options {
|
|
controls = append(controls, interaction.Control{ID: role.id + option.name, Label: option.Label, Group: role.Label, Kind: interaction.CheckBox, Checked: modifier_checked(role.names, option.name)})
|
|
}
|
|
}
|
|
return controls
|
|
}
|
|
|
|
func selected_modifiers(values map[string]interaction.Control, prefix string, names []string, options []modifierOption) ([]string, bool, error) {
|
|
selected := slices.Clone(names)
|
|
changed := false
|
|
for _, option := range options {
|
|
control, ok := values["keyboard."+prefix+option.name]
|
|
if !ok {
|
|
return nil, false, fmt.Errorf("missing modifier checkbox %s", option.name)
|
|
}
|
|
if control.Checked == modifier_checked(names, option.name) {
|
|
continue
|
|
}
|
|
changed = true
|
|
if control.Checked {
|
|
selected = append(selected, option.name)
|
|
continue
|
|
}
|
|
selected = slices.DeleteFunc(selected, func(name string) bool { return modifier_checked([]string{name}, option.name) })
|
|
}
|
|
return selected, changed, nil
|
|
}
|
|
|
|
func modifier_settings(values map[string]interaction.Control, base *config.Settings) error {
|
|
reader, changed, err := selected_modifiers(values, "fenrir_key.", reader_names(*base), modifierOptions)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
scripts, _, err := selected_modifiers(values, "script_key.", base.General.ScriptKeys, append(slices.Clone(modifierOptions), modifierOption{name: "KEY_COMPOSE", Label: "Compose"}))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := input.Validate_modifier_keys(reader, scripts); err != nil {
|
|
return err
|
|
}
|
|
if changed {
|
|
base.Keyboard.FenrirKeys = reader
|
|
}
|
|
base.General.ScriptKeys = scripts
|
|
return nil
|
|
}
|