Fix explicit device capabilities and add tests.

This commit is contained in:
Anna Rose Wiggins 2025-07-17 15:30:32 -04:00
parent 10d8c67a68
commit 56e38a9ba1
2 changed files with 95 additions and 29 deletions

View file

@ -23,6 +23,12 @@ func (parser *ConfigParser) CreateVirtualDevices() map[string]*evdev.InputDevice
} }
name := fmt.Sprintf("joyful-%s", deviceConfig.Name) name := fmt.Sprintf("joyful-%s", deviceConfig.Name)
capabilities := map[evdev.EvType][]evdev.EvCode{
evdev.EV_KEY: makeButtons(deviceConfig.NumButtons, deviceConfig.Buttons),
evdev.EV_ABS: makeAxes(deviceConfig.NumAxes, deviceConfig.Axes),
evdev.EV_REL: makeRelativeAxes(deviceConfig.NumRelativeAxes, deviceConfig.RelativeAxes),
}
device, err := evdev.CreateDevice( device, err := evdev.CreateDevice(
name, name,
// TODO: who knows what these should actually be // TODO: who knows what these should actually be
@ -32,11 +38,7 @@ func (parser *ConfigParser) CreateVirtualDevices() map[string]*evdev.InputDevice
Product: 0x0816, Product: 0x0816,
Version: 1, Version: 1,
}, },
map[evdev.EvType][]evdev.EvCode{ capabilities,
evdev.EV_KEY: makeButtons(deviceConfig.NumButtons, deviceConfig.Buttons),
evdev.EV_ABS: makeAxes(deviceConfig.NumAxes, deviceConfig.Axes),
evdev.EV_REL: makeRelativeAxes(deviceConfig.NumRelativeAxes, deviceConfig.RelativeAxes),
},
) )
if err != nil { if err != nil {
@ -45,7 +47,13 @@ func (parser *ConfigParser) CreateVirtualDevices() map[string]*evdev.InputDevice
} }
deviceMap[deviceConfig.Name] = device deviceMap[deviceConfig.Name] = device
logger.Log(fmt.Sprintf("Created virtual device '%s'", name)) logger.Log(fmt.Sprintf(
"Created virtual device '%s' with %d buttons, %d axes, and %d relative axes",
name,
len(capabilities[evdev.EV_KEY]),
len(capabilities[evdev.EV_ABS]),
len(capabilities[evdev.EV_REL]),
))
} }
return deviceMap return deviceMap
@ -81,6 +89,8 @@ func (parser *ConfigParser) ConnectPhysicalDevices() map[string]*evdev.InputDevi
return deviceMap return deviceMap
} }
// TODO: these functions have a lot of duplication; we need to figure out how to refactor it cleanly
// without losing logging context...
func makeButtons(numButtons int, buttonList []string) []evdev.EvCode { func makeButtons(numButtons int, buttonList []string) []evdev.EvCode {
if numButtons > 0 && len(buttonList) > 0 { if numButtons > 0 && len(buttonList) > 0 {
logger.Log("'num_buttons' and 'buttons' both specified, ignoring 'num_buttons'") logger.Log("'num_buttons' and 'buttons' both specified, ignoring 'num_buttons'")
@ -92,7 +102,7 @@ func makeButtons(numButtons int, buttonList []string) []evdev.EvCode {
} }
if len(buttonList) > 0 { if len(buttonList) > 0 {
buttons := make([]evdev.EvCode, len(buttonList)) buttons := make([]evdev.EvCode, 0, len(buttonList))
for _, codeStr := range buttonList { for _, codeStr := range buttonList {
code, err := parseCode(codeStr, "BTN") code, err := parseCode(codeStr, "BTN")
if err != nil { if err != nil {
@ -127,7 +137,7 @@ func makeAxes(numAxes int, axisList []string) []evdev.EvCode {
} }
if len(axisList) > 0 { if len(axisList) > 0 {
axes := make([]evdev.EvCode, len(axisList)) axes := make([]evdev.EvCode, 0, len(axisList))
for _, codeStr := range axisList { for _, codeStr := range axisList {
code, err := parseCode(codeStr, "ABS") code, err := parseCode(codeStr, "ABS")
if err != nil { if err != nil {
@ -158,7 +168,7 @@ func makeRelativeAxes(numAxes int, axisList []string) []evdev.EvCode {
} }
if len(axisList) > 0 { if len(axisList) > 0 {
axes := make([]evdev.EvCode, len(axisList)) axes := make([]evdev.EvCode, 0, len(axisList))
for _, codeStr := range axisList { for _, codeStr := range axisList {
code, err := parseCode(codeStr, "REL") code, err := parseCode(codeStr, "REL")
if err != nil { if err != nil {
@ -170,9 +180,9 @@ func makeRelativeAxes(numAxes int, axisList []string) []evdev.EvCode {
return axes return axes
} }
if numAxes > 8 { if numAxes > 10 {
numAxes = 8 numAxes = 10
logger.Log("Limiting virtual device relative axes to 8") logger.Log("Limiting virtual device relative axes to 10")
} }
axes := make([]evdev.EvCode, numAxes) axes := make([]evdev.EvCode, numAxes)

View file

@ -15,6 +15,35 @@ func TestRunnerDevicesConfig(t *testing.T) {
suite.Run(t, new(DevicesConfigTests)) suite.Run(t, new(DevicesConfigTests))
} }
func (t *DevicesConfigTests) TestMakeButtons() {
t.Run("Maximum buttons", func() {
buttons := makeButtons(VirtualDeviceMaxButtons, []string{})
t.Equal(VirtualDeviceMaxButtons, len(buttons))
})
t.Run("Truncated buttons", func() {
buttons := makeButtons(VirtualDeviceMaxButtons+1, []string{})
t.Equal(VirtualDeviceMaxButtons, len(buttons))
})
t.Run("16 buttons", func() {
buttons := makeButtons(16, []string{})
t.Equal(16, len(buttons))
t.Contains(buttons, evdev.EvCode(evdev.BTN_DEAD))
t.NotContains(buttons, evdev.EvCode(evdev.BTN_TRIGGER_HAPPY))
})
t.Run("Explicit buttons", func() {
buttonConfig := []string{"BTN_THUMB", "top", "btn_top2", "0x2fe", "0x300", "15"}
buttons := makeButtons(0, buttonConfig)
t.Equal(len(buttonConfig), len(buttons))
t.Contains(buttons, evdev.EvCode(0x2fe))
t.Contains(buttons, evdev.EvCode(0x300))
t.Contains(buttons, evdev.EvCode(evdev.BTN_TOP))
t.Contains(buttons, evdev.EvCode(evdev.BTN_DEAD))
})
}
func (t *DevicesConfigTests) TestMakeAxes() { func (t *DevicesConfigTests) TestMakeAxes() {
t.Run("8 axes", func() { t.Run("8 axes", func() {
axes := makeAxes(8, []string{}) axes := makeAxes(8, []string{})
@ -41,23 +70,50 @@ func (t *DevicesConfigTests) TestMakeAxes() {
t.Contains(axes, evdev.EvCode(evdev.ABS_Y)) t.Contains(axes, evdev.EvCode(evdev.ABS_Y))
t.Contains(axes, evdev.EvCode(evdev.ABS_Z)) t.Contains(axes, evdev.EvCode(evdev.ABS_Z))
}) })
}
func (t *DevicesConfigTests) TestMakeButtons() { t.Run("4 explicit axis", func() {
t.Run("Maximum buttons", func() { axes := makeAxes(0, []string{"x", "y", "throttle", "rudder"})
buttons := makeButtons(VirtualDeviceMaxButtons, []string{}) t.Equal(4, len(axes))
t.Equal(VirtualDeviceMaxButtons, len(buttons)) t.Contains(axes, evdev.EvCode(evdev.ABS_X))
}) t.Contains(axes, evdev.EvCode(evdev.ABS_Y))
t.Contains(axes, evdev.EvCode(evdev.ABS_THROTTLE))
t.Run("Truncated buttons", func() { t.Contains(axes, evdev.EvCode(evdev.ABS_RUDDER))
buttons := makeButtons(VirtualDeviceMaxButtons+1, []string{}) })
t.Equal(VirtualDeviceMaxButtons, len(buttons)) }
})
func (t *DevicesConfigTests) TestMakeRelativeAxes() {
t.Run("16 buttons", func() { t.Run("10 axes", func() {
buttons := makeButtons(16, []string{}) axes := makeRelativeAxes(10, []string{})
t.Equal(16, len(buttons)) t.Equal(10, len(axes))
t.Contains(buttons, evdev.EvCode(evdev.BTN_DEAD)) t.Contains(axes, evdev.EvCode(evdev.REL_X))
t.NotContains(buttons, evdev.EvCode(evdev.BTN_TRIGGER_HAPPY)) t.Contains(axes, evdev.EvCode(evdev.REL_MISC))
})
t.Run("11 axes", func() {
axes := makeRelativeAxes(11, []string{})
t.Equal(10, len(axes))
})
t.Run("3 axes", func() {
axes := makeRelativeAxes(3, []string{})
t.Equal(3, len(axes))
t.Contains(axes, evdev.EvCode(evdev.REL_X))
t.Contains(axes, evdev.EvCode(evdev.REL_Y))
t.Contains(axes, evdev.EvCode(evdev.REL_Z))
})
t.Run("1 explicit axis", func() {
axes := makeRelativeAxes(0, []string{"wheel"})
t.Equal(1, len(axes))
t.Contains(axes, evdev.EvCode(evdev.REL_WHEEL))
})
t.Run("4 explicit axis", func() {
axes := makeRelativeAxes(0, []string{"x", "y", "wheel", "hwheel"})
t.Equal(4, len(axes))
t.Contains(axes, evdev.EvCode(evdev.REL_X))
t.Contains(axes, evdev.EvCode(evdev.REL_Y))
t.Contains(axes, evdev.EvCode(evdev.REL_WHEEL))
t.Contains(axes, evdev.EvCode(evdev.REL_HWHEEL))
}) })
} }