// Copyright (c) 2026 Storm Dragon // SPDX-License-Identifier: GPL-3.0-or-later //go:build linux && cgo package input import ( "fmt" "strings" ) // Modifier_codes expands generic modifiers to their physical left/right keys. func Modifier_codes(names []string) (map[uint16]bool, error) { keys := make(map[uint16]bool) for _, name := range names { code, ok := Key_code(name) if !ok { return nil, fmt.Errorf("invalid modifier key %q", name) } keys[code] = true switch strings.ToUpper(name) { case "KEY_META": keys[KeyRightMeta] = true case "KEY_CTRL": keys[KeyRightCtrl] = true case "KEY_SHIFT": keys[KeyRightShift] = true case "KEY_ALT": keys[KeyRightAlt] = true } } return keys, nil } // Fenrir_codes validates the supported configurable reader modifiers. func Fenrir_codes(names []string) (map[uint16]bool, error) { if len(names) == 0 { return nil, fmt.Errorf("select at least one Fenrir key") } keys, err := Modifier_codes(names) if err != nil { return nil, err } for code := range keys { switch code { case KeyInsert, KeyKP0, KeyCapsLock, KeyMeta, KeyRightMeta: default: return nil, fmt.Errorf("unsupported Fenrir key %q", Key_name(code)) } } return keys, nil } func Validate_modifier_keys(fenrirKeys, scriptKeys []string) error { reader, err := Fenrir_codes(fenrirKeys) if err != nil { return err } scripts, err := Modifier_codes(scriptKeys) if err != nil { return err } for code := range scripts { if reader[code] { return fmt.Errorf("%s cannot be both a Fenrir key and a script key", Key_name(code)) } } return nil } func (r *Router) Set_fenrir_keys(names []string) error { keys, err := Fenrir_codes(names) if err != nil { return err } for key := range keys { if r.scriptKeys[key] { return fmt.Errorf("%s cannot be both a Fenrir key and a script key", Key_name(key)) } } r.Reset() r.readerKeys = keys return nil } // Fenrir_keys returns the effective selection for the settings form. func (r *Router) Fenrir_keys() []string { var keys []string for _, code := range []uint16{KeyKP0, KeyInsert, KeyCapsLock, KeyMeta, KeyRightMeta} { if !r.reader_modifier(code, false) { continue } if code == KeyMeta && r.reader_modifier(KeyRightMeta, false) { keys = append(keys, "KEY_META") continue } if code == KeyRightMeta && r.reader_modifier(KeyMeta, false) { continue } keys = append(keys, Key_name(code)) } return keys } func (r *Router) script_modifier(key uint16, numLock bool) bool { return r.scriptKeys[key] && (key != KeyKP0 || !numLock) } // Available_fenrir_keys keeps explicitly configured script keys out of reader // defaults. Explicit reader selections are validated instead of being changed. func Available_fenrir_keys(readerNames, scriptNames []string) ([]string, error) { reader, err := Fenrir_codes(readerNames) if err != nil { return nil, err } scripts, err := Modifier_codes(scriptNames) if err != nil { return nil, err } var names []string for _, key := range []uint16{KeyKP0, KeyInsert, KeyCapsLock, KeyMeta, KeyRightMeta} { if reader[key] && !scripts[key] { names = append(names, Key_name(key)) } } if len(names) == 0 { return nil, fmt.Errorf("select at least one Fenrir key that is not a script key") } return names, nil } func (r *Router) Configure_modifiers(fenrirKeys, scriptKeys []string) error { if fenrirKeys == nil { var err error fenrirKeys, err = Available_fenrir_keys(r.Fenrir_keys(), scriptKeys) if err != nil { return err } } if err := Validate_modifier_keys(fenrirKeys, scriptKeys); err != nil { return err } candidate := *r candidate.scriptKeys = nil if err := candidate.Set_fenrir_keys(fenrirKeys); err != nil { return err } if err := candidate.Configure_scripts(scriptKeys, nil); err != nil { return err } candidate.scriptBindings = r.scriptBindings *r = candidate return nil }