Move rule target builders into the correct locations.

This commit is contained in:
Anna Rose Wiggins 2025-08-11 20:53:01 -04:00
parent 9e4062ba30
commit 33b62496a3
17 changed files with 198 additions and 194 deletions

View file

@ -1,156 +0,0 @@
package mappingrules
import (
"errors"
"fmt"
"slices"
"git.annabunches.net/annabunches/joyful/internal/configparser"
"git.annabunches.net/annabunches/joyful/internal/eventcodes"
"github.com/holoplot/go-evdev"
)
func makeRuleTargetButton(targetConfig configparser.RuleTargetConfigButton, devs map[string]Device) (*RuleTargetButton, error) {
device, ok := devs[targetConfig.Device]
if !ok {
return nil, fmt.Errorf("non-existent device '%s'", targetConfig.Device)
}
eventCode, err := eventcodes.ParseCodeButton(targetConfig.Button)
if err != nil {
return nil, err
}
return NewRuleTargetButton(
targetConfig.Device,
device,
eventCode,
targetConfig.Inverted,
)
}
func makeRuleTargetAxis(targetConfig configparser.RuleTargetConfigAxis, devs map[string]Device) (*RuleTargetAxis, error) {
device, ok := devs[targetConfig.Device]
if !ok {
return nil, fmt.Errorf("non-existent device '%s'", targetConfig.Device)
}
if targetConfig.DeadzoneEnd < targetConfig.DeadzoneStart {
return nil, errors.New("deadzone_end must be greater than deadzone_start")
}
eventCode, err := eventcodes.ParseCode(targetConfig.Axis, eventcodes.CodePrefixAxis)
if err != nil {
return nil, err
}
deadzoneStart, deadzoneEnd, err := calculateDeadzones(targetConfig, device, eventCode)
if err != nil {
return nil, err
}
return NewRuleTargetAxis(
targetConfig.Device,
device,
eventCode,
targetConfig.Inverted,
deadzoneStart,
deadzoneEnd,
)
}
func makeRuleTargetRelaxis(targetConfig configparser.RuleTargetConfigRelaxis, devs map[string]Device) (*RuleTargetRelaxis, error) {
device, ok := devs[targetConfig.Device]
if !ok {
return nil, fmt.Errorf("non-existent device '%s'", targetConfig.Device)
}
eventCode, err := eventcodes.ParseCode(targetConfig.Axis, eventcodes.CodePrefixRelaxis)
if err != nil {
return nil, err
}
return NewRuleTargetRelaxis(
targetConfig.Device,
device,
eventCode,
)
}
func makeRuleTargetModeSelect(targetConfig configparser.RuleTargetConfigModeSelect, allModes []string) (*RuleTargetModeSelect, error) {
if ok := validateModes(targetConfig.Modes, allModes); !ok {
return nil, errors.New("undefined mode in mode select list")
}
return NewRuleTargetModeSelect(targetConfig.Modes)
}
// calculateDeadzones produces the deadzone start and end values in absolute terms
func calculateDeadzones(targetConfig configparser.RuleTargetConfigAxis, device Device, axis evdev.EvCode) (int32, int32, error) {
var deadzoneStart, deadzoneEnd int32
deadzoneStart = 0
deadzoneEnd = 0
if targetConfig.DeadzoneStart != 0 || targetConfig.DeadzoneEnd != 0 {
return targetConfig.DeadzoneStart, targetConfig.DeadzoneEnd, nil
}
var min, max int32
absInfoMap, err := device.AbsInfos()
if err != nil {
min = AxisValueMin
max = AxisValueMax
} else {
absInfo := absInfoMap[axis]
min = absInfo.Minimum
max = absInfo.Maximum
}
if targetConfig.DeadzoneCenter < min || targetConfig.DeadzoneCenter > max {
return 0, 0, fmt.Errorf("deadzone_center '%d' is out of bounds", targetConfig.DeadzoneCenter)
}
switch {
case targetConfig.DeadzoneSize != 0:
deadzoneStart = targetConfig.DeadzoneCenter - targetConfig.DeadzoneSize/2
deadzoneEnd = targetConfig.DeadzoneCenter + targetConfig.DeadzoneSize/2
case targetConfig.DeadzoneSizePercent != 0:
deadzoneSize := (max - min) / targetConfig.DeadzoneSizePercent
deadzoneStart = targetConfig.DeadzoneCenter - deadzoneSize/2
deadzoneEnd = targetConfig.DeadzoneCenter + deadzoneSize/2
}
deadzoneStart, deadzoneEnd = clampAndShift(deadzoneStart, deadzoneEnd, min, max)
return deadzoneStart, deadzoneEnd, nil
}
func clampAndShift(start, end, min, max int32) (int32, int32) {
if start < min {
end += min - start
start = min
}
if end > max {
start -= end - max
end = max
}
return start, end
}
// validateModes checks the provided modes against a larger subset of modes (usually all defined ones)
// and returns false if any of the modes are not defined.
func validateModes(modes []string, allModes []string) bool {
if len(modes) == 0 {
return true
}
for _, mode := range modes {
if !slices.Contains(allModes, mode) {
return false
}
}
return true
}

