From 56e38a9ba15df0f61285a015261a31f1e5444f60 Mon Sep 17 00:00:00 2001 From: Anna Rose Wiggins Date: Thu, 17 Jul 2025 15:30:32 -0400 Subject: [PATCH] Fix explicit device capabilities and add tests. --- internal/config/devices.go | 34 ++++++++----- internal/config/devices_test.go | 90 ++++++++++++++++++++++++++------- 2 files changed, 95 insertions(+), 29 deletions(-) diff --git a/internal/config/devices.go b/internal/config/devices.go index 38d74d8..15d6c1f 100644 --- a/internal/config/devices.go +++ b/internal/config/devices.go @@ -23,6 +23,12 @@ func (parser *ConfigParser) CreateVirtualDevices() map[string]*evdev.InputDevice } 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( name, // TODO: who knows what these should actually be @@ -32,11 +38,7 @@ func (parser *ConfigParser) CreateVirtualDevices() map[string]*evdev.InputDevice Product: 0x0816, Version: 1, }, - 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), - }, + capabilities, ) if err != nil { @@ -45,7 +47,13 @@ func (parser *ConfigParser) CreateVirtualDevices() map[string]*evdev.InputDevice } 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 @@ -81,6 +89,8 @@ func (parser *ConfigParser) ConnectPhysicalDevices() map[string]*evdev.InputDevi 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 { if numButtons > 0 && len(buttonList) > 0 { 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 { - buttons := make([]evdev.EvCode, len(buttonList)) + buttons := make([]evdev.EvCode, 0, len(buttonList)) for _, codeStr := range buttonList { code, err := parseCode(codeStr, "BTN") if err != nil { @@ -127,7 +137,7 @@ func makeAxes(numAxes int, axisList []string) []evdev.EvCode { } if len(axisList) > 0 { - axes := make([]evdev.EvCode, len(axisList)) + axes := make([]evdev.EvCode, 0, len(axisList)) for _, codeStr := range axisList { code, err := parseCode(codeStr, "ABS") if err != nil { @@ -158,7 +168,7 @@ func makeRelativeAxes(numAxes int, axisList []string) []evdev.EvCode { } if len(axisList) > 0 { - axes := make([]evdev.EvCode, len(axisList)) + axes := make([]evdev.EvCode, 0, len(axisList)) for _, codeStr := range axisList { code, err := parseCode(codeStr, "REL") if err != nil { @@ -170,9 +180,9 @@ func makeRelativeAxes(numAxes int, axisList []string) []evdev.EvCode { return axes } - if numAxes > 8 { - numAxes = 8 - logger.Log("Limiting virtual device relative axes to 8") + if numAxes > 10 { + numAxes = 10 + logger.Log("Limiting virtual device relative axes to 10") } axes := make([]evdev.EvCode, numAxes) diff --git a/internal/config/devices_test.go b/internal/config/devices_test.go index 3b979fe..ad3b624 100644 --- a/internal/config/devices_test.go +++ b/internal/config/devices_test.go @@ -15,6 +15,35 @@ func TestRunnerDevicesConfig(t *testing.T) { 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() { t.Run("8 axes", func() { 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_Z)) }) -} -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("4 explicit axis", func() { + axes := makeAxes(0, []string{"x", "y", "throttle", "rudder"}) + t.Equal(4, len(axes)) + 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.Contains(axes, evdev.EvCode(evdev.ABS_RUDDER)) + }) +} + +func (t *DevicesConfigTests) TestMakeRelativeAxes() { + t.Run("10 axes", func() { + axes := makeRelativeAxes(10, []string{}) + t.Equal(10, len(axes)) + t.Contains(axes, evdev.EvCode(evdev.REL_X)) + 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)) }) }