// 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 ( "strings" "unsafe" "git.stormux.org/storm/cthulhu/internal/input" ) var modifierKeys = []string{"Insert", "KP_Insert", "Caps_Lock"} func (session *uiSession) setKeyBindings(values Values) { session.bindingDraft = input.CloneBindingOverrides(values.KeyBindings) setCheck("generalDesktopButton", values.KeyboardLayout != 2) setCheck("generalLaptopButton", values.KeyboardLayout == 2) setModifierKeys(values.CthulhuModifierKeys) session.bindingLayout = values.KeyboardLayout if session.bindingLayout != 2 { session.bindingLayout = 1 } setCheck("keyBindingDesktopButton", session.bindingLayout == 1) setCheck("keyBindingLaptopButton", session.bindingLayout == 2) session.populateKeyBindings(0) } func (session *uiSession) refreshKeyBindingLayout() { layout := 1 if getCheck("keyBindingLaptopButton") { layout = 2 } if layout != session.bindingLayout && (layout == 1 || layout == 2) { session.bindingLayout = layout session.populateKeyBindings(0) } } func (session *uiSession) populateKeyBindings(selected int) { session.bindingRows = input.BindingRows(input.KeyboardLayout(session.bindingLayout), session.bindingDraft) C.prefs_bindings_clear() for _, row := range session.bindingRows { scope := "Global" if row.Scope == "browse" { scope = "Browse mode" } keys := row.Keys if keys == "" { keys = "Unbound" } label, scopeText, shortcuts, defaults := C.CString(row.Label), C.CString(scope), C.CString(keys), C.CString(row.DefaultKeys) C.prefs_binding_append(label, scopeText, shortcuts, defaults) C.free(unsafe.Pointer(label)) C.free(unsafe.Pointer(scopeText)) C.free(unsafe.Pointer(shortcuts)) C.free(unsafe.Pointer(defaults)) } C.prefs_binding_select(C.int(selected)) // Keep the editor empty when the selected command has been disabled. if selected >= 0 && selected < len(session.bindingRows) { id, text := C.CString("keyBindingEntry"), C.CString(session.bindingRows[selected].Keys) C.prefs_entry_set(id, text) C.free(unsafe.Pointer(id)) C.free(unsafe.Pointer(text)) } } func (session *uiSession) editKeyBinding(action int) { selected := int(C.prefs_binding_selected()) if action != 7 && (selected < 0 || selected >= len(session.bindingRows)) { return } draft := input.CloneBindingOverrides(session.bindingDraft) var row input.BindingRow if selected >= 0 && selected < len(session.bindingRows) { row = session.bindingRows[selected] } kept := draft[:0] for _, override := range draft { matches := override.Layout == input.KeyboardLayout(session.bindingLayout) && (action == 7 || (override.Scope == row.Scope && override.Command == row.Command)) if !matches { kept = append(kept, override) } } draft = kept if action == 4 || action == 5 { keys := []string{} if action == 4 { id := C.CString("keyBindingEntry") text := C.GoString(C.prefs_entry_get(id)) C.free(unsafe.Pointer(id)) if strings.TrimSpace(text) != "" { for _, key := range strings.Split(text, ";") { chord, err := input.ParseCommandChord(key) if err != nil { status(err.Error(), true) return } keys = append(keys, input.FormatCommandChord(chord)) } } } draft = append(draft, input.BindingOverride{Layout: input.KeyboardLayout(session.bindingLayout), Scope: row.Scope, Command: row.Command, Keys: keys}) } if err := input.ValidateBindingOverrides(draft); err != nil { status(err.Error(), true) return } session.bindingDraft = draft session.populateKeyBindings(selected) status("Binding updated. Apply or OK commits these changes.", true) } func setModifierKeys(keys []string) { for _, key := range modifierKeys { enabled := false for _, configured := range keys { if configured == key { enabled = true break } } id := C.CString(key) C.prefs_modifier_set(id, boolean(enabled)) C.free(unsafe.Pointer(id)) } } func getModifierKeys() []string { keys := []string{} for _, key := range modifierKeys { id := C.CString(key) if C.prefs_modifier_get(id) != 0 { keys = append(keys, key) } C.free(unsafe.Pointer(id)) } return keys }