Allow button targets to handle keyboard events.

This commit is contained in:
Anna Rose Wiggins 2025-08-01 17:26:19 -04:00
parent 61fe5208e6
commit 32fa7d27e0
4 changed files with 123 additions and 88 deletions

View file

@ -8,13 +8,23 @@ import (
"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:
case CodePrefixButton, CodePrefixKey:
codeLookup = evdev.KEYFromString
case CodePrefixAxis:
codeLookup = evdev.ABSFromString

View file

@ -16,7 +16,7 @@ func TestRunnerEventCodeParserTests(t *testing.T) {
suite.Run(t, new(EventCodeParserTests))
}
func parseCodeTestCase(t *EventCodeParserTests, in string, out int, prefix string) {
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)
@ -24,95 +24,119 @@ func parseCodeTestCase(t *EventCodeParserTests, in string, out int, prefix strin
})
}
func (t *EventCodeParserTests) TestParseCodeABS() {
func (t *EventCodeParserTests) TestParseCodeButton() {
testCases := []struct {
in string
out int
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},
{"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 {
parseCodeTestCase(t, testCase.in, testCase.out, "ABS")
}
}
func (t *EventCodeParserTests) TestParseCodeREL() {
testCases := []struct {
in string
out int
}{
{"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")
}
}
func (t *EventCodeParserTests) TestParseCodeBTN() {
testCases := []struct {
in string
out int
}{
{"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")
}
}
func (t *EventCodeParserTests) TestParseCodeInvalid() {
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)
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

@ -14,7 +14,7 @@ func makeRuleTargetButton(targetConfig RuleTargetConfig, devs map[string]Device)
return nil, fmt.Errorf("non-existent device '%s'", targetConfig.Device)
}
eventCode, err := parseCode(targetConfig.Button, "BTN")
eventCode, err := parseCodeButton(targetConfig.Button)
if err != nil {
return nil, err
}
@ -37,7 +37,7 @@ func makeRuleTargetAxis(targetConfig RuleTargetConfig, devs map[string]Device) (
return nil, errors.New("deadzone_end must be greater than deadzone_start")
}
eventCode, err := parseCode(targetConfig.Axis, "ABS")
eventCode, err := parseCode(targetConfig.Axis, CodePrefixAxis)
if err != nil {
return nil, err
}
@ -63,7 +63,7 @@ func makeRuleTargetRelaxis(targetConfig RuleTargetConfig, devs map[string]Device
return nil, fmt.Errorf("non-existent device '%s'", targetConfig.Device)
}
eventCode, err := parseCode(targetConfig.Axis, "REL")
eventCode, err := parseCode(targetConfig.Axis, CodePrefixRelaxis)
if err != nil {
return nil, err
}

View file

@ -18,6 +18,7 @@ const (
RuleTypeAxisToRelaxis = "axis-to-relaxis"
CodePrefixButton = "BTN"
CodePrefixKey = "KEY"
CodePrefixAxis = "ABS"
CodePrefixRelaxis = "REL"