Implement Axis targets.
This commit is contained in:
parent
ff38db6596
commit
2f7e11e8a2
6 changed files with 79 additions and 35 deletions
1
go.mod
1
go.mod
|
@ -6,6 +6,7 @@ require (
|
|||
github.com/goccy/go-yaml v1.18.0
|
||||
github.com/holoplot/go-evdev v0.0.0-20240306072622-217e18f17db1
|
||||
github.com/stretchr/testify v1.10.0
|
||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b
|
||||
)
|
||||
|
||||
require (
|
||||
|
|
2
go.sum
2
go.sum
|
@ -8,6 +8,8 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
|
|||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=
|
||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
|
|
|
@ -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,9 @@ 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 makeRuleTargetModeSelect(targetConfig RuleTargetConfig, allModes []string) (*mappingrules.RuleTargetModeSelect, error) {
|
||||
|
|
15
internal/mappingrules/math.go
Normal file
15
internal/mappingrules/math.go
Normal file
|
@ -0,0 +1,15 @@
|
|||
package mappingrules
|
||||
|
||||
import (
|
||||
"golang.org/x/exp/constraints"
|
||||
)
|
||||
|
||||
func AbsInt[T constraints.Integer](value T) T {
|
||||
return max(value, -value)
|
||||
}
|
||||
|
||||
// LerpInt linearly interpolates between two integer values using
|
||||
// a float64 index value
|
||||
func LerpInt[T constraints.Integer](min, max T, t float64) T {
|
||||
return T((1-t)*float64(min) + t*float64(max))
|
||||
}
|
|
@ -1,6 +1,9 @@
|
|||
package mappingrules
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/holoplot/go-evdev"
|
||||
)
|
||||
|
||||
|
@ -11,51 +14,72 @@ type RuleTargetAxis struct {
|
|||
Inverted bool
|
||||
DeadzoneStart int32
|
||||
DeadzoneEnd int32
|
||||
Sensitivity float64
|
||||
Sensitivity float64 // TODO: is this even a value that makes sense?
|
||||
axisSize int32
|
||||
deadzoneSize int32
|
||||
}
|
||||
|
||||
const (
|
||||
MinAxisValue = int32(-32768)
|
||||
MaxAxisValue = int32(32767)
|
||||
)
|
||||
|
||||
func NewRuleTargetAxis(device_name string,
|
||||
device *evdev.InputDevice,
|
||||
axis evdev.EvCode,
|
||||
inverted bool,
|
||||
deadzone_start int32,
|
||||
deadzone_end int32,
|
||||
sensitivity float64) *RuleTargetAxis {
|
||||
deadzoneStart int32,
|
||||
deadzoneEnd int32) (*RuleTargetAxis, error) {
|
||||
|
||||
info, err := device.AbsInfos()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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: deadzone_start,
|
||||
DeadzoneEnd: deadzone_end,
|
||||
Sensitivity: sensitivity,
|
||||
}
|
||||
DeadzoneStart: deadzoneStart,
|
||||
DeadzoneEnd: deadzoneEnd,
|
||||
deadzoneSize: deadzoneSize,
|
||||
axisSize: axisSize,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// TODO: lots of fixes and decisions to make here. Should we normalize all axes to the same range?
|
||||
// How do we handle deadzones in light of that?
|
||||
// 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 {
|
||||
if !target.Inverted {
|
||||
return value
|
||||
axisStrength := float64(value-target.deadzoneSize) / float64(target.axisSize)
|
||||
if target.Inverted {
|
||||
axisStrength = 1.0 - axisStrength
|
||||
}
|
||||
|
||||
axisRange := target.DeadzoneEnd - target.DeadzoneStart
|
||||
axisMid := target.DeadzoneEnd - axisRange/2
|
||||
delta := value - axisMid
|
||||
if delta < 0 {
|
||||
delta = -delta
|
||||
}
|
||||
|
||||
if value < axisMid {
|
||||
return axisMid + delta
|
||||
} else if value > axisMid {
|
||||
return axisMid - delta
|
||||
}
|
||||
|
||||
// If we reach here, we're either exactly at the midpoint or something
|
||||
// strange has happened. Either way, just return the value.
|
||||
return value
|
||||
normalizedValue := LerpInt(MinAxisValue, MaxAxisValue, axisStrength)
|
||||
return normalizedValue
|
||||
}
|
||||
|
||||
func (target *RuleTargetAxis) CreateEvent(value int32, mode *string) *evdev.InputEvent {
|
||||
|
@ -71,5 +95,6 @@ func (target *RuleTargetAxis) CreateEvent(value int32, mode *string) *evdev.Inpu
|
|||
func (target *RuleTargetAxis) MatchEvent(device *evdev.InputDevice, event *evdev.InputEvent) bool {
|
||||
return device == target.Device &&
|
||||
event.Type == evdev.EV_ABS &&
|
||||
event.Code == target.Axis
|
||||
event.Code == target.Axis &&
|
||||
(event.Value <= target.DeadzoneStart || event.Value >= target.DeadzoneEnd)
|
||||
}
|
||||
|
|
|
@ -9,13 +9,13 @@ type RuleTargetButton struct {
|
|||
Inverted bool
|
||||
}
|
||||
|
||||
func NewRuleTargetButton(device_name string, device *evdev.InputDevice, code evdev.EvCode, inverted bool) *RuleTargetButton {
|
||||
func NewRuleTargetButton(device_name string, device *evdev.InputDevice, code evdev.EvCode, inverted bool) (*RuleTargetButton, error) {
|
||||
return &RuleTargetButton{
|
||||
DeviceName: device_name,
|
||||
Device: device,
|
||||
Button: code,
|
||||
Inverted: inverted,
|
||||
}
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (target *RuleTargetButton) NormalizeValue(value int32) int32 {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue