diff --git a/internal/config/devices.go b/internal/config/devices.go index 9802bff..4fa75da 100644 --- a/internal/config/devices.go +++ b/internal/config/devices.go @@ -22,6 +22,8 @@ func (parser *ConfigParser) CreateVirtualDevices() map[string]*evdev.InputDevice continue } + deviceConfig := deviceConfig.Config.(DeviceConfigVirtual) + name := fmt.Sprintf("joyful-%s", deviceConfig.Name) var capabilities map[evdev.EvType][]evdev.EvCode @@ -88,6 +90,8 @@ func (parser *ConfigParser) ConnectPhysicalDevices() map[string]*evdev.InputDevi continue } + deviceConfig := deviceConfig.Config.(DeviceConfigPhysical) + var infoName string var device *evdev.InputDevice var err error diff --git a/internal/config/make_rule_targets.go b/internal/config/make_rule_targets.go index 7e8c2eb..203a015 100644 --- a/internal/config/make_rule_targets.go +++ b/internal/config/make_rule_targets.go @@ -8,7 +8,7 @@ import ( "github.com/holoplot/go-evdev" ) -func makeRuleTargetButton(targetConfig RuleTargetConfig, devs map[string]Device) (*mappingrules.RuleTargetButton, error) { +func makeRuleTargetButton(targetConfig RuleTargetConfigButton, devs map[string]Device) (*mappingrules.RuleTargetButton, error) { device, ok := devs[targetConfig.Device] if !ok { return nil, fmt.Errorf("non-existent device '%s'", targetConfig.Device) @@ -27,7 +27,7 @@ func makeRuleTargetButton(targetConfig RuleTargetConfig, devs map[string]Device) ) } -func makeRuleTargetAxis(targetConfig RuleTargetConfig, devs map[string]Device) (*mappingrules.RuleTargetAxis, error) { +func makeRuleTargetAxis(targetConfig RuleTargetConfigAxis, devs map[string]Device) (*mappingrules.RuleTargetAxis, error) { device, ok := devs[targetConfig.Device] if !ok { return nil, fmt.Errorf("non-existent device '%s'", targetConfig.Device) @@ -57,7 +57,7 @@ func makeRuleTargetAxis(targetConfig RuleTargetConfig, devs map[string]Device) ( ) } -func makeRuleTargetRelaxis(targetConfig RuleTargetConfig, devs map[string]Device) (*mappingrules.RuleTargetRelaxis, error) { +func makeRuleTargetRelaxis(targetConfig RuleTargetConfigRelaxis, devs map[string]Device) (*mappingrules.RuleTargetRelaxis, error) { device, ok := devs[targetConfig.Device] if !ok { return nil, fmt.Errorf("non-existent device '%s'", targetConfig.Device) @@ -72,11 +72,10 @@ func makeRuleTargetRelaxis(targetConfig RuleTargetConfig, devs map[string]Device targetConfig.Device, device, eventCode, - targetConfig.Inverted, ) } -func makeRuleTargetModeSelect(targetConfig RuleTargetConfig, allModes []string) (*mappingrules.RuleTargetModeSelect, error) { +func makeRuleTargetModeSelect(targetConfig RuleTargetConfigModeSelect, allModes []string) (*mappingrules.RuleTargetModeSelect, error) { if ok := validateModes(targetConfig.Modes, allModes); !ok { return nil, errors.New("undefined mode in mode select list") } @@ -92,7 +91,7 @@ func hasError(_ any, err error) bool { // calculateDeadzones produces the deadzone start and end values in absolute terms // TODO: on the one hand, this logic feels betten encapsulated in mappingrules. On the other hand, // passing even more parameters to NewRuleTargetAxis feels terrible -func calculateDeadzones(targetConfig RuleTargetConfig, device Device, axis evdev.EvCode) (int32, int32, error) { +func calculateDeadzones(targetConfig RuleTargetConfigAxis, device Device, axis evdev.EvCode) (int32, int32, error) { var deadzoneStart, deadzoneEnd int32 deadzoneStart = 0 diff --git a/internal/config/make_rule_targets_test.go b/internal/config/make_rule_targets_test.go index 6e71fa6..1c5b33f 100644 --- a/internal/config/make_rule_targets_test.go +++ b/internal/config/make_rule_targets_test.go @@ -12,7 +12,6 @@ type MakeRuleTargetsTests struct { suite.Suite devs map[string]Device deviceMock *DeviceMock - config RuleTargetConfig } type DeviceMock struct { @@ -47,198 +46,198 @@ func (t *MakeRuleTargetsTests) SetupSuite() { } } -func (t *MakeRuleTargetsTests) SetupSubTest() { - t.config = RuleTargetConfig{ - Device: "test", - } -} - func (t *MakeRuleTargetsTests) TestMakeRuleTargetButton() { + config := RuleTargetConfigButton{Device: "test"} + t.Run("Standard keycode", func() { - t.config.Button = "BTN_TRIGGER" - rule, err := makeRuleTargetButton(t.config, t.devs) + 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() { - t.config.Button = "0x2fd" - rule, err := makeRuleTargetButton(t.config, t.devs) + config.Button = "0x2fd" + rule, err := makeRuleTargetButton(config, t.devs) t.Nil(err) t.EqualValues(evdev.EvCode(0x2fd), rule.Button) }) t.Run("Index", func() { - t.config.Button = "3" - rule, err := makeRuleTargetButton(t.config, t.devs) + 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() { - t.config.Button = "74" - _, err := makeRuleTargetButton(t.config, t.devs) + config.Button = "74" + _, err := makeRuleTargetButton(config, t.devs) t.NotNil(err) }) t.Run("Un-prefixed keycode", func() { - t.config.Button = "pinkie" - rule, err := makeRuleTargetButton(t.config, t.devs) + config.Button = "pinkie" + rule, err := makeRuleTargetButton(config, t.devs) t.Nil(err) t.EqualValues(evdev.BTN_PINKIE, rule.Button) }) t.Run("Invalid keycode", func() { - t.config.Button = "foo" - _, err := makeRuleTargetButton(t.config, t.devs) + config.Button = "foo" + _, err := makeRuleTargetButton(config, t.devs) t.NotNil(err) }) } func (t *MakeRuleTargetsTests) TestMakeRuleTargetAxis() { + config := RuleTargetConfigAxis{Device: "test"} + t.Run("Standard code", func() { - t.config.Axis = "ABS_X" - rule, err := makeRuleTargetAxis(t.config, t.devs) + config.Axis = "ABS_X" + rule, err := makeRuleTargetAxis(config, t.devs) t.Nil(err) t.EqualValues(evdev.ABS_X, rule.Axis) }) t.Run("Hex code", func() { - t.config.Axis = "0x01" - rule, err := makeRuleTargetAxis(t.config, t.devs) + config.Axis = "0x01" + rule, err := makeRuleTargetAxis(config, t.devs) t.Nil(err) t.EqualValues(evdev.ABS_Y, rule.Axis) }) t.Run("Un-prefixed code", func() { - t.config.Axis = "x" - rule, err := makeRuleTargetAxis(t.config, t.devs) + config.Axis = "x" + rule, err := makeRuleTargetAxis(config, t.devs) t.Nil(err) t.EqualValues(evdev.ABS_X, rule.Axis) }) t.Run("Invalid code", func() { - t.config.Axis = "foo" - _, err := makeRuleTargetAxis(t.config, t.devs) + config.Axis = "foo" + _, err := makeRuleTargetAxis(config, t.devs) t.NotNil(err) }) t.Run("Invalid deadzone", func() { - t.config.Axis = "x" - t.config.DeadzoneEnd = 100 - t.config.DeadzoneStart = 1000 - _, err := makeRuleTargetAxis(t.config, t.devs) + config.Axis = "x" + config.DeadzoneEnd = 100 + config.DeadzoneStart = 1000 + _, err := makeRuleTargetAxis(config, t.devs) t.NotNil(err) }) t.Run("Deadzone center/size", func() { - t.config.Axis = "x" - t.config.DeadzoneCenter = 5000 - t.config.DeadzoneSize = 1000 - rule, err := makeRuleTargetAxis(t.config, t.devs) + config.Axis = "x" + config.DeadzoneCenter = 5000 + config.DeadzoneSize = 1000 + rule, err := makeRuleTargetAxis(config, t.devs) t.Nil(err) t.EqualValues(4500, rule.DeadzoneStart) t.EqualValues(5500, rule.DeadzoneEnd) }) t.Run("Deadzone center/size lower boundary", func() { - t.config.Axis = "x" - t.config.DeadzoneCenter = 0 - t.config.DeadzoneSize = 500 - rule, err := makeRuleTargetAxis(t.config, t.devs) + config.Axis = "x" + config.DeadzoneCenter = 0 + config.DeadzoneSize = 500 + rule, err := makeRuleTargetAxis(config, t.devs) t.Nil(err) t.EqualValues(0, rule.DeadzoneStart) t.EqualValues(500, rule.DeadzoneEnd) }) t.Run("Deadzone center/size upper boundary", func() { - t.config.Axis = "x" - t.config.DeadzoneCenter = 10000 - t.config.DeadzoneSize = 500 - rule, err := makeRuleTargetAxis(t.config, t.devs) + config.Axis = "x" + config.DeadzoneCenter = 10000 + config.DeadzoneSize = 500 + rule, err := makeRuleTargetAxis(config, t.devs) t.Nil(err) t.EqualValues(9500, rule.DeadzoneStart) t.EqualValues(10000, rule.DeadzoneEnd) }) t.Run("Deadzone center/size invalid center", func() { - t.config.Axis = "x" - t.config.DeadzoneCenter = 20000 - t.config.DeadzoneSize = 500 - _, err := makeRuleTargetAxis(t.config, t.devs) + config.Axis = "x" + config.DeadzoneCenter = 20000 + config.DeadzoneSize = 500 + _, err := makeRuleTargetAxis(config, t.devs) t.NotNil(err) }) t.Run("Deadzone center/percent", func() { - t.config.Axis = "x" - t.config.DeadzoneCenter = 5000 - t.config.DeadzoneSizePercent = 10 - rule, err := makeRuleTargetAxis(t.config, t.devs) + config.Axis = "x" + config.DeadzoneCenter = 5000 + config.DeadzoneSizePercent = 10 + rule, err := makeRuleTargetAxis(config, t.devs) t.Nil(err) t.EqualValues(4500, rule.DeadzoneStart) t.EqualValues(5500, rule.DeadzoneEnd) }) t.Run("Deadzone center/percent lower boundary", func() { - t.config.Axis = "x" - t.config.DeadzoneCenter = 0 - t.config.DeadzoneSizePercent = 10 - rule, err := makeRuleTargetAxis(t.config, t.devs) + config.Axis = "x" + config.DeadzoneCenter = 0 + config.DeadzoneSizePercent = 10 + rule, err := makeRuleTargetAxis(config, t.devs) t.Nil(err) t.EqualValues(0, rule.DeadzoneStart) t.EqualValues(1000, rule.DeadzoneEnd) }) t.Run("Deadzone center/percent upper boundary", func() { - t.config.Axis = "x" - t.config.DeadzoneCenter = 10000 - t.config.DeadzoneSizePercent = 10 - rule, err := makeRuleTargetAxis(t.config, t.devs) + config.Axis = "x" + config.DeadzoneCenter = 10000 + config.DeadzoneSizePercent = 10 + rule, err := makeRuleTargetAxis(config, t.devs) t.Nil(err) t.EqualValues(9000, rule.DeadzoneStart) t.EqualValues(10000, rule.DeadzoneEnd) }) t.Run("Deadzone center/percent invalid center", func() { - t.config.Axis = "x" - t.config.DeadzoneCenter = 20000 - t.config.DeadzoneSizePercent = 10 - _, err := makeRuleTargetAxis(t.config, t.devs) + config.Axis = "x" + config.DeadzoneCenter = 20000 + config.DeadzoneSizePercent = 10 + _, err := makeRuleTargetAxis(config, t.devs) t.NotNil(err) }) } func (t *MakeRuleTargetsTests) TestMakeRuleTargetRelaxis() { + config := RuleTargetConfigRelaxis{Device: "test"} + t.Run("Standard keycode", func() { - t.config.Axis = "REL_WHEEL" - rule, err := makeRuleTargetRelaxis(t.config, t.devs) + 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() { - t.config.Axis = "0x00" - rule, err := makeRuleTargetRelaxis(t.config, t.devs) + 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() { - t.config.Axis = "wheel" - rule, err := makeRuleTargetRelaxis(t.config, t.devs) + config.Axis = "wheel" + rule, err := makeRuleTargetRelaxis(config, t.devs) t.Nil(err) t.EqualValues(evdev.REL_WHEEL, rule.Axis) }) t.Run("Invalid keycode", func() { - t.config.Axis = "foo" - _, err := makeRuleTargetRelaxis(t.config, t.devs) + config.Axis = "foo" + _, err := makeRuleTargetRelaxis(config, t.devs) t.NotNil(err) }) t.Run("Incorrect axis type", func() { - t.config.Axis = "ABS_X" - _, err := makeRuleTargetRelaxis(t.config, t.devs) + config.Axis = "ABS_X" + _, err := makeRuleTargetRelaxis(config, t.devs) t.NotNil(err) }) } diff --git a/internal/config/make_rules.go b/internal/config/make_rules.go index 647987c..b3e73f3 100644 --- a/internal/config/make_rules.go +++ b/internal/config/make_rules.go @@ -42,21 +42,21 @@ func (parser *ConfigParser) BuildRules(pInputDevs map[string]*evdev.InputDevice, switch strings.ToLower(ruleConfig.Type) { case RuleTypeButton: - newRule, err = makeMappingRuleButton(ruleConfig, pDevs, vDevs, base) + newRule, err = makeMappingRuleButton(ruleConfig.Config.(RuleConfigButton), pDevs, vDevs, base) case RuleTypeButtonCombo: - newRule, err = makeMappingRuleCombo(ruleConfig, pDevs, vDevs, base) - case RuleTypeLatched: - newRule, err = makeMappingRuleLatched(ruleConfig, pDevs, vDevs, base) + newRule, err = makeMappingRuleCombo(ruleConfig.Config.(RuleConfigButtonCombo), pDevs, vDevs, base) + case RuleTypeButtonLatched: + newRule, err = makeMappingRuleLatched(ruleConfig.Config.(RuleConfigButtonLatched), pDevs, vDevs, base) case RuleTypeAxis: - newRule, err = makeMappingRuleAxis(ruleConfig, pDevs, vDevs, base) + newRule, err = makeMappingRuleAxis(ruleConfig.Config.(RuleConfigAxis), pDevs, vDevs, base) case RuleTypeAxisCombined: - newRule, err = makeMappingRuleAxisCombined(ruleConfig, pDevs, vDevs, base) + newRule, err = makeMappingRuleAxisCombined(ruleConfig.Config.(RuleConfigAxisCombined), pDevs, vDevs, base) case RuleTypeAxisToButton: - newRule, err = makeMappingRuleAxisToButton(ruleConfig, pDevs, vDevs, base) + newRule, err = makeMappingRuleAxisToButton(ruleConfig.Config.(RuleConfigAxisToButton), pDevs, vDevs, base) case RuleTypeAxisToRelaxis: - newRule, err = makeMappingRuleAxisToRelaxis(ruleConfig, pDevs, vDevs, base) + newRule, err = makeMappingRuleAxisToRelaxis(ruleConfig.Config.(RuleConfigAxisToRelaxis), pDevs, vDevs, base) case RuleTypeModeSelect: - newRule, err = makeMappingRuleModeSelect(ruleConfig, pDevs, modes, base) + newRule, err = makeMappingRuleModeSelect(ruleConfig.Config.(RuleConfigModeSelect), pDevs, modes, base) default: err = fmt.Errorf("bad rule type '%s' for rule '%s'", ruleConfig.Type, ruleConfig.Name) } @@ -72,7 +72,14 @@ func (parser *ConfigParser) BuildRules(pInputDevs map[string]*evdev.InputDevice, return rules } -func makeMappingRuleButton(ruleConfig RuleConfig, +// TODO: how much of these functions could we fold into the unmarshaling logic itself? The main problem +// is that we don't have access to the device maps in those functions... could we set device names +// as stand-ins and do a post-processing pass that *just* handles device linking and possibly mode +// checking? +// +// In other words - can we unmarshal the config directly into our target structs and remove most of +// this library? +func makeMappingRuleButton(ruleConfig RuleConfigButton, pDevs map[string]Device, vDevs map[string]Device, base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleButton, error) { @@ -90,7 +97,7 @@ func makeMappingRuleButton(ruleConfig RuleConfig, return mappingrules.NewMappingRuleButton(base, input, output), nil } -func makeMappingRuleCombo(ruleConfig RuleConfig, +func makeMappingRuleCombo(ruleConfig RuleConfigButtonCombo, pDevs map[string]Device, vDevs map[string]Device, base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleButtonCombo, error) { @@ -112,7 +119,7 @@ func makeMappingRuleCombo(ruleConfig RuleConfig, return mappingrules.NewMappingRuleButtonCombo(base, inputs, output), nil } -func makeMappingRuleLatched(ruleConfig RuleConfig, +func makeMappingRuleLatched(ruleConfig RuleConfigButtonLatched, pDevs map[string]Device, vDevs map[string]Device, base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleButtonLatched, error) { @@ -130,7 +137,7 @@ func makeMappingRuleLatched(ruleConfig RuleConfig, return mappingrules.NewMappingRuleButtonLatched(base, input, output), nil } -func makeMappingRuleAxis(ruleConfig RuleConfig, +func makeMappingRuleAxis(ruleConfig RuleConfigAxis, pDevs map[string]Device, vDevs map[string]Device, base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleAxis, error) { @@ -148,7 +155,7 @@ func makeMappingRuleAxis(ruleConfig RuleConfig, return mappingrules.NewMappingRuleAxis(base, input, output), nil } -func makeMappingRuleAxisCombined(ruleConfig RuleConfig, +func makeMappingRuleAxisCombined(ruleConfig RuleConfigAxisCombined, pDevs map[string]Device, vDevs map[string]Device, base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleAxisCombined, error) { @@ -171,7 +178,7 @@ func makeMappingRuleAxisCombined(ruleConfig RuleConfig, return mappingrules.NewMappingRuleAxisCombined(base, inputLower, inputUpper, output), nil } -func makeMappingRuleAxisToButton(ruleConfig RuleConfig, +func makeMappingRuleAxisToButton(ruleConfig RuleConfigAxisToButton, pDevs map[string]Device, vDevs map[string]Device, base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleAxisToButton, error) { @@ -189,7 +196,7 @@ func makeMappingRuleAxisToButton(ruleConfig RuleConfig, return mappingrules.NewMappingRuleAxisToButton(base, input, output, ruleConfig.RepeatRateMin, ruleConfig.RepeatRateMax), nil } -func makeMappingRuleAxisToRelaxis(ruleConfig RuleConfig, +func makeMappingRuleAxisToRelaxis(ruleConfig RuleConfigAxisToRelaxis, pDevs map[string]Device, vDevs map[string]Device, base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleAxisToRelaxis, error) { @@ -211,7 +218,7 @@ func makeMappingRuleAxisToRelaxis(ruleConfig RuleConfig, ruleConfig.Increment), nil } -func makeMappingRuleModeSelect(ruleConfig RuleConfig, +func makeMappingRuleModeSelect(ruleConfig RuleConfigModeSelect, pDevs map[string]Device, modes []string, base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleModeSelect, error) { diff --git a/internal/config/schema.go b/internal/config/schema.go index 1ea3527..083005a 100644 --- a/internal/config/schema.go +++ b/internal/config/schema.go @@ -1,25 +1,39 @@ // These types comprise the YAML schema for configuring Joyful. // The config files will be combined and then unmarshalled into this -// -// TODO: currently the types in here aren't especially strong; each one is -// decomposed into a different object based on the Type fields. We should implement -// some sort of delayed unmarshalling technique, for example see ideas at -// https://stackoverflow.com/questions/70635636/unmarshaling-yaml-into-different-struct-based-off-yaml-field -// Then we can be more explicit about the interface here. package config +import "fmt" + type Config struct { - Devices []DeviceConfig `yaml:"devices"` - Modes []string `yaml:"modes,omitempty"` - Rules []RuleConfig `yaml:"rules"` + Devices []DeviceConfig + Modes []string + Rules []RuleConfig } +// These structs use custom unmarshaling to inline each available sub-type type DeviceConfig struct { + Type string + Config interface{} `yaml:",inline"` +} + +type RuleConfig struct { + Type string + Name string + Modes []string + Config interface{} `yaml:",inline"` +} + +type DeviceConfigPhysical struct { + Name string `yaml:"name"` + DeviceName string `yaml:"device_name,omitempty"` + DevicePath string `yaml:"device_path,omitempty"` + Lock bool `yaml:"lock,omitempty"` +} + +// TODO: configure custom unmarshaling so we can overload Buttons, Axes, and RelativeAxes... +type DeviceConfigVirtual struct { Name string `yaml:"name"` - Type string `yaml:"type"` - DeviceName string `yaml:"device_name,omitempty"` - DevicePath string `yaml:"device_path,omitempty"` Preset string `yaml:"preset,omitempty"` NumButtons int `yaml:"num_buttons,omitempty"` NumAxes int `yaml:"num_axes,omitempty"` @@ -27,53 +41,130 @@ type DeviceConfig struct { Buttons []string `yaml:"buttons,omitempty"` Axes []string `yaml:"axes,omitempty"` RelativeAxes []string `yaml:"rel_axes,omitempty"` - Lock bool `yaml:"lock,omitempty"` } -type RuleConfig struct { - Name string `yaml:"name,omitempty"` - Type string `yaml:"type"` - Input RuleTargetConfig `yaml:"input,omitempty"` - InputLower RuleTargetConfig `yaml:"input_lower,omitempty"` - InputUpper RuleTargetConfig `yaml:"input_upper,omitempty"` - Inputs []RuleTargetConfig `yaml:"inputs,omitempty"` - Output RuleTargetConfig `yaml:"output"` - Modes []string `yaml:"modes,omitempty"` - RepeatRateMin int `yaml:"repeat_rate_min,omitempty"` - RepeatRateMax int `yaml:"repeat_rate_max,omitempty"` - Increment int `yaml:"increment,omitempty"` +type RuleConfigButton struct { + Input RuleTargetConfigButton + Output RuleTargetConfigButton } -type RuleTargetConfig struct { - Device string `yaml:"device,omitempty"` - Button string `yaml:"button,omitempty"` - Axis string `yaml:"axis,omitempty"` - DeadzoneCenter int32 `yaml:"deadzone_center,omitempty"` - DeadzoneSize int32 `yaml:"deadzone_size,omitempty"` - DeadzoneSizePercent int32 `yaml:"deadzone_size_percent,omitempty"` - DeadzoneStart int32 `yaml:"deadzone_start,omitempty"` - DeadzoneEnd int32 `yaml:"deadzone_end,omitempty"` - Inverted bool `yaml:"inverted,omitempty"` - Modes []string `yaml:"modes,omitempty"` +type RuleConfigButtonCombo struct { + Inputs []RuleTargetConfigButton + Output RuleTargetConfigButton +} + +type RuleConfigButtonLatched struct { + Input RuleTargetConfigButton + Output RuleTargetConfigButton +} + +type RuleConfigAxis struct { + Input RuleTargetConfigAxis + Output RuleTargetConfigAxis +} + +type RuleConfigAxisCombined struct { + InputLower RuleTargetConfigAxis `yaml:"input_lower,omitempty"` + InputUpper RuleTargetConfigAxis `yaml:"input_upper,omitempty"` + Output RuleTargetConfigAxis +} + +type RuleConfigAxisToButton struct { + RepeatRateMin int `yaml:"repeat_rate_min,omitempty"` + RepeatRateMax int `yaml:"repeat_rate_max,omitempty"` + Input RuleTargetConfigAxis + Output RuleTargetConfigButton +} + +type RuleConfigAxisToRelaxis struct { + RepeatRateMin int `yaml:"repeat_rate_min"` + RepeatRateMax int `yaml:"repeat_rate_max"` + Increment int + Input RuleTargetConfigAxis + Output RuleTargetConfigRelaxis +} + +type RuleConfigModeSelect struct { + Input RuleTargetConfigButton + Output RuleTargetConfigModeSelect +} + +type RuleTargetConfigButton struct { + Device string + Button string + Inverted bool +} + +type RuleTargetConfigAxis struct { + Device string + Axis string + DeadzoneCenter int32 `yaml:"deadzone_center,omitempty"` + DeadzoneSize int32 `yaml:"deadzone_size,omitempty"` + DeadzoneSizePercent int32 `yaml:"deadzone_size_percent,omitempty"` + DeadzoneStart int32 `yaml:"deadzone_start,omitempty"` + DeadzoneEnd int32 `yaml:"deadzone_end,omitempty"` + Inverted bool +} + +type RuleTargetConfigRelaxis struct { + Device string + Axis string +} + +type RuleTargetConfigModeSelect struct { + Modes []string +} + +func (dc *DeviceConfig) UnmarshalYAML(unmarshal func(data interface{}) error) error { + var config interface{} + dc.Config = config + + switch dc.Type { + case DeviceTypePhysical: + config = &DeviceConfigPhysical{} + case DeviceTypeVirtual: + config = &DeviceConfigVirtual{} + default: + return fmt.Errorf("invalid device type '%s'", dc.Type) + } + + return unmarshal(&config) +} + +func (dc *RuleConfig) UnmarshalYAML(unmarshal func(data interface{}) error) error { + switch dc.Type { + case RuleTypeButton: + dc.Config = &RuleConfigButton{} + case RuleTypeButtonCombo: + dc.Config = &RuleConfigButtonCombo{} + case RuleTypeButtonLatched: + dc.Config = &RuleConfigButtonLatched{} + case RuleTypeAxis: + dc.Config = &RuleConfigAxis{} + case RuleTypeAxisCombined: + dc.Config = &RuleConfigAxisCombined{} + case RuleTypeAxisToButton: + dc.Config = &RuleConfigAxisToButton{} + case RuleTypeAxisToRelaxis: + dc.Config = &RuleConfigAxisToRelaxis{} + default: + return fmt.Errorf("invalid rule type '%s'", dc.Type) + } + + return unmarshal(&dc.Config) } // TODO: custom yaml unmarshaling is obtuse; do we really need to do all of this work // just to set a single default value? -func (dc *DeviceConfig) UnmarshalYAML(unmarshal func(data interface{}) error) error { +func (dc *DeviceConfigPhysical) UnmarshalYAML(unmarshal func(data interface{}) error) error { var raw struct { - Name string - Type string - DeviceName string `yaml:"device_name"` - DevicePath string `yaml:"device_path"` - Preset string - NumButtons int `yaml:"num_buttons"` - NumAxes int `yaml:"num_axes"` - NumRelativeAxes int `yaml:"num_rel_axes"` - Buttons []string - Axes []string - RelativeAxes []string `yaml:"relative_axes"` - Lock bool `yaml:"lock,omitempty"` + Name string + DeviceName string `yaml:"device_name"` + DevicePath string `yaml:"device_path"` + Lock bool `yaml:"lock,omitempty"` } + + // Set non-standard defaults raw.Lock = true err := unmarshal(&raw) @@ -81,19 +172,11 @@ func (dc *DeviceConfig) UnmarshalYAML(unmarshal func(data interface{}) error) er return err } - *dc = DeviceConfig{ - Name: raw.Name, - Type: raw.Type, - DeviceName: raw.DeviceName, - DevicePath: raw.DevicePath, - Preset: raw.Preset, - NumButtons: raw.NumButtons, - NumAxes: raw.NumAxes, - NumRelativeAxes: raw.NumRelativeAxes, - Buttons: raw.Buttons, - Axes: raw.Axes, - RelativeAxes: raw.RelativeAxes, - Lock: raw.Lock, + *dc = DeviceConfigPhysical{ + Name: raw.Name, + DeviceName: raw.DeviceName, + DevicePath: raw.DevicePath, + Lock: raw.Lock, } return nil } diff --git a/internal/config/variables.go b/internal/config/variables.go index e4e0bf0..6e62977 100644 --- a/internal/config/variables.go +++ b/internal/config/variables.go @@ -15,12 +15,12 @@ const ( RuleTypeButton = "button" RuleTypeButtonCombo = "button-combo" - RuleTypeLatched = "button-latched" + RuleTypeButtonLatched = "button-latched" RuleTypeAxis = "axis" RuleTypeAxisCombined = "axis-combined" - RuleTypeModeSelect = "mode-select" RuleTypeAxisToButton = "axis-to-button" RuleTypeAxisToRelaxis = "axis-to-relaxis" + RuleTypeModeSelect = "mode-select" CodePrefixButton = "BTN" CodePrefixKey = "KEY" diff --git a/internal/mappingrules/rule_target_relaxis.go b/internal/mappingrules/rule_target_relaxis.go index 8de8c0b..1942c4b 100644 --- a/internal/mappingrules/rule_target_relaxis.go +++ b/internal/mappingrules/rule_target_relaxis.go @@ -8,19 +8,16 @@ type RuleTargetRelaxis struct { DeviceName string Device Device Axis evdev.EvCode - Inverted bool } func NewRuleTargetRelaxis(device_name string, device Device, - axis evdev.EvCode, - inverted bool) (*RuleTargetRelaxis, error) { + axis evdev.EvCode) (*RuleTargetRelaxis, error) { return &RuleTargetRelaxis{ DeviceName: device_name, Device: device, Axis: axis, - Inverted: inverted, }, nil }