// Copyright (c) 2026 Storm Dragon // SPDX-License-Identifier: GPL-3.0-or-later package input import ( "git.stormux.org/storm/fenrir/internal/command" "strings" "testing" ) func laptop_router(t *testing.T) Router { t.Helper() layout, _, err := Load_bindings([]string{"../../config/keyboard/laptop.toml"}) if err != nil { t.Fatal(err) } router, err := New_router(layout) if err != nil { t.Fatal(err) } return router } func TestLaptopCapsLockModifier(t *testing.T) { for _, releaseCapsFirst := range []bool{false, true} { r := laptop_router(t) for range 2 { if got := r.Key(0, KeyCapsLock, 1, false, false); got.Forward || got.ToggleCapsLock { t.Fatal(got) } if got := r.Key(0, 23, 1, false, false); got.Command != command.CurrentLine || got.Forward || got.Typing { t.Fatal(got) } keys := []uint16{23, KeyCapsLock} if releaseCapsFirst { keys = []uint16{KeyCapsLock, 23} } for _, key := range keys { if got := r.Key(0, key, 0, false, false); got.Forward || got.ToggleCapsLock { t.Fatal(got) } } r.Reset() } if got := r.Key(0, KeyMeta, 1, false, false); !got.Forward { t.Fatal("Super consumed", got) } if got := r.Key(0, 23, 1, false, false); got.Command != "" || !got.Forward { t.Fatal("Super became Fenrir", got) } } desktop := desktop_router(t) if got := desktop.Key(0, KeyCapsLock, 1, false, false); !got.Forward { t.Fatal("desktop Caps Lock consumed", got) } desktop.Key(0, KeyCapsLock, 0, false, false) desktop.Key(0, KeyMeta, 1, false, false) if got := desktop.Key(0, KeyF, 1, false, false); got.Command != command.FenrirMenu { t.Fatal(got) } } func TestLaptopShiftCapsLockGesture(t *testing.T) { for _, shift := range []uint16{KeyShift, KeyRightShift} { for _, shiftFirst := range []bool{false, true} { for _, releaseShiftFirst := range []bool{false, true} { for _, extraKey := range []uint16{0, 22, KeyCtrl} { r := laptop_router(t) keys := []uint16{KeyCapsLock, shift} if shiftFirst { keys = []uint16{shift, KeyCapsLock} } for _, key := range keys { r.Key(0, key, 1, false, false) } if extraKey != 0 { r.Key(0, extraKey, 1, false, false) r.Key(0, extraKey, 0, false, false) } // Repeats must not toggle or restart the gesture. if got := r.Key(0, KeyCapsLock, 2, false, false); got.ToggleCapsLock { t.Fatal(got) } if releaseShiftFirst { r.Key(0, shift, 0, false, false) } got := r.Key(0, KeyCapsLock, 0, false, false) if got.ToggleCapsLock != (extraKey == 0) || got.Forward { t.Fatal(shift, shiftFirst, releaseShiftFirst, extraKey, got) } if !releaseShiftFirst { r.Key(0, shift, 0, false, false) } if got := r.Key(0, KeyCapsLock, 0, false, false); got.ToggleCapsLock { t.Fatal("orphan release toggled", got) } } } } } } func TestCapsLockCapturePassAndRepair(t *testing.T) { for _, mode := range []string{"capture", "pass", "repair", "drop", "reset"} { r := laptop_router(t) if mode == "capture" { r.Capture(true) } if mode == "pass" { r.Pass_next() } r.Key(0, KeyShift, 1, false, false) got := r.Key(0, KeyCapsLock, 1, false, false) if mode == "pass" && !got.Forward { t.Fatal("pass-next failed", got) } switch mode { case "repair": r.Key(1, KeyCtrl, 1, false, true) case "drop": r.Drop(1) case "reset": r.Reset() } if got := r.Key(0, KeyCapsLock, 0, false, false); got.ToggleCapsLock { t.Fatal(mode, got) } } } func TestLayoutModifierValidation(t *testing.T) { for _, keys := range []string{`["UNKNOWN"]`, `["CAPSLOCK", "CAPSLOCK"]`} { data := strings.Replace(string(desktop_data(t)), "version = 1", "version = 1\nfenrir_keys = "+keys, 1) if _, err := Decode_bindings([]byte(data)); err == nil { t.Fatal("invalid modifier keys accepted", keys) } } r := laptop_router(t) if got := r.script_name(KeyCapsLock, false); got != "KEY_FENRIR" { t.Fatal(got) } if got := r.script_name(KeyMeta, false); got == "KEY_FENRIR" { t.Fatal(got) } }