Implement axis targets, axis -> button and axis -> relative axis mappings. (#1)

Co-authored-by: Anna Rose Wiggins <annabunches@gmail.com>
Co-committed-by: Anna Rose Wiggins <annabunches@gmail.com>
This commit is contained in:
Anna Rose Wiggins 2025-07-15 19:55:19 +00:00 committed by Anna Rose Wiggins
parent ff38db6596
commit e617a6eda6
25 changed files with 903 additions and 130 deletions

View file

@ -35,6 +35,7 @@ func (parser *ConfigParser) CreateVirtualDevices() map[string]*evdev.InputDevice
map[evdev.EvType][]evdev.EvCode{
evdev.EV_KEY: makeButtons(int(deviceConfig.Buttons)),
evdev.EV_ABS: makeAxes(int(deviceConfig.Axes)),
evdev.EV_REL: makeRelativeAxes(deviceConfig.RelativeAxes),
},
)
@ -116,3 +117,20 @@ func makeAxes(numAxes int) []evdev.EvCode {
return axes
}
func makeRelativeAxes(axes []string) []evdev.EvCode {
codes := make([]evdev.EvCode, 0)
for _, axis := range axes {
code, ok := evdev.RELFromString[axis]
if !ok {
logger.Logf("Relative axis '%s' invalid. Skipping.", axis)
continue
}
codes = append(codes, code)
}
return codes
}

View file

@ -24,7 +24,7 @@ func makeRuleTargetButton(targetConfig RuleTargetConfig, devs map[string]*evdev.
device,
eventCode,
targetConfig.Inverted,
), nil
)
}
func makeRuleTargetAxis(targetConfig RuleTargetConfig, devs map[string]*evdev.InputDevice) (*mappingrules.RuleTargetAxis, error) {
@ -43,8 +43,28 @@ func makeRuleTargetAxis(targetConfig RuleTargetConfig, devs map[string]*evdev.In
device,
eventCode,
targetConfig.Inverted,
0, 0, 0, // TODO: replace these with real values
), nil
targetConfig.DeadzoneStart,
targetConfig.DeadzoneEnd,
)
}
func makeRuleTargetRelaxis(targetConfig RuleTargetConfig, devs map[string]*evdev.InputDevice) (*mappingrules.RuleTargetRelaxis, error) {
device, ok := devs[targetConfig.Device]
if !ok {
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)
}
return mappingrules.NewRuleTargetRelaxis(
targetConfig.Device,
device,
eventCode,
targetConfig.Inverted,
)
}
func makeRuleTargetModeSelect(targetConfig RuleTargetConfig, allModes []string) (*mappingrules.RuleTargetModeSelect, error) {

View file

@ -1,7 +1,6 @@
package config
import (
"errors"
"fmt"
"strings"
@ -41,6 +40,8 @@ func (parser *ConfigParser) BuildRules(pDevs map[string]*evdev.InputDevice, vDev
newRule, err = makeMappingRuleAxis(ruleConfig, pDevs, vDevs, base)
case RuleTypeAxisToButton:
newRule, err = makeMappingRuleAxisToButton(ruleConfig, pDevs, vDevs, base)
case RuleTypeAxisToRelaxis:
newRule, err = makeMappingRuleAxisToRelaxis(ruleConfig, pDevs, vDevs, base)
case RuleTypeModeSelect:
newRule, err = makeMappingRuleModeSelect(ruleConfig, pDevs, modes, base)
default:
@ -134,13 +135,44 @@ func makeMappingRuleAxis(ruleConfig RuleConfig,
return mappingrules.NewMappingRuleAxis(base, input, output), nil
}
// STUB
func makeMappingRuleAxisToButton(ruleConfig RuleConfig,
pDevs map[string]*evdev.InputDevice,
vDevs map[string]*evdev.InputDevice,
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleAxisToButton, error) {
return nil, errors.New("stub: makeMappingRuleAxisToButton")
input, err := makeRuleTargetAxis(ruleConfig.Input, pDevs)
if err != nil {
return nil, err
}
output, err := makeRuleTargetButton(ruleConfig.Output, vDevs)
if err != nil {
return nil, err
}
return mappingrules.NewMappingRuleAxisToButton(base, input, output, ruleConfig.RepeatRateMin, ruleConfig.RepeatRateMax), nil
}
func makeMappingRuleAxisToRelaxis(ruleConfig RuleConfig,
pDevs map[string]*evdev.InputDevice,
vDevs map[string]*evdev.InputDevice,
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleAxisToRelaxis, error) {
input, err := makeRuleTargetAxis(ruleConfig.Input, pDevs)
if err != nil {
return nil, err
}
output, err := makeRuleTargetRelaxis(ruleConfig.Output, vDevs)
if err != nil {
return nil, err
}
return mappingrules.NewMappingRuleAxisToRelaxis(base,
input, output,
ruleConfig.RepeatRateMin,
ruleConfig.RepeatRateMax,
ruleConfig.Increment), nil
}
func makeMappingRuleModeSelect(ruleConfig RuleConfig,

View file

@ -1,5 +1,11 @@
// 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
@ -10,29 +16,33 @@ type Config struct {
}
type DeviceConfig struct {
Name string `yaml:"name"`
Type string `yaml:"type"`
DeviceName string `yaml:"device_name,omitempty"`
Uuid string `yaml:"uuid,omitempty"`
Buttons int `yaml:"buttons,omitempty"`
Axes int `yaml:"axes,omitempty"`
Name string `yaml:"name"`
Type string `yaml:"type"`
DeviceName string `yaml:"device_name,omitempty"`
Uuid string `yaml:"uuid,omitempty"`
Buttons int `yaml:"buttons,omitempty"`
Axes int `yaml:"axes,omitempty"`
RelativeAxes []string `yaml:"rel_axes,omitempty"`
}
type RuleConfig struct {
Name string `yaml:"name,omitempty"`
Type string `yaml:"type"`
Input RuleTargetConfig `yaml:"input,omitempty"`
Inputs []RuleTargetConfig `yaml:"inputs,omitempty"`
Output RuleTargetConfig `yaml:"output"`
Modes []string `yaml:"modes,omitempty"`
Name string `yaml:"name,omitempty"`
Type string `yaml:"type"`
Input RuleTargetConfig `yaml:"input,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 RuleTargetConfig struct {
Device string `yaml:"device,omitempty"`
Button string `yaml:"button,omitempty"`
Axis string `yaml:"axis,omitempty"`
DeadzoneStart int32 `yaml:"axis_start,omitempty"`
DeadzoneEnd int32 `yaml:"axis_end,omitempty"`
DeadzoneStart int32 `yaml:"deadzone_start,omitempty"`
DeadzoneEnd int32 `yaml:"deadzone_end,omitempty"`
Inverted bool `yaml:"inverted,omitempty"`
Modes []string `yaml:"modes,omitempty"`
}

View file

@ -8,12 +8,13 @@ const (
DeviceTypePhysical = "physical"
DeviceTypeVirtual = "virtual"
RuleTypeButton = "button"
RuleTypeButtonCombo = "button-combo"
RuleTypeLatched = "button-latched"
RuleTypeAxis = "axis"
RuleTypeModeSelect = "mode-select"
RuleTypeAxisToButton = "axis-to-button"
RuleTypeButton = "button"
RuleTypeButtonCombo = "button-combo"
RuleTypeLatched = "button-latched"
RuleTypeAxis = "axis"
RuleTypeModeSelect = "mode-select"
RuleTypeAxisToButton = "axis-to-button"
RuleTypeAxisToRelaxis = "axis-to-relaxis"
)
var (