// Copyright (c) 2026 Storm Dragon // SPDX-License-Identifier: GPL-3.0-or-later package input import ( "testing" "time" "git.stormux.org/storm/fenrir/internal/command" ) func TestCapturedKeysDescribeActualBindingsWithoutDispatch(t *testing.T) { bindings := []Binding{{Name: "Custom", Keys: []string{"FENRIR", "Z"}, Command: command.Time, DoubleCommand: command.Date, TripleCommand: command.Quit}} router, err := New_router(Layout{Bindings: bindings, FenrirKeys: []string{"INSERT", "KP0"}}) if err != nil { t.Fatal(err) } if err := router.Configure_scripts([]string{"KEY_COMPOSE", "KEY_META"}, []ScriptBinding{{ID: "script:example", Keys: []string{"KEY_SCRIPT", "KEY_U"}}}); err != nil { t.Fatal(err) } now := time.Unix(1000, 0) router.now = func() time.Time { return now } router.Capture(true) press := func(code uint16, want command.ID) { t.Helper() decision := router.Key(0, code, 1, false, false) if decision.Forward || decision.Command != "" || decision.Learn == nil || decision.Learn.Code != code || decision.Learn.Command != want { t.Fatalf("code %d: %+v learn=%+v want %s", code, decision, decision.Learn, want) } if got := router.Key(0, code, 2, false, false); got.Learn != nil || got.Command != "" || got.Forward { t.Fatal("repeat dispatched", got) } } for _, want := range []command.ID{command.Time, command.Date, command.Quit} { press(KeyInsert, "") press(44, want) // Releasing the modifier first must not turn the action into typing. for _, code := range []uint16{KeyInsert, 44} { if got := router.Key(0, code, 0, false, false); got.Forward || got.Learn != nil { t.Fatal(got) } } now = now.Add(50 * time.Millisecond) } compose, _ := Key_code("KEY_COMPOSE") for _, modifier := range []uint16{compose, KeyMeta} { press(modifier, "") press(22, "script:example") router.Key(0, 22, 0, false, false) router.Key(0, modifier, 0, false, false) } press(88, "") // F12 has no binding but retains its physical identity. if Key_name(88) != "KEY_F12" { t.Fatal(Key_name(88)) } if got := router.Key(2, KeyRightShift, 1, false, true); got.Learn != nil || got.Forward { t.Fatal("sync spoke", got) } router.Capture(false) if got := router.Key(0, 88, 2, false, false); got.Forward || got.Learn != nil { t.Fatal("held key escaped exit", got) } router.Key(0, 88, 0, false, false) if got := router.Key(0, 30, 1, false, false); !got.Forward { t.Fatal("typing not restored", got) } } func TestTutorialEntryAliasesAndNumLockIdentification(t *testing.T) { for _, layout := range []string{"desktop", "laptop"} { bindings, _, err := Load_bindings([]string{"../../config/keyboard/" + layout + ".toml"}) if err != nil { t.Fatal(err) } for _, entry := range []uint16{KeyH, 59} { router, err := New_router(bindings) if err != nil { t.Fatal(err) } router.Key(0, KeyInsert, 1, false, false) if got := router.Key(0, entry, 1, false, false); got.Command != command.Help { t.Fatal(layout, entry, got) } } router, _ := New_router(bindings) router.Capture(true) for _, numLock := range []bool{false, true} { got := router.Key(0, KeyKP8, 1, numLock, false) want := command.ID("") if !numLock && layout == "desktop" { want = command.CurrentLine } if got.Forward || got.Learn == nil || got.Learn.Command != want { t.Fatal(layout, numLock, got) } router.Key(0, KeyKP8, 0, numLock, false) } } }