diff --git a/cmd/joyful/config.go b/cmd/joyful/config.go index 2b43380..64d6b2d 100644 --- a/cmd/joyful/config.go +++ b/cmd/joyful/config.go @@ -2,7 +2,6 @@ package main import ( "context" - "strings" "sync" "git.annabunches.net/annabunches/joyful/internal/configparser" @@ -16,7 +15,7 @@ func initPhysicalDevices(conf *configparser.Config) map[string]*evdev.InputDevic pDeviceMap := make(map[string]*evdev.InputDevice) for _, devConfig := range conf.Devices { - if strings.ToLower(devConfig.Type) != configparser.DeviceTypePhysical { + if devConfig.Type != configparser.DeviceTypePhysical { continue } @@ -71,7 +70,7 @@ func initVirtualBuffers(config *configparser.Config) (map[string]*evdev.InputDev vBuffersByDevice := make(map[*evdev.InputDevice]*virtualdevice.EventBuffer) for _, devConfig := range config.Devices { - if strings.ToLower(devConfig.Type) != configparser.DeviceTypeVirtual { + if devConfig.Type != configparser.DeviceTypeVirtual { continue } diff --git a/internal/configparser/deviceconfig.go b/internal/configparser/deviceconfig.go new file mode 100644 index 0000000..eafd8ca --- /dev/null +++ b/internal/configparser/deviceconfig.go @@ -0,0 +1,31 @@ +package configparser + +// These top-level structs use custom unmarshaling to unpack each available sub-type +type DeviceConfig struct { + Type DeviceType + Config interface{} +} + +func (dc *DeviceConfig) UnmarshalYAML(unmarshal func(data interface{}) error) error { + metaConfig := &struct { + Type DeviceType + }{} + err := unmarshal(metaConfig) + if err != nil { + return err + } + dc.Type = metaConfig.Type + + err = nil + switch metaConfig.Type { + case DeviceTypePhysical: + config := DeviceConfigPhysical{} + err = unmarshal(&config) + dc.Config = config + case DeviceTypeVirtual: + config := DeviceConfigVirtual{} + err = unmarshal(&config) + dc.Config = config + } + return err +} diff --git a/internal/configparser/deviceconfigphysical.go b/internal/configparser/deviceconfigphysical.go new file mode 100644 index 0000000..ecb5255 --- /dev/null +++ b/internal/configparser/deviceconfigphysical.go @@ -0,0 +1,35 @@ +package configparser + +type DeviceConfigPhysical struct { + Name string + DeviceName string `yaml:"device_name,omitempty"` + DevicePath string `yaml:"device_path,omitempty"` + Lock bool +} + +// 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 *DeviceConfigPhysical) UnmarshalYAML(unmarshal func(data interface{}) error) error { + var raw struct { + 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) + if err != nil { + return err + } + + *dc = DeviceConfigPhysical{ + Name: raw.Name, + DeviceName: raw.DeviceName, + DevicePath: raw.DevicePath, + Lock: raw.Lock, + } + return nil +} diff --git a/internal/configparser/devicetype.go b/internal/configparser/devicetype.go new file mode 100644 index 0000000..7640304 --- /dev/null +++ b/internal/configparser/devicetype.go @@ -0,0 +1,40 @@ +package configparser + +import ( + "fmt" + "strings" +) + +type DeviceType string + +const ( + DeviceTypeNone DeviceType = "" + DeviceTypePhysical DeviceType = "physical" + DeviceTypeVirtual DeviceType = "virtual" +) + +var ( + deviceTypeMap = map[string]DeviceType{ + "physical": DeviceTypePhysical, + "virtual": DeviceTypeVirtual, + } +) + +func ParseDeviceType(in string) (DeviceType, error) { + deviceType, ok := deviceTypeMap[strings.ToLower(in)] + if !ok { + return DeviceTypeNone, fmt.Errorf("invalid rule type '%s'", in) + } + return deviceType, nil +} + +func (rt *DeviceType) UnmarshalYAML(unmarshal func(data interface{}) error) error { + var raw string + err := unmarshal(&raw) + if err != nil { + return err + } + + *rt, err = ParseDeviceType(raw) + return err +} diff --git a/internal/configparser/ruleconfig.go b/internal/configparser/ruleconfig.go new file mode 100644 index 0000000..b41e339 --- /dev/null +++ b/internal/configparser/ruleconfig.go @@ -0,0 +1,60 @@ +package configparser + +type RuleConfig struct { + Type RuleType + Name string + Modes []string + Config interface{} +} + +func (dc *RuleConfig) UnmarshalYAML(unmarshal func(data interface{}) error) error { + metaConfig := &struct { + Type RuleType + Name string + Modes []string + }{} + err := unmarshal(metaConfig) + if err != nil { + return err + } + dc.Type = metaConfig.Type + dc.Name = metaConfig.Name + dc.Modes = metaConfig.Modes + + switch dc.Type { + case RuleTypeButton: + config := RuleConfigButton{} + err = unmarshal(&config) + dc.Config = config + case RuleTypeButtonCombo: + config := RuleConfigButtonCombo{} + err = unmarshal(&config) + dc.Config = config + case RuleTypeButtonLatched: + config := RuleConfigButtonLatched{} + err = unmarshal(&config) + dc.Config = config + case RuleTypeAxis: + config := RuleConfigAxis{} + err = unmarshal(&config) + dc.Config = config + case RuleTypeAxisCombined: + config := RuleConfigAxisCombined{} + err = unmarshal(&config) + dc.Config = config + case RuleTypeAxisToButton: + config := RuleConfigAxisToButton{} + err = unmarshal(&config) + dc.Config = config + case RuleTypeAxisToRelaxis: + config := RuleConfigAxisToRelaxis{} + err = unmarshal(&config) + dc.Config = config + case RuleTypeModeSelect: + config := RuleConfigModeSelect{} + err = unmarshal(&config) + dc.Config = config + } + + return err +} diff --git a/internal/configparser/ruletype.go b/internal/configparser/ruletype.go new file mode 100644 index 0000000..7f43001 --- /dev/null +++ b/internal/configparser/ruletype.go @@ -0,0 +1,53 @@ +package configparser + +import ( + "fmt" + "strings" +) + +// TODO: maybe these want to live somewhere other than configparser? +type RuleType string + +const ( + RuleTypeNone RuleType = "" + RuleTypeButton RuleType = "button" + RuleTypeButtonCombo RuleType = "button-combo" + RuleTypeButtonLatched RuleType = "button-latched" + RuleTypeAxis RuleType = "axis" + RuleTypeAxisCombined RuleType = "axis-combined" + RuleTypeAxisToButton RuleType = "axis-to-button" + RuleTypeAxisToRelaxis RuleType = "axis-to-relaxis" + RuleTypeModeSelect RuleType = "mode-select" +) + +var ( + ruleTypeMap = map[string]RuleType{ + "button": RuleTypeButton, + "button-combo": RuleTypeButtonCombo, + "button-latched": RuleTypeButtonLatched, + "axis": RuleTypeAxis, + "axis-combined": RuleTypeAxisCombined, + "axis-to-button": RuleTypeAxisToButton, + "axis-to-relaxis": RuleTypeAxisToRelaxis, + "mode-select": RuleTypeModeSelect, + } +) + +func ParseRuleType(in string) (RuleType, error) { + ruleType, ok := ruleTypeMap[strings.ToLower(in)] + if !ok { + return RuleTypeNone, fmt.Errorf("invalid rule type '%s'", in) + } + return ruleType, nil +} + +func (rt *RuleType) UnmarshalYAML(unmarshal func(data interface{}) error) error { + var raw string + err := unmarshal(&raw) + if err != nil { + return err + } + + *rt, err = ParseRuleType(raw) + return err +} diff --git a/internal/configparser/schema.go b/internal/configparser/schema.go index 8b70521..942f873 100644 --- a/internal/configparser/schema.go +++ b/internal/configparser/schema.go @@ -1,38 +1,13 @@ -// These types comprise the YAML schema for configuring Joyful. -// The config files will be combined and then unmarshalled into this +// These types comprise the YAML schema that doesn't need custom unmarshalling. package configparser -import ( - "fmt" -) - type Config struct { Devices []DeviceConfig Modes []string Rules []RuleConfig } -// These top-level structs use custom unmarshaling to unpack each available sub-type -type DeviceConfig struct { - Type string - Config interface{} -} - -type RuleConfig struct { - Type string - Name string - Modes []string - Config interface{} -} - -type DeviceConfigPhysical struct { - Name string - DeviceName string `yaml:"device_name,omitempty"` - DevicePath string `yaml:"device_path,omitempty"` - Lock bool -} - // TODO: configure custom unmarshaling so we can overload Buttons, Axes, and RelativeAxes... type DeviceConfigVirtual struct { Name string @@ -116,110 +91,3 @@ type RuleTargetConfigRelaxis struct { type RuleTargetConfigModeSelect struct { Modes []string } - -func (dc *DeviceConfig) UnmarshalYAML(unmarshal func(data interface{}) error) error { - metaConfig := &struct { - Type string - }{} - err := unmarshal(metaConfig) - if err != nil { - return err - } - dc.Type = metaConfig.Type - - err = nil - switch metaConfig.Type { - case DeviceTypePhysical: - config := DeviceConfigPhysical{} - err = unmarshal(&config) - dc.Config = config - case DeviceTypeVirtual: - config := DeviceConfigVirtual{} - err = unmarshal(&config) - dc.Config = config - default: - err = fmt.Errorf("invalid device type '%s'", dc.Type) - } - return err -} - -func (dc *RuleConfig) UnmarshalYAML(unmarshal func(data interface{}) error) error { - metaConfig := &struct { - Type string - Name string - Modes []string - }{} - err := unmarshal(metaConfig) - if err != nil { - return err - } - dc.Type = metaConfig.Type - dc.Name = metaConfig.Name - dc.Modes = metaConfig.Modes - - switch dc.Type { - case RuleTypeButton: - config := RuleConfigButton{} - err = unmarshal(&config) - dc.Config = config - case RuleTypeButtonCombo: - config := RuleConfigButtonCombo{} - err = unmarshal(&config) - dc.Config = config - case RuleTypeButtonLatched: - config := RuleConfigButtonLatched{} - err = unmarshal(&config) - dc.Config = config - case RuleTypeAxis: - config := RuleConfigAxis{} - err = unmarshal(&config) - dc.Config = config - case RuleTypeAxisCombined: - config := RuleConfigAxisCombined{} - err = unmarshal(&config) - dc.Config = config - case RuleTypeAxisToButton: - config := RuleConfigAxisToButton{} - err = unmarshal(&config) - dc.Config = config - case RuleTypeAxisToRelaxis: - config := RuleConfigAxisToRelaxis{} - err = unmarshal(&config) - dc.Config = config - case RuleTypeModeSelect: - config := RuleConfigModeSelect{} - err = unmarshal(&config) - dc.Config = config - default: - err = fmt.Errorf("invalid rule type '%s'", dc.Type) - } - - return err -} - -// 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 *DeviceConfigPhysical) UnmarshalYAML(unmarshal func(data interface{}) error) error { - var raw struct { - 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) - if err != nil { - return err - } - - *dc = DeviceConfigPhysical{ - Name: raw.Name, - DeviceName: raw.DeviceName, - DevicePath: raw.DevicePath, - Lock: raw.Lock, - } - return nil -} diff --git a/internal/configparser/variables.go b/internal/configparser/variables.go deleted file mode 100644 index 77e2b9c..0000000 --- a/internal/configparser/variables.go +++ /dev/null @@ -1,15 +0,0 @@ -package configparser - -const ( - DeviceTypePhysical = "physical" - DeviceTypeVirtual = "virtual" - - RuleTypeButton = "button" - RuleTypeButtonCombo = "button-combo" - RuleTypeButtonLatched = "button-latched" - RuleTypeAxis = "axis" - RuleTypeAxisCombined = "axis-combined" - RuleTypeAxisToButton = "axis-to-button" - RuleTypeAxisToRelaxis = "axis-to-relaxis" - RuleTypeModeSelect = "mode-select" -) diff --git a/internal/mappingrules/init_rules.go b/internal/mappingrules/init_rules.go index 7ea0ea4..f621875 100644 --- a/internal/mappingrules/init_rules.go +++ b/internal/mappingrules/init_rules.go @@ -4,7 +4,6 @@ import ( "errors" "fmt" "slices" - "strings" "git.annabunches.net/annabunches/joyful/internal/configparser" "git.annabunches.net/annabunches/joyful/internal/logger" @@ -33,24 +32,25 @@ func NewRule(config configparser.RuleConfig, pDevs map[string]Device, vDevs map[ base := NewMappingRuleBase(config.Name, config.Modes) - switch strings.ToLower(config.Type) { - case RuleTypeButton: + switch config.Type { + case configparser.RuleTypeButton: newRule, err = NewMappingRuleButton(config.Config.(configparser.RuleConfigButton), pDevs, vDevs, base) - case RuleTypeButtonCombo: + case configparser.RuleTypeButtonCombo: newRule, err = NewMappingRuleButtonCombo(config.Config.(configparser.RuleConfigButtonCombo), pDevs, vDevs, base) - case RuleTypeButtonLatched: + case configparser.RuleTypeButtonLatched: newRule, err = NewMappingRuleButtonLatched(config.Config.(configparser.RuleConfigButtonLatched), pDevs, vDevs, base) - case RuleTypeAxis: + case configparser.RuleTypeAxis: newRule, err = NewMappingRuleAxis(config.Config.(configparser.RuleConfigAxis), pDevs, vDevs, base) - case RuleTypeAxisCombined: + case configparser.RuleTypeAxisCombined: newRule, err = NewMappingRuleAxisCombined(config.Config.(configparser.RuleConfigAxisCombined), pDevs, vDevs, base) - case RuleTypeAxisToButton: + case configparser.RuleTypeAxisToButton: newRule, err = NewMappingRuleAxisToButton(config.Config.(configparser.RuleConfigAxisToButton), pDevs, vDevs, base) - case RuleTypeAxisToRelaxis: + case configparser.RuleTypeAxisToRelaxis: newRule, err = NewMappingRuleAxisToRelaxis(config.Config.(configparser.RuleConfigAxisToRelaxis), pDevs, vDevs, base) - case RuleTypeModeSelect: + case configparser.RuleTypeModeSelect: newRule, err = NewMappingRuleModeSelect(config.Config.(configparser.RuleConfigModeSelect), pDevs, modes, base) default: + // Shouldn't actually be possible to get here... err = fmt.Errorf("bad rule type '%s' for rule '%s'", config.Type, config.Name) } diff --git a/internal/mappingrules/variables.go b/internal/mappingrules/variables.go deleted file mode 100644 index d9a171b..0000000 --- a/internal/mappingrules/variables.go +++ /dev/null @@ -1,12 +0,0 @@ -package mappingrules - -const ( - RuleTypeButton = "button" - RuleTypeButtonCombo = "button-combo" - RuleTypeButtonLatched = "button-latched" - RuleTypeAxis = "axis" - RuleTypeAxisCombined = "axis-combined" - RuleTypeAxisToButton = "axis-to-button" - RuleTypeAxisToRelaxis = "axis-to-relaxis" - RuleTypeModeSelect = "mode-select" -)