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 }