package app import ( "slices" "strings" "github.com/veandco/go-sdl2/sdl" ) type InputAction string const ( InputNone InputAction = "none" InputNextFile InputAction = "next" InputPreviousFile InputAction = "previous" InputMinimize InputAction = "minimize" InputClearFilter InputAction = "clear_filter" InputGroup1 InputAction = "group_1" InputGroup2 InputAction = "group_2" InputGroup3 InputAction = "group_3" InputGroup4 InputAction = "group_4" InputGroup5 InputAction = "group_5" InputGroup6 InputAction = "group_6" InputGroup7 InputAction = "group_7" InputGroup8 InputAction = "group_8" InputGroup9 InputAction = "group_9" ) type InputSequence struct { Keys []sdl.Keycode Delay uint64 } func ParseInputSequence(input string) InputSequence { keys := make([]sdl.Keycode, 0) for key := range strings.SplitSeq(input, "+") { keys = append(keys, sdl.GetKeyFromName(key)) } return InputSequence{Keys: keys, Delay: 0} } const holdThreshold = uint64(2000) type InputHandler struct { actionMap map[InputAction]InputSequence activeInputs []sdl.Keycode lastInputTick uint64 lastTriggerTick uint64 tapCallbacks map[InputAction]func() holdCallbacks map[InputAction]func() holdStartTick map[InputAction]uint64 held map[InputAction]bool holdFired map[InputAction]bool tapFired map[InputAction]bool } func NewInputHandler(actionKeyMap map[InputAction]string) *InputHandler { actionMap := make(map[InputAction]InputSequence) for action, inputSeqStr := range actionKeyMap { actionMap[action] = ParseInputSequence(inputSeqStr) } return &InputHandler{ actionMap: actionMap, activeInputs: make([]sdl.Keycode, 0), tapCallbacks: make(map[InputAction]func()), holdCallbacks: make(map[InputAction]func()), holdStartTick: make(map[InputAction]uint64), held: make(map[InputAction]bool), holdFired: make(map[InputAction]bool), tapFired: make(map[InputAction]bool), } } func (i *InputHandler) HandleEvent(event sdl.Event) { switch event := event.(type) { case *sdl.KeyboardEvent: switch event.Type { case sdl.KEYDOWN: if !slices.Contains(i.activeInputs, event.Keysym.Sym) { i.activeInputs = append(i.activeInputs, event.Keysym.Sym) i.lastInputTick = sdl.GetTicks64() } case sdl.KEYUP: index := slices.Index(i.activeInputs, event.Keysym.Sym) if index != -1 { i.activeInputs = append(i.activeInputs[:index], i.activeInputs[index+1:]...) i.lastInputTick = sdl.GetTicks64() } } } } func (i *InputHandler) Check() { tick := sdl.GetTicks64() for action, inputSeq := range i.actionMap { hasInput := true for _, key := range inputSeq.Keys { if !slices.Contains(i.activeInputs, key) { hasInput = false break } } if hasInput { if !i.held[action] { i.held[action] = true i.holdStartTick[action] = tick i.tapFired[action] = false i.holdFired[action] = false } if tick-i.holdStartTick[action] >= holdThreshold && !i.holdFired[action] { i.holdFired[action] = true if i.holdCallbacks[action] != nil { i.holdCallbacks[action]() i.lastTriggerTick = tick } } } else if i.held[action] { if !i.tapFired[action] && tick-i.holdStartTick[action] < holdThreshold { i.tapFired[action] = true if i.tapCallbacks[action] != nil { i.tapCallbacks[action]() i.lastTriggerTick = tick } } i.held[action] = false i.holdStartTick[action] = 0 } } } func (i *InputHandler) AttachCallback(action InputAction, callback func()) { i.tapCallbacks[action] = callback } func (i *InputHandler) AttachHoldCallback(action InputAction, callback func()) { i.holdCallbacks[action] = callback } func (i *InputHandler) DetachCallback(action InputAction) { i.tapCallbacks[action] = nil i.holdCallbacks[action] = nil }