Move initialization code closer to the appropriate structs. (#17)

Reviewed-on: #17
Co-authored-by: Anna Rose Wiggins <annabunches@gmail.com>
Co-committed-by: Anna Rose Wiggins <annabunches@gmail.com>
This commit is contained in:
Anna Rose Wiggins 2025-08-12 00:57:11 +00:00 committed by Anna Rose Wiggins
parent d9babf5dc0
commit 8d2b15a7c8
40 changed files with 1087 additions and 1109 deletions

View file

@ -0,0 +1,77 @@
package eventcodes
import (
"fmt"
"strconv"
"strings"
"github.com/holoplot/go-evdev"
)
func ParseCodeButton(code string) (evdev.EvCode, error) {
prefix := CodePrefixButton
if strings.HasPrefix(code, CodePrefixKey+"_") {
prefix = CodePrefixKey
}
return ParseCode(code, prefix)
}
func ParseCode(code, prefix string) (evdev.EvCode, error) {
code = strings.ToUpper(code)
var codeLookup map[string]evdev.EvCode
switch prefix {
case CodePrefixButton, CodePrefixKey:
codeLookup = evdev.KEYFromString
case CodePrefixAxis:
codeLookup = evdev.ABSFromString
case CodePrefixRelaxis:
codeLookup = evdev.RELFromString
default:
return 0, fmt.Errorf("invalid EvCode prefix '%s'", prefix)
}
switch {
case strings.HasPrefix(code, prefix+"_"):
eventCode, ok := codeLookup[code]
if !ok {
return 0, fmt.Errorf("invalid keycode specification '%s'", code)
}
return eventCode, nil
case strings.HasPrefix(code, "0X"):
codeInt, err := strconv.ParseUint(code[2:], 16, 0)
if err != nil {
return 0, err
}
return evdev.EvCode(codeInt), nil
case prefix == CodePrefixButton && !hasError(strconv.Atoi(code)):
index, err := strconv.Atoi(code)
if err != nil {
return 0, err
}
if index >= len(ButtonFromIndex) {
return 0, fmt.Errorf("button index '%d' out of bounds", index)
}
return ButtonFromIndex[index], nil
default:
eventCode, ok := codeLookup[prefix+"_"+code]
if !ok {
return 0, fmt.Errorf("invalid keycode specification '%s'", code)
}
return eventCode, nil
}
}
// hasError exists solely to switch on errors in conditional and case statements
func hasError(_ any, err error) bool {
return err != nil
}

View file

@ -0,0 +1,142 @@
package eventcodes
import (
"fmt"
"testing"
"github.com/holoplot/go-evdev"
"github.com/stretchr/testify/suite"
)
type EventCodeParserTests struct {
suite.Suite
}
func TestRunnerEventCodeParserTests(t *testing.T) {
suite.Run(t, new(EventCodeParserTests))
}
func parseCodeTestCase(t *EventCodeParserTests, in string, out evdev.EvCode, prefix string) {
t.Run(fmt.Sprintf("%s: %s", prefix, in), func() {
code, err := ParseCode(in, prefix)
t.Nil(err)
t.EqualValues(out, code)
})
}
func (t *EventCodeParserTests) TestParseCodeButton() {
testCases := []struct {
in string
out evdev.EvCode
}{
{"BTN_A", evdev.BTN_A},
{"A", evdev.BTN_A},
{"BTN_TRIGGER_HAPPY", evdev.BTN_TRIGGER_HAPPY},
{"KEY_A", evdev.KEY_A},
{"KEY_ESC", evdev.KEY_ESC},
}
for _, testCase := range testCases {
t.Run(testCase.in, func() {
code, err := ParseCodeButton(testCase.in)
t.Nil(err)
t.EqualValues(code, testCase.out)
})
}
}
func (t *EventCodeParserTests) TestParseCode() {
t.Run("ABS", func() {
testCases := []struct {
in string
out evdev.EvCode
}{
{"ABS_X", evdev.ABS_X},
{"ABS_Y", evdev.ABS_Y},
{"ABS_Z", evdev.ABS_Z},
{"ABS_RX", evdev.ABS_RX},
{"ABS_RY", evdev.ABS_RY},
{"ABS_RZ", evdev.ABS_RZ},
{"ABS_THROTTLE", evdev.ABS_THROTTLE},
{"ABS_RUDDER", evdev.ABS_RUDDER},
{"x", evdev.ABS_X},
{"y", evdev.ABS_Y},
{"z", evdev.ABS_Z},
{"throttle", evdev.ABS_THROTTLE},
{"rudder", evdev.ABS_RUDDER},
{"0x0", evdev.ABS_X},
{"0x1", evdev.ABS_Y},
{"0x2", evdev.ABS_Z},
}
for _, testCase := range testCases {
parseCodeTestCase(t, testCase.in, testCase.out, "ABS")
}
})
t.Run("REL", func() {
testCases := []struct {
in string
out evdev.EvCode
}{
{"REL_X", evdev.REL_X},
{"REL_Y", evdev.REL_Y},
{"REL_Z", evdev.REL_Z},
{"REL_RX", evdev.REL_RX},
{"REL_RY", evdev.REL_RY},
{"REL_RZ", evdev.REL_RZ},
{"REL_WHEEL", evdev.REL_WHEEL},
{"REL_HWHEEL", evdev.REL_HWHEEL},
{"REL_MISC", evdev.REL_MISC},
{"x", evdev.REL_X},
{"y", evdev.REL_Y},
{"wheel", evdev.REL_WHEEL},
{"0x0", evdev.REL_X},
{"0x1", evdev.REL_Y},
{"0x2", evdev.REL_Z},
}
for _, testCase := range testCases {
parseCodeTestCase(t, testCase.in, testCase.out, "REL")
}
})
t.Run("BTN", func() {
testCases := []struct {
in string
out evdev.EvCode
}{
{"BTN_TRIGGER", evdev.BTN_TRIGGER},
{"trigger", evdev.BTN_TRIGGER},
{"0", evdev.BTN_TRIGGER},
{"0x120", evdev.BTN_TRIGGER},
}
for _, testCase := range testCases {
parseCodeTestCase(t, testCase.in, testCase.out, "BTN")
}
})
t.Run("Invalid", func() {
testCases := []struct {
in string
prefix string
}{
{"badbutton", "BTN"},
{"ABS_X", "BTN"},
{"!@#$%^&*(){}-_", "BTN"},
{"REL_X", "ABS"},
{"ABS_W", "ABS"},
{"0", "ABS"},
{"0xg", "ABS"},
}
for _, testCase := range testCases {
t.Run(fmt.Sprintf("%s - '%s'", testCase.prefix, testCase.in), func() {
_, err := ParseCode(testCase.in, testCase.prefix)
t.NotNil(err)
})
}
})
}

View file

@ -0,0 +1,90 @@
package eventcodes
import "github.com/holoplot/go-evdev"
const (
CodePrefixButton = "BTN"
CodePrefixKey = "KEY"
CodePrefixAxis = "ABS"
CodePrefixRelaxis = "REL"
)
var (
// Map joystick buttons to integer indices
ButtonFromIndex = []evdev.EvCode{
evdev.BTN_TRIGGER,
evdev.BTN_THUMB,
evdev.BTN_THUMB2,
evdev.BTN_TOP,
evdev.BTN_TOP2,
evdev.BTN_PINKIE,
evdev.BTN_BASE,
evdev.BTN_BASE2,
evdev.BTN_BASE3,
evdev.BTN_BASE4,
evdev.BTN_BASE5,
evdev.BTN_BASE6,
evdev.EvCode(0x12c), // decimal 300
evdev.EvCode(0x12d), // decimal 301
evdev.EvCode(0x12e), // decimal 302
evdev.BTN_DEAD,
evdev.BTN_TRIGGER_HAPPY1,
evdev.BTN_TRIGGER_HAPPY2,
evdev.BTN_TRIGGER_HAPPY3,
evdev.BTN_TRIGGER_HAPPY4,
evdev.BTN_TRIGGER_HAPPY5,
evdev.BTN_TRIGGER_HAPPY6,
evdev.BTN_TRIGGER_HAPPY7,
evdev.BTN_TRIGGER_HAPPY8,
evdev.BTN_TRIGGER_HAPPY9,
evdev.BTN_TRIGGER_HAPPY10,
evdev.BTN_TRIGGER_HAPPY11,
evdev.BTN_TRIGGER_HAPPY12,
evdev.BTN_TRIGGER_HAPPY13,
evdev.BTN_TRIGGER_HAPPY14,
evdev.BTN_TRIGGER_HAPPY15,
evdev.BTN_TRIGGER_HAPPY16,
evdev.BTN_TRIGGER_HAPPY17,
evdev.BTN_TRIGGER_HAPPY18,
evdev.BTN_TRIGGER_HAPPY19,
evdev.BTN_TRIGGER_HAPPY20,
evdev.BTN_TRIGGER_HAPPY21,
evdev.BTN_TRIGGER_HAPPY22,
evdev.BTN_TRIGGER_HAPPY23,
evdev.BTN_TRIGGER_HAPPY24,
evdev.BTN_TRIGGER_HAPPY25,
evdev.BTN_TRIGGER_HAPPY26,
evdev.BTN_TRIGGER_HAPPY27,
evdev.BTN_TRIGGER_HAPPY28,
evdev.BTN_TRIGGER_HAPPY29,
evdev.BTN_TRIGGER_HAPPY30,
evdev.BTN_TRIGGER_HAPPY31,
evdev.BTN_TRIGGER_HAPPY32,
evdev.BTN_TRIGGER_HAPPY33,
evdev.BTN_TRIGGER_HAPPY34,
evdev.BTN_TRIGGER_HAPPY35,
evdev.BTN_TRIGGER_HAPPY36,
evdev.BTN_TRIGGER_HAPPY37,
evdev.BTN_TRIGGER_HAPPY38,
evdev.BTN_TRIGGER_HAPPY39,
evdev.BTN_TRIGGER_HAPPY40,
evdev.EvCode(0x2e8),
evdev.EvCode(0x2e9),
evdev.EvCode(0x2f0),
evdev.EvCode(0x2f1),
evdev.EvCode(0x2f2),
evdev.EvCode(0x2f3),
evdev.EvCode(0x2f4),
evdev.EvCode(0x2f5),
evdev.EvCode(0x2f6),
evdev.EvCode(0x2f7),
evdev.EvCode(0x2f8),
evdev.EvCode(0x2f9),
evdev.EvCode(0x2fa),
evdev.EvCode(0x2fb),
evdev.EvCode(0x2fc),
evdev.EvCode(0x2fd),
evdev.EvCode(0x2fe),
evdev.EvCode(0x2ff),
}
)