Add support for multiple keycode formats. (#3)
Additionally: - Increases maximum supported buttons per output device to 74. - Updates documentation. Reviewed-on: #3 Co-authored-by: Anna Rose Wiggins <annabunches@gmail.com> Co-committed-by: Anna Rose Wiggins <annabunches@gmail.com>
This commit is contained in:
parent
e1940006d8
commit
a05dc9126d
5 changed files with 271 additions and 17 deletions
|
@ -82,9 +82,9 @@ func (parser *ConfigParser) ConnectPhysicalDevices() map[string]*evdev.InputDevi
|
|||
}
|
||||
|
||||
func makeButtons(numButtons int) []evdev.EvCode {
|
||||
if numButtons > 56 {
|
||||
numButtons = 56
|
||||
logger.Log("Limiting virtual device buttons to 56")
|
||||
if numButtons > VirtualDeviceMaxButtons {
|
||||
numButtons = VirtualDeviceMaxButtons
|
||||
logger.Logf("Limiting virtual device buttons to %d", VirtualDeviceMaxButtons)
|
||||
}
|
||||
|
||||
buttons := make([]evdev.EvCode, numButtons)
|
||||
|
|
|
@ -3,6 +3,8 @@ package config
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"git.annabunches.net/annabunches/joyful/internal/mappingrules"
|
||||
"github.com/holoplot/go-evdev"
|
||||
|
@ -14,9 +16,39 @@ func makeRuleTargetButton(targetConfig RuleTargetConfig, devs map[string]*evdev.
|
|||
return nil, fmt.Errorf("non-existent device '%s'", targetConfig.Device)
|
||||
}
|
||||
|
||||
eventCode, ok := evdev.KEYFromString[targetConfig.Button]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid button code '%s'", targetConfig.Button)
|
||||
var eventCode evdev.EvCode
|
||||
buttonConfig := strings.ToUpper(targetConfig.Button)
|
||||
switch {
|
||||
case strings.HasPrefix(buttonConfig, "BTN_"):
|
||||
eventCode, ok = evdev.KEYFromString[buttonConfig]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid button specification '%s'", buttonConfig)
|
||||
}
|
||||
|
||||
case strings.HasPrefix(buttonConfig, "0X"):
|
||||
codeInt, err := strconv.ParseUint(buttonConfig[2:], 16, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
eventCode = evdev.EvCode(codeInt)
|
||||
|
||||
case !hasError(strconv.Atoi(buttonConfig)):
|
||||
index, err := strconv.Atoi(buttonConfig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if index >= len(ButtonFromIndex) {
|
||||
return nil, fmt.Errorf("button index '%d' out of bounds", index)
|
||||
}
|
||||
|
||||
eventCode = ButtonFromIndex[index]
|
||||
|
||||
default:
|
||||
eventCode, ok = evdev.KEYFromString["BTN_"+buttonConfig]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid button specification '%s'", buttonConfig)
|
||||
}
|
||||
}
|
||||
|
||||
return mappingrules.NewRuleTargetButton(
|
||||
|
@ -33,9 +65,31 @@ func makeRuleTargetAxis(targetConfig RuleTargetConfig, devs map[string]*evdev.In
|
|||
return nil, fmt.Errorf("non-existent device '%s'", targetConfig.Device)
|
||||
}
|
||||
|
||||
eventCode, ok := evdev.ABSFromString[targetConfig.Axis]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid button code '%s'", targetConfig.Button)
|
||||
if targetConfig.DeadzoneEnd < targetConfig.DeadzoneStart {
|
||||
return nil, errors.New("deadzone_end must be greater than deadzone_start")
|
||||
}
|
||||
|
||||
var eventCode evdev.EvCode
|
||||
axisConfig := strings.ToUpper(targetConfig.Axis)
|
||||
switch {
|
||||
case strings.HasPrefix(axisConfig, "ABS_"):
|
||||
eventCode, ok = evdev.ABSFromString[axisConfig]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid axis code '%s'", axisConfig)
|
||||
}
|
||||
|
||||
case strings.HasPrefix(axisConfig, "0X"):
|
||||
codeInt, err := strconv.ParseUint(axisConfig[2:], 16, 32)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
eventCode = evdev.EvCode(codeInt)
|
||||
|
||||
default:
|
||||
eventCode, ok = evdev.ABSFromString["ABS_"+axisConfig]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid axis code '%s'", axisConfig)
|
||||
}
|
||||
}
|
||||
|
||||
return mappingrules.NewRuleTargetAxis(
|
||||
|
@ -54,11 +108,28 @@ func makeRuleTargetRelaxis(targetConfig RuleTargetConfig, devs map[string]*evdev
|
|||
return nil, fmt.Errorf("non-existent device '%s'", targetConfig.Device)
|
||||
}
|
||||
|
||||
eventCode, ok := evdev.RELFromString[targetConfig.Axis]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid button code '%s'", targetConfig.Button)
|
||||
}
|
||||
var eventCode evdev.EvCode
|
||||
axisConfig := strings.ToUpper(targetConfig.Axis)
|
||||
switch {
|
||||
case strings.HasPrefix(axisConfig, "REL_"):
|
||||
eventCode, ok = evdev.RELFromString[axisConfig]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid axis code '%s'", axisConfig)
|
||||
}
|
||||
|
||||
case strings.HasPrefix(axisConfig, "0X"):
|
||||
codeInt, err := strconv.ParseUint(axisConfig[2:], 16, 32)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
eventCode = evdev.EvCode(codeInt)
|
||||
|
||||
default:
|
||||
eventCode, ok = evdev.RELFromString["REL_"+axisConfig]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid axis code '%s'", axisConfig)
|
||||
}
|
||||
}
|
||||
return mappingrules.NewRuleTargetRelaxis(
|
||||
targetConfig.Device,
|
||||
device,
|
||||
|
@ -74,3 +145,8 @@ func makeRuleTargetModeSelect(targetConfig RuleTargetConfig, allModes []string)
|
|||
|
||||
return mappingrules.NewRuleTargetModeSelect(targetConfig.Modes)
|
||||
}
|
||||
|
||||
// hasError exists solely to switch on errors in case statements
|
||||
func hasError(_ any, err error) bool {
|
||||
return err != nil
|
||||
}
|
||||
|
|
147
internal/config/make_rule_targets_test.go
Normal file
147
internal/config/make_rule_targets_test.go
Normal file
|
@ -0,0 +1,147 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/holoplot/go-evdev"
|
||||
"github.com/stretchr/testify/suite"
|
||||
)
|
||||
|
||||
type MakeRuleTargetsTests struct {
|
||||
suite.Suite
|
||||
devs map[string]*evdev.InputDevice
|
||||
}
|
||||
|
||||
func (t *MakeRuleTargetsTests) SetupSuite() {
|
||||
t.devs = map[string]*evdev.InputDevice{
|
||||
"test": {},
|
||||
}
|
||||
}
|
||||
|
||||
func (t *MakeRuleTargetsTests) TestMakeRuleTargetButton() {
|
||||
config := RuleTargetConfig{
|
||||
Device: "test",
|
||||
}
|
||||
t.Run("Standard keycode", func() {
|
||||
config.Button = "BTN_TRIGGER"
|
||||
rule, err := makeRuleTargetButton(config, t.devs)
|
||||
t.Nil(err)
|
||||
t.EqualValues(evdev.BTN_TRIGGER, rule.Button)
|
||||
})
|
||||
|
||||
t.Run("Hex code", func() {
|
||||
config.Button = "0x2fd"
|
||||
rule, err := makeRuleTargetButton(config, t.devs)
|
||||
t.Nil(err)
|
||||
t.EqualValues(evdev.EvCode(0x2fd), rule.Button)
|
||||
})
|
||||
|
||||
t.Run("Index", func() {
|
||||
config.Button = "3"
|
||||
rule, err := makeRuleTargetButton(config, t.devs)
|
||||
t.Nil(err)
|
||||
t.EqualValues(evdev.BTN_TOP, rule.Button)
|
||||
})
|
||||
|
||||
t.Run("Index too high", func() {
|
||||
config.Button = "74"
|
||||
_, err := makeRuleTargetButton(config, t.devs)
|
||||
t.NotNil(err)
|
||||
})
|
||||
|
||||
t.Run("Un-prefixed keycode", func() {
|
||||
config.Button = "pinkie"
|
||||
rule, err := makeRuleTargetButton(config, t.devs)
|
||||
t.Nil(err)
|
||||
t.EqualValues(evdev.BTN_PINKIE, rule.Button)
|
||||
})
|
||||
|
||||
t.Run("Invalid keycode", func() {
|
||||
config.Button = "foo"
|
||||
_, err := makeRuleTargetButton(config, t.devs)
|
||||
t.NotNil(err)
|
||||
})
|
||||
}
|
||||
|
||||
func (t *MakeRuleTargetsTests) TestMakeRuleTargetAxis() {
|
||||
config := RuleTargetConfig{
|
||||
Device: "test",
|
||||
}
|
||||
|
||||
t.Run("Standard keycode", func() {
|
||||
config.Axis = "ABS_X"
|
||||
rule, err := makeRuleTargetAxis(config, t.devs)
|
||||
t.Nil(err)
|
||||
t.EqualValues(evdev.ABS_X, rule.Axis)
|
||||
})
|
||||
|
||||
t.Run("Hex keycode", func() {
|
||||
config.Axis = "0x01"
|
||||
rule, err := makeRuleTargetAxis(config, t.devs)
|
||||
t.Nil(err)
|
||||
t.EqualValues(evdev.ABS_Y, rule.Axis)
|
||||
})
|
||||
|
||||
t.Run("Un-prefixed keycode", func() {
|
||||
config.Axis = "x"
|
||||
rule, err := makeRuleTargetAxis(config, t.devs)
|
||||
t.Nil(err)
|
||||
t.EqualValues(evdev.ABS_X, rule.Axis)
|
||||
})
|
||||
|
||||
t.Run("Invalid keycode", func() {
|
||||
config.Axis = "foo"
|
||||
_, err := makeRuleTargetAxis(config, t.devs)
|
||||
t.NotNil(err)
|
||||
})
|
||||
|
||||
t.Run("Invalid deadzone", func() {
|
||||
config.DeadzoneEnd = 100
|
||||
config.DeadzoneStart = 1000
|
||||
_, err := makeRuleTargetAxis(config, t.devs)
|
||||
t.NotNil(err)
|
||||
})
|
||||
}
|
||||
|
||||
func (t *MakeRuleTargetsTests) TestMakeRuleTargetRelaxis() {
|
||||
config := RuleTargetConfig{
|
||||
Device: "test",
|
||||
}
|
||||
|
||||
t.Run("Standard keycode", func() {
|
||||
config.Axis = "REL_WHEEL"
|
||||
rule, err := makeRuleTargetRelaxis(config, t.devs)
|
||||
t.Nil(err)
|
||||
t.EqualValues(evdev.REL_WHEEL, rule.Axis)
|
||||
})
|
||||
|
||||
t.Run("Hex keycode", func() {
|
||||
config.Axis = "0x00"
|
||||
rule, err := makeRuleTargetRelaxis(config, t.devs)
|
||||
t.Nil(err)
|
||||
t.EqualValues(evdev.REL_X, rule.Axis)
|
||||
})
|
||||
|
||||
t.Run("Un-prefixed keycode", func() {
|
||||
config.Axis = "wheel"
|
||||
rule, err := makeRuleTargetRelaxis(config, t.devs)
|
||||
t.Nil(err)
|
||||
t.EqualValues(evdev.REL_WHEEL, rule.Axis)
|
||||
})
|
||||
|
||||
t.Run("Invalid keycode", func() {
|
||||
config.Axis = "foo"
|
||||
_, err := makeRuleTargetRelaxis(config, t.devs)
|
||||
t.NotNil(err)
|
||||
})
|
||||
|
||||
t.Run("Incorrect axis type", func() {
|
||||
config.Axis = "ABS_X"
|
||||
_, err := makeRuleTargetRelaxis(config, t.devs)
|
||||
t.NotNil(err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestRunnerMakeRuleTargets(t *testing.T) {
|
||||
suite.Run(t, new(MakeRuleTargetsTests))
|
||||
}
|
|
@ -15,6 +15,8 @@ const (
|
|||
RuleTypeModeSelect = "mode-select"
|
||||
RuleTypeAxisToButton = "axis-to-button"
|
||||
RuleTypeAxisToRelaxis = "axis-to-relaxis"
|
||||
|
||||
VirtualDeviceMaxButtons = 74
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -31,6 +33,10 @@ var (
|
|||
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,
|
||||
|
@ -71,5 +77,23 @@ var (
|
|||
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),
|
||||
}
|
||||
)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue