package mappingrules import ( "errors" "fmt" "github.com/holoplot/go-evdev" ) type RuleTargetAxis struct { DeviceName string Device RuleTargetDevice Axis evdev.EvCode Inverted bool DeadzoneStart int32 DeadzoneEnd int32 axisSize int32 deadzoneSize int32 } const ( AxisValueMin = int32(-32768) AxisValueMax = int32(32767) ) func NewRuleTargetAxis(device_name string, device RuleTargetDevice, axis evdev.EvCode, inverted bool, deadzoneStart int32, deadzoneEnd int32) (*RuleTargetAxis, error) { info, err := device.AbsInfos() if err != nil { // If we can't get AbsInfo (for example, we're a virtual device) // we set the bounds to the maximum allowable info = map[evdev.EvCode]evdev.AbsInfo{ axis: { Minimum: AxisValueMin, Maximum: AxisValueMax, }, } } if _, ok := info[axis]; !ok { return nil, fmt.Errorf("device does not support axis %v", axis) } if deadzoneStart > deadzoneEnd { return nil, errors.New("deadzone_end must be a higher value than deadzone_start") } deadzoneSize := AbsInt(deadzoneEnd - deadzoneStart) // Our output range is limited to 16 bits, but we represent values internally with 32 bits. // As a result, we shouldn't need to worry about integer overruns axisSize := info[axis].Maximum - info[axis].Minimum - deadzoneSize if axisSize == 0 { return nil, errors.New("axis has size 0") } return &RuleTargetAxis{ DeviceName: device_name, Device: device, Axis: axis, Inverted: inverted, DeadzoneStart: deadzoneStart, DeadzoneEnd: deadzoneEnd, deadzoneSize: deadzoneSize, axisSize: axisSize, }, nil } // NormalizeValue takes a raw input value and converts it to a value suitable for output. // // Axis inputs are normalized to the full signed int32 range to match the virtual device's axis // characteristics. // // Typically this function is called after RuleTargetAxis.MatchEvent, which checks whether we are // in the deadzone, among other things. func (target *RuleTargetAxis) NormalizeValue(value int32) int32 { axisStrength := float64(value-target.deadzoneSize) / float64(target.axisSize) if target.Inverted { axisStrength = 1.0 - axisStrength } normalizedValue := LerpInt(AxisValueMin, AxisValueMax, axisStrength) return normalizedValue } func (target *RuleTargetAxis) CreateEvent(value int32, mode *string) *evdev.InputEvent { value = ClampInt(value, AxisValueMin, AxisValueMax) return &evdev.InputEvent{ Type: evdev.EV_ABS, Code: target.Axis, Value: value, } } func (target *RuleTargetAxis) MatchEvent(device RuleTargetDevice, event *evdev.InputEvent) bool { return device == target.Device && event.Type == evdev.EV_ABS && event.Code == target.Axis && (event.Value < target.DeadzoneStart || event.Value > target.DeadzoneEnd) }