View file

@ -53,41 +53,41 @@ func (t *MakeRuleTargetsTests) TestMakeRuleTargetButton() {
t.Run("Standard keycode", func() { t.Run("Standard keycode", func() {
config.Button = "BTN_TRIGGER" config.Button = "BTN_TRIGGER"
rule, err := makeRuleTargetButton(config, t.devs) rule, err := NewRuleTargetButtonFromConfig(config, t.devs)
t.Nil(err) t.Nil(err)
t.EqualValues(evdev.BTN_TRIGGER, rule.Button) t.EqualValues(evdev.BTN_TRIGGER, rule.Button)
}) })
t.Run("Hex code", func() { t.Run("Hex code", func() {
config.Button = "0x2fd" config.Button = "0x2fd"
rule, err := makeRuleTargetButton(config, t.devs) rule, err := NewRuleTargetButtonFromConfig(config, t.devs)
t.Nil(err) t.Nil(err)
t.EqualValues(evdev.EvCode(0x2fd), rule.Button) t.EqualValues(evdev.EvCode(0x2fd), rule.Button)
}) })
t.Run("Index", func() { t.Run("Index", func() {
config.Button = "3" config.Button = "3"
rule, err := makeRuleTargetButton(config, t.devs) rule, err := NewRuleTargetButtonFromConfig(config, t.devs)
t.Nil(err) t.Nil(err)
t.EqualValues(evdev.BTN_TOP, rule.Button) t.EqualValues(evdev.BTN_TOP, rule.Button)
}) })
t.Run("Index too high", func() { t.Run("Index too high", func() {
config.Button = "74" config.Button = "74"
_, err := makeRuleTargetButton(config, t.devs) _, err := NewRuleTargetButtonFromConfig(config, t.devs)
t.NotNil(err) t.NotNil(err)
}) })
t.Run("Un-prefixed keycode", func() { t.Run("Un-prefixed keycode", func() {
config.Button = "pinkie" config.Button = "pinkie"
rule, err := makeRuleTargetButton(config, t.devs) rule, err := NewRuleTargetButtonFromConfig(config, t.devs)
t.Nil(err) t.Nil(err)
t.EqualValues(evdev.BTN_PINKIE, rule.Button) t.EqualValues(evdev.BTN_PINKIE, rule.Button)
}) })
t.Run("Invalid keycode", func() { t.Run("Invalid keycode", func() {
config.Button = "foo" config.Button = "foo"
_, err := makeRuleTargetButton(config, t.devs) _, err := NewRuleTargetButtonFromConfig(config, t.devs)
t.NotNil(err) t.NotNil(err)
}) })
} }
@ -106,7 +106,7 @@ func (t *MakeRuleTargetsTests) TestMakeRuleTargetAxis() {
t.Run(fmt.Sprintf("KeyCode %s", tc.input), func() { t.Run(fmt.Sprintf("KeyCode %s", tc.input), func() {
config := configparser.RuleTargetConfigAxis{Device: "test"} config := configparser.RuleTargetConfigAxis{Device: "test"}
config.Axis = tc.input config.Axis = tc.input
rule, err := makeRuleTargetAxis(config, t.devs) rule, err := NewRuleTargetAxisFromConfig(config, t.devs)
t.Nil(err) t.Nil(err)
t.EqualValues(tc.output, rule.Axis) t.EqualValues(tc.output, rule.Axis)
@ -116,7 +116,7 @@ func (t *MakeRuleTargetsTests) TestMakeRuleTargetAxis() {
t.Run("Invalid code", func() { t.Run("Invalid code", func() {
config := configparser.RuleTargetConfigAxis{Device: "test"} config := configparser.RuleTargetConfigAxis{Device: "test"}
config.Axis = "foo" config.Axis = "foo"
_, err := makeRuleTargetAxis(config, t.devs) _, err := NewRuleTargetAxisFromConfig(config, t.devs)
t.NotNil(err) t.NotNil(err)
}) })
@ -125,7 +125,7 @@ func (t *MakeRuleTargetsTests) TestMakeRuleTargetAxis() {
config.Axis = "x" config.Axis = "x"
config.DeadzoneEnd = 100 config.DeadzoneEnd = 100
config.DeadzoneStart = 1000 config.DeadzoneStart = 1000
_, err := makeRuleTargetAxis(config, t.devs) _, err := NewRuleTargetAxisFromConfig(config, t.devs)
t.NotNil(err) t.NotNil(err)
}) })
@ -148,7 +148,7 @@ func (t *MakeRuleTargetsTests) TestMakeRuleTargetAxis() {
DeadzoneCenter: tc.inCenter, DeadzoneCenter: tc.inCenter,
DeadzoneSize: tc.inSize, DeadzoneSize: tc.inSize,
} }
rule, err := makeRuleTargetAxis(config, t.devs) rule, err := NewRuleTargetAxisFromConfig(config, t.devs)
t.Nil(err) t.Nil(err)
t.Equal(tc.outStart, rule.DeadzoneStart) t.Equal(tc.outStart, rule.DeadzoneStart)
@ -163,7 +163,7 @@ func (t *MakeRuleTargetsTests) TestMakeRuleTargetAxis() {
DeadzoneCenter: 20000, DeadzoneCenter: 20000,
DeadzoneSize: 500, DeadzoneSize: 500,
} }
_, err := makeRuleTargetAxis(config, t.devs) _, err := NewRuleTargetAxisFromConfig(config, t.devs)
t.NotNil(err) t.NotNil(err)
}) })
@ -186,7 +186,7 @@ func (t *MakeRuleTargetsTests) TestMakeRuleTargetAxis() {
DeadzoneCenter: tc.inCenter, DeadzoneCenter: tc.inCenter,
DeadzoneSizePercent: tc.inSizePercent, DeadzoneSizePercent: tc.inSizePercent,
} }
rule, err := makeRuleTargetAxis(config, t.devs) rule, err := NewRuleTargetAxisFromConfig(config, t.devs)
t.Nil(err) t.Nil(err)
t.Equal(tc.outStart, rule.DeadzoneStart) t.Equal(tc.outStart, rule.DeadzoneStart)
@ -201,7 +201,7 @@ func (t *MakeRuleTargetsTests) TestMakeRuleTargetAxis() {
DeadzoneCenter: 20000, DeadzoneCenter: 20000,
DeadzoneSizePercent: 10, DeadzoneSizePercent: 10,
} }
_, err := makeRuleTargetAxis(config, t.devs) _, err := NewRuleTargetAxisFromConfig(config, t.devs)
t.NotNil(err) t.NotNil(err)
}) })
} }
@ -211,34 +211,34 @@ func (t *MakeRuleTargetsTests) TestMakeRuleTargetRelaxis() {
t.Run("Standard keycode", func() { t.Run("Standard keycode", func() {
config.Axis = "REL_WHEEL" config.Axis = "REL_WHEEL"
rule, err := makeRuleTargetRelaxis(config, t.devs) rule, err := NewRuleTargetRelaxisFromConfig(config, t.devs)
t.Nil(err) t.Nil(err)
t.EqualValues(evdev.REL_WHEEL, rule.Axis) t.EqualValues(evdev.REL_WHEEL, rule.Axis)
}) })
t.Run("Hex keycode", func() { t.Run("Hex keycode", func() {
config.Axis = "0x00" config.Axis = "0x00"
rule, err := makeRuleTargetRelaxis(config, t.devs) rule, err := NewRuleTargetRelaxisFromConfig(config, t.devs)
t.Nil(err) t.Nil(err)
t.EqualValues(evdev.REL_X, rule.Axis) t.EqualValues(evdev.REL_X, rule.Axis)
}) })
t.Run("Un-prefixed keycode", func() { t.Run("Un-prefixed keycode", func() {
config.Axis = "wheel" config.Axis = "wheel"
rule, err := makeRuleTargetRelaxis(config, t.devs) rule, err := NewRuleTargetRelaxisFromConfig(config, t.devs)
t.Nil(err) t.Nil(err)
t.EqualValues(evdev.REL_WHEEL, rule.Axis) t.EqualValues(evdev.REL_WHEEL, rule.Axis)
}) })
t.Run("Invalid keycode", func() { t.Run("Invalid keycode", func() {
config.Axis = "foo" config.Axis = "foo"
_, err := makeRuleTargetRelaxis(config, t.devs) _, err := NewRuleTargetRelaxisFromConfig(config, t.devs)
t.NotNil(err) t.NotNil(err)
}) })
t.Run("Incorrect axis type", func() { t.Run("Incorrect axis type", func() {
config.Axis = "ABS_X" config.Axis = "ABS_X"
_, err := makeRuleTargetRelaxis(config, t.devs) _, err := NewRuleTargetRelaxisFromConfig(config, t.devs)
t.NotNil(err) t.NotNil(err)
}) })
} }

