// These types comprise the YAML schema for configuring Joyful. // The config files will be combined and then unmarshalled into this package config import "fmt" type Config struct { 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"` Preset string `yaml:"preset,omitempty"` NumButtons int `yaml:"num_buttons,omitempty"` NumAxes int `yaml:"num_axes,omitempty"` NumRelativeAxes int `yaml:"num_rel_axes"` Buttons []string `yaml:"buttons,omitempty"` Axes []string `yaml:"axes,omitempty"` RelativeAxes []string `yaml:"rel_axes,omitempty"` } type RuleConfigButton struct { Input RuleTargetConfigButton Output RuleTargetConfigButton } 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 *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 }