View file

@ -3,6 +3,7 @@ package mappingrules
import ( import (
"errors" "errors"
"fmt" "fmt"
"slices"
"strings" "strings"
"git.annabunches.net/annabunches/joyful/internal/configparser" "git.annabunches.net/annabunches/joyful/internal/configparser"
@ -60,3 +61,19 @@ func NewRule(config configparser.RuleConfig, pDevs map[string]Device, vDevs map[
return newRule, nil return newRule, nil
} }
// validateModes checks the provided modes against a larger subset of modes (usually all defined ones)
// and returns false if any of the modes are not defined.
func validateModes(modes []string, allModes []string) bool {
if len(modes) == 0 {
return true
}
for _, mode := range modes {
if !slices.Contains(allModes, mode) {
return false
}
}
return true
}

View file

@ -17,12 +17,12 @@ func NewMappingRuleAxis(ruleConfig configparser.RuleConfigAxis,
vDevs map[string]Device, vDevs map[string]Device,
base MappingRuleBase) (*MappingRuleAxis, error) { base MappingRuleBase) (*MappingRuleAxis, error) {
input, err := makeRuleTargetAxis(ruleConfig.Input, pDevs) input, err := NewRuleTargetAxisFromConfig(ruleConfig.Input, pDevs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
output, err := makeRuleTargetAxis(ruleConfig.Output, vDevs) output, err := NewRuleTargetAxisFromConfig(ruleConfig.Output, vDevs)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -18,17 +18,17 @@ func NewMappingRuleAxisCombined(ruleConfig configparser.RuleConfigAxisCombined,
vDevs map[string]Device, vDevs map[string]Device,
base MappingRuleBase) (*MappingRuleAxisCombined, error) { base MappingRuleBase) (*MappingRuleAxisCombined, error) {
inputLower, err := makeRuleTargetAxis(ruleConfig.InputLower, pDevs) inputLower, err := NewRuleTargetAxisFromConfig(ruleConfig.InputLower, pDevs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
inputUpper, err := makeRuleTargetAxis(ruleConfig.InputUpper, pDevs) inputUpper, err := NewRuleTargetAxisFromConfig(ruleConfig.InputUpper, pDevs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
output, err := makeRuleTargetAxis(ruleConfig.Output, vDevs) output, err := NewRuleTargetAxisFromConfig(ruleConfig.Output, vDevs)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -59,6 +59,7 @@ func (t *MappingRuleAxisCombinedTests) TearDownSubTest() {
t.inputDevice.Reset() t.inputDevice.Reset()
} }
// TODO: this test sucks
func (t *MappingRuleAxisCombinedTests) TestNewMappingRuleAxisCombined() { func (t *MappingRuleAxisCombinedTests) TestNewMappingRuleAxisCombined() {
t.inputDevice.Stub("AbsInfos").Return(map[evdev.EvCode]evdev.AbsInfo{ t.inputDevice.Stub("AbsInfos").Return(map[evdev.EvCode]evdev.AbsInfo{
evdev.ABS_X: {Minimum: 0, Maximum: 10000}, evdev.ABS_X: {Minimum: 0, Maximum: 10000},

View file

@ -29,12 +29,12 @@ func NewMappingRuleAxisToButton(ruleConfig configparser.RuleConfigAxisToButton,
vDevs map[string]Device, vDevs map[string]Device,
base MappingRuleBase) (*MappingRuleAxisToButton, error) { base MappingRuleBase) (*MappingRuleAxisToButton, error) {
input, err := makeRuleTargetAxis(ruleConfig.Input, pDevs) input, err := NewRuleTargetAxisFromConfig(ruleConfig.Input, pDevs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
output, err := makeRuleTargetButton(ruleConfig.Output, vDevs) output, err := NewRuleTargetButtonFromConfig(ruleConfig.Output, vDevs)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -29,12 +29,12 @@ func NewMappingRuleAxisToRelaxis(ruleConfig configparser.RuleConfigAxisToRelaxis
vDevs map[string]Device, vDevs map[string]Device,
base MappingRuleBase) (*MappingRuleAxisToRelaxis, error) { base MappingRuleBase) (*MappingRuleAxisToRelaxis, error) {
input, err := makeRuleTargetAxis(ruleConfig.Input, pDevs) input, err := NewRuleTargetAxisFromConfig(ruleConfig.Input, pDevs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
output, err := makeRuleTargetRelaxis(ruleConfig.Output, vDevs) output, err := NewRuleTargetRelaxisFromConfig(ruleConfig.Output, vDevs)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -17,12 +17,12 @@ func NewMappingRuleButton(ruleConfig configparser.RuleConfigButton,
vDevs map[string]Device, vDevs map[string]Device,
base MappingRuleBase) (*MappingRuleButton, error) { base MappingRuleBase) (*MappingRuleButton, error) {
input, err := makeRuleTargetButton(ruleConfig.Input, pDevs) input, err := NewRuleTargetButtonFromConfig(ruleConfig.Input, pDevs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
output, err := makeRuleTargetButton(ruleConfig.Output, vDevs) output, err := NewRuleTargetButtonFromConfig(ruleConfig.Output, vDevs)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -20,14 +20,14 @@ func NewMappingRuleButtonCombo(ruleConfig configparser.RuleConfigButtonCombo,
inputs := make([]*RuleTargetButton, 0) inputs := make([]*RuleTargetButton, 0)
for _, inputConfig := range ruleConfig.Inputs { for _, inputConfig := range ruleConfig.Inputs {
input, err := makeRuleTargetButton(inputConfig, pDevs) input, err := NewRuleTargetButtonFromConfig(inputConfig, pDevs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
inputs = append(inputs, input) inputs = append(inputs, input)
} }
output, err := makeRuleTargetButton(ruleConfig.Output, vDevs) output, err := NewRuleTargetButtonFromConfig(ruleConfig.Output, vDevs)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -17,12 +17,12 @@ func NewMappingRuleButtonLatched(ruleConfig configparser.RuleConfigButtonLatched
vDevs map[string]Device, vDevs map[string]Device,
base MappingRuleBase) (*MappingRuleButtonLatched, error) { base MappingRuleBase) (*MappingRuleButtonLatched, error) {
input, err := makeRuleTargetButton(ruleConfig.Input, pDevs) input, err := NewRuleTargetButtonFromConfig(ruleConfig.Input, pDevs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
output, err := makeRuleTargetButton(ruleConfig.Output, vDevs) output, err := NewRuleTargetButtonFromConfig(ruleConfig.Output, vDevs)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -16,12 +16,12 @@ func NewMappingRuleModeSelect(ruleConfig configparser.RuleConfigModeSelect,
modes []string, modes []string,
base MappingRuleBase) (*MappingRuleModeSelect, error) { base MappingRuleBase) (*MappingRuleModeSelect, error) {
input, err := makeRuleTargetButton(ruleConfig.Input, pDevs) input, err := NewRuleTargetButtonFromConfig(ruleConfig.Input, pDevs)
if err != nil { if err != nil {
return nil, err return nil, err
} }
output, err := makeRuleTargetModeSelect(ruleConfig.Output, modes) output, err := NewRuleTargetModeSelectFromConfig(ruleConfig.Output, modes)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View file

@ -28,3 +28,16 @@ func Clamp[T Numeric](value, min, max T) T {
} }
return value return value
} }
func clampAndShift(start, end, min, max int32) (int32, int32) {
if start < min {
end += min - start
start = min
}
if end > max {
start -= end - max
end = max
}
return start, end
}

View file

@ -4,6 +4,8 @@ import (
"errors" "errors"
"fmt" "fmt"
"git.annabunches.net/annabunches/joyful/internal/configparser"
"git.annabunches.net/annabunches/joyful/internal/eventcodes"
"github.com/holoplot/go-evdev" "github.com/holoplot/go-evdev"
) )
@ -20,6 +22,77 @@ type RuleTargetAxis struct {
deadzoneSize int32 deadzoneSize int32
} }
func NewRuleTargetAxisFromConfig(targetConfig configparser.RuleTargetConfigAxis, devs map[string]Device) (*RuleTargetAxis, error) {
device, ok := devs[targetConfig.Device]
if !ok {
return nil, fmt.Errorf("non-existent device '%s'", targetConfig.Device)
}
if targetConfig.DeadzoneEnd < targetConfig.DeadzoneStart {
return nil, errors.New("deadzone_end must be greater than deadzone_start")
}
eventCode, err := eventcodes.ParseCode(targetConfig.Axis, eventcodes.CodePrefixAxis)
if err != nil {
return nil, err
}
deadzoneStart, deadzoneEnd, err := calculateDeadzones(targetConfig, device, eventCode)
if err != nil {
return nil, err
}
return NewRuleTargetAxis(
targetConfig.Device,
device,
eventCode,
targetConfig.Inverted,
deadzoneStart,
deadzoneEnd,
)
}
// calculateDeadzones produces the deadzone start and end values in absolute terms
func calculateDeadzones(targetConfig configparser.RuleTargetConfigAxis, device Device, axis evdev.EvCode) (int32, int32, error) {
var deadzoneStart, deadzoneEnd int32
deadzoneStart = 0
deadzoneEnd = 0
if targetConfig.DeadzoneStart != 0 || targetConfig.DeadzoneEnd != 0 {
return targetConfig.DeadzoneStart, targetConfig.DeadzoneEnd, nil
}
var min, max int32
absInfoMap, err := device.AbsInfos()
if err != nil {
min = AxisValueMin
max = AxisValueMax
} else {
absInfo := absInfoMap[axis]
min = absInfo.Minimum
max = absInfo.Maximum
}
if targetConfig.DeadzoneCenter < min || targetConfig.DeadzoneCenter > max {
return 0, 0, fmt.Errorf("deadzone_center '%d' is out of bounds", targetConfig.DeadzoneCenter)
}
switch {
case targetConfig.DeadzoneSize != 0:
deadzoneStart = targetConfig.DeadzoneCenter - targetConfig.DeadzoneSize/2
deadzoneEnd = targetConfig.DeadzoneCenter + targetConfig.DeadzoneSize/2
case targetConfig.DeadzoneSizePercent != 0:
deadzoneSize := (max - min) / targetConfig.DeadzoneSizePercent
deadzoneStart = targetConfig.DeadzoneCenter - deadzoneSize/2
deadzoneEnd = targetConfig.DeadzoneCenter + deadzoneSize/2
}
deadzoneStart, deadzoneEnd = clampAndShift(deadzoneStart, deadzoneEnd, min, max)
return deadzoneStart, deadzoneEnd, nil
}
func NewRuleTargetAxis(device_name string, func NewRuleTargetAxis(device_name string,
device Device, device Device,
axis evdev.EvCode, axis evdev.EvCode,

View file

@ -1,6 +1,12 @@
package mappingrules package mappingrules
import "github.com/holoplot/go-evdev" import (
"fmt"
"git.annabunches.net/annabunches/joyful/internal/configparser"
"git.annabunches.net/annabunches/joyful/internal/eventcodes"
"github.com/holoplot/go-evdev"
)
type RuleTargetButton struct { type RuleTargetButton struct {
DeviceName string DeviceName string
@ -9,6 +15,25 @@ type RuleTargetButton struct {
Inverted bool Inverted bool
} }
func NewRuleTargetButtonFromConfig(targetConfig configparser.RuleTargetConfigButton, devs map[string]Device) (*RuleTargetButton, error) {
device, ok := devs[targetConfig.Device]
if !ok {
return nil, fmt.Errorf("non-existent device '%s'", targetConfig.Device)
}
eventCode, err := eventcodes.ParseCodeButton(targetConfig.Button)
if err != nil {
return nil, err
}
return NewRuleTargetButton(
targetConfig.Device,
device,
eventCode,
targetConfig.Inverted,
)
}
func NewRuleTargetButton(device_name string, device Device, code evdev.EvCode, inverted bool) (*RuleTargetButton, error) { func NewRuleTargetButton(device_name string, device Device, code evdev.EvCode, inverted bool) (*RuleTargetButton, error) {
return &RuleTargetButton{ return &RuleTargetButton{
DeviceName: device_name, DeviceName: device_name,

View file

@ -4,6 +4,7 @@ import (
"errors" "errors"
"slices" "slices"
"git.annabunches.net/annabunches/joyful/internal/configparser"
"git.annabunches.net/annabunches/joyful/internal/logger" "git.annabunches.net/annabunches/joyful/internal/logger"
"github.com/holoplot/go-evdev" "github.com/holoplot/go-evdev"
) )
@ -12,6 +13,14 @@ type RuleTargetModeSelect struct {
Modes []string Modes []string
} }
func NewRuleTargetModeSelectFromConfig(targetConfig configparser.RuleTargetConfigModeSelect, allModes []string) (*RuleTargetModeSelect, error) {
if ok := validateModes(targetConfig.Modes, allModes); !ok {
return nil, errors.New("undefined mode in mode select list")
}
return NewRuleTargetModeSelect(targetConfig.Modes)
}
func NewRuleTargetModeSelect(modes []string) (*RuleTargetModeSelect, error) { func NewRuleTargetModeSelect(modes []string) (*RuleTargetModeSelect, error) {
if len(modes) == 0 { if len(modes) == 0 {
return nil, errors.New("cannot create RuleTargetModeSelect: mode list is empty") return nil, errors.New("cannot create RuleTargetModeSelect: mode list is empty")

View file

@ -1,6 +1,10 @@
package mappingrules package mappingrules
import ( import (
"fmt"
"git.annabunches.net/annabunches/joyful/internal/configparser"
"git.annabunches.net/annabunches/joyful/internal/eventcodes"
"github.com/holoplot/go-evdev" "github.com/holoplot/go-evdev"
) )
@ -10,12 +14,30 @@ type RuleTargetRelaxis struct {
Axis evdev.EvCode Axis evdev.EvCode
} }
func NewRuleTargetRelaxis(device_name string, func NewRuleTargetRelaxisFromConfig(targetConfig configparser.RuleTargetConfigRelaxis, devs map[string]Device) (*RuleTargetRelaxis, error) {
device, ok := devs[targetConfig.Device]
if !ok {
return nil, fmt.Errorf("non-existent device '%s'", targetConfig.Device)
}
eventCode, err := eventcodes.ParseCode(targetConfig.Axis, eventcodes.CodePrefixRelaxis)
if err != nil {
return nil, err
}
return NewRuleTargetRelaxis(
targetConfig.Device,
device,
eventCode,
)
}
func NewRuleTargetRelaxis(deviceName string,
device Device, device Device,
axis evdev.EvCode) (*RuleTargetRelaxis, error) { axis evdev.EvCode) (*RuleTargetRelaxis, error) {
return &RuleTargetRelaxis{ return &RuleTargetRelaxis{
DeviceName: device_name, DeviceName: deviceName,
Device: device, Device: device,
Axis: axis, Axis: axis,
}, nil }, nil