Better deadzones (#19)
Reviewed-on: #19 Co-authored-by: Anna Rose Wiggins <annabunches@gmail.com> Co-committed-by: Anna Rose Wiggins <annabunches@gmail.com>
This commit is contained in:
parent
8a903e0703
commit
2650159a81
12 changed files with 273 additions and 194 deletions
|
|
@ -92,8 +92,9 @@ rules:
|
||||||
input:
|
input:
|
||||||
device: left-stick
|
device: left-stick
|
||||||
axis: RY
|
axis: RY
|
||||||
deadzone_start: 0
|
deadzones:
|
||||||
deadzone_end: 30500
|
- start: 0
|
||||||
|
end: 30500
|
||||||
output:
|
output:
|
||||||
device: mouse
|
device: mouse
|
||||||
axis: REL_WHEEL
|
axis: REL_WHEEL
|
||||||
|
|
@ -108,8 +109,9 @@ rules:
|
||||||
input:
|
input:
|
||||||
device: left-stick
|
device: left-stick
|
||||||
axis: RY
|
axis: RY
|
||||||
deadzone_start: 29500
|
deadzones:
|
||||||
deadzone_end: 64000
|
- start: 29500
|
||||||
|
end: 64000
|
||||||
inverted: true
|
inverted: true
|
||||||
output:
|
output:
|
||||||
device: mouse
|
device: mouse
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
devices:
|
devices:
|
||||||
- name: primary
|
- name: primary
|
||||||
type: virtual
|
type: Virtual
|
||||||
preset: joystick
|
preset: joystick
|
||||||
- name: secondary
|
- name: secondary
|
||||||
type: virtual
|
type: virtual
|
||||||
|
|
|
||||||
|
|
@ -18,8 +18,9 @@ rules:
|
||||||
input:
|
input:
|
||||||
device: flightstick
|
device: flightstick
|
||||||
# To find reasonable values for your device's deadzones, use the evtest command
|
# To find reasonable values for your device's deadzones, use the evtest command
|
||||||
deadzone_start: 28000
|
deadzones:
|
||||||
deadzone_end: 30000
|
- start: 28000
|
||||||
|
end: 30000
|
||||||
inverted: false
|
inverted: false
|
||||||
axis: ABS_X
|
axis: ABS_X
|
||||||
output:
|
output:
|
||||||
|
|
@ -33,8 +34,9 @@ rules:
|
||||||
# size value. This will create a deadzone that covers a range of deadzone_size,
|
# size value. This will create a deadzone that covers a range of deadzone_size,
|
||||||
# centered on the center value. Note that if your deadzone_center is at the lower or upper end
|
# centered on the center value. Note that if your deadzone_center is at the lower or upper end
|
||||||
# of the axis, the total size will still be as given; the deadzone will be "shifted" into bounds.
|
# of the axis, the total size will still be as given; the deadzone will be "shifted" into bounds.
|
||||||
deadzone_center: 29000
|
deadzones:
|
||||||
deadzone_size: 2000
|
- center: 29000
|
||||||
|
size: 2000
|
||||||
inverted: false
|
inverted: false
|
||||||
axis: Y # The ABS_ prefix is optional
|
axis: Y # The ABS_ prefix is optional
|
||||||
output:
|
output:
|
||||||
|
|
@ -46,8 +48,9 @@ rules:
|
||||||
device: flightstick
|
device: flightstick
|
||||||
# A final way to specify deadzones is to use a size percentage instead of an absolute size.
|
# A final way to specify deadzones is to use a size percentage instead of an absolute size.
|
||||||
# This works exactly like deadzone_size, but calculates a percentage of the axis' total range.
|
# This works exactly like deadzone_size, but calculates a percentage of the axis' total range.
|
||||||
deadzone_center: 29000
|
deadzones:
|
||||||
deadzone_size_percent: 5
|
- center: 29000
|
||||||
|
size_percent: 5
|
||||||
inverted: false
|
inverted: false
|
||||||
axis: Y # The ABS_ prefix is optional
|
axis: Y # The ABS_ prefix is optional
|
||||||
output:
|
output:
|
||||||
|
|
|
||||||
|
|
@ -73,13 +73,17 @@ evtest | grep BTN_
|
||||||
|
|
||||||
**NOTE: For most axis mappings, you probably don't want to specify a deadzone!** Use deadzone configurations in your target game instead. Joyful-configured deadzones are intended to be used in conjunction with the `axis-to-button` and `axis-to-relaxis` input types, or when splitting an axis into multiple outputs. Using them with standard `axis` mappings may result in a loss of fidelity and "stuck" inputs.
|
**NOTE: For most axis mappings, you probably don't want to specify a deadzone!** Use deadzone configurations in your target game instead. Joyful-configured deadzones are intended to be used in conjunction with the `axis-to-button` and `axis-to-relaxis` input types, or when splitting an axis into multiple outputs. Using them with standard `axis` mappings may result in a loss of fidelity and "stuck" inputs.
|
||||||
|
|
||||||
There are three ways to specify deadzones:
|
Axis inputs can define a list of deadzones. Each deadzone can be specified a few ways:
|
||||||
|
|
||||||
* Define `deadzone_start` and `deadzone_end` to explicitly set the deadzone bounds.
|
* Define `start` and `end` to explicitly set the deadzone bounds.
|
||||||
* Define `deadzone_center` and `deadzone_size`; this will create a deadzone of the indicated size centered at the given axis position.
|
* Define `center` and `size`; this will create a deadzone of the indicated size centered at the given axis position.
|
||||||
* Define `deadzone_center` and `deadzone_size_percent` to use a percentage of the total axis size.
|
* Define `center` and `size_percent` to use a percentage of the total axis size.
|
||||||
|
|
||||||
See <examples/ruletypes.yml> for usage examples.
|
In addition, deadzones can set `emit` to `true` and `emit_value` to a value that should be emitted when inside the deadzone.
|
||||||
|
|
||||||
|
**Note**: The `emit_value` is the final output value and should be between -32,768 and 32,767.
|
||||||
|
|
||||||
|
See the <examples/> directory for usage examples.
|
||||||
|
|
||||||
## Modes
|
## Modes
|
||||||
|
|
||||||
|
|
|
||||||
33
internal/configparser/ruletarget.go
Normal file
33
internal/configparser/ruletarget.go
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
package configparser
|
||||||
|
|
||||||
|
type RuleTargetConfigButton struct {
|
||||||
|
Device string
|
||||||
|
Button string
|
||||||
|
Inverted bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type RuleTargetConfigAxis struct {
|
||||||
|
Device string
|
||||||
|
Axis string
|
||||||
|
Inverted bool
|
||||||
|
Deadzones []DeadzoneConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeadzoneConfig struct {
|
||||||
|
Center int32 `yaml:"center,omitempty"`
|
||||||
|
Size int32 `yaml:"size,omitempty"`
|
||||||
|
SizePercent int32 `yaml:"size_percent,omitempty"`
|
||||||
|
Start int32 `yaml:"start,omitempty"`
|
||||||
|
End int32 `yaml:"end,omitempty"`
|
||||||
|
Emit bool `yaml:"emit,omitempty"`
|
||||||
|
Value int32 `yaml:"emit_value,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RuleTargetConfigRelaxis struct {
|
||||||
|
Device string
|
||||||
|
Axis string
|
||||||
|
}
|
||||||
|
|
||||||
|
type RuleTargetConfigModeSelect struct {
|
||||||
|
Modes []string
|
||||||
|
}
|
||||||
|
|
@ -65,29 +65,3 @@ type RuleConfigModeSelect struct {
|
||||||
Input RuleTargetConfigButton
|
Input RuleTargetConfigButton
|
||||||
Output RuleTargetConfigModeSelect
|
Output RuleTargetConfigModeSelect
|
||||||
}
|
}
|
||||||
|
|
||||||
type RuleTargetConfigButton struct {
|
|
||||||
Device string
|
|
||||||
Button string
|
|
||||||
Inverted bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type RuleTargetConfigAxis struct {
|
|
||||||
Device string
|
|
||||||
Axis string
|
|
||||||
DeadzoneCenter int32 `yaml:"deadzone_center,omitempty"`
|
|
||||||
DeadzoneSize int32 `yaml:"deadzone_size,omitempty"`
|
|
||||||
DeadzoneSizePercent int32 `yaml:"deadzone_size_percent,omitempty"`
|
|
||||||
DeadzoneStart int32 `yaml:"deadzone_start,omitempty"`
|
|
||||||
DeadzoneEnd int32 `yaml:"deadzone_end,omitempty"`
|
|
||||||
Inverted bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type RuleTargetConfigRelaxis struct {
|
|
||||||
Device string
|
|
||||||
Axis string
|
|
||||||
}
|
|
||||||
|
|
||||||
type RuleTargetConfigModeSelect struct {
|
|
||||||
Modes []string
|
|
||||||
}
|
|
||||||
|
|
|
||||||
99
internal/mappingrules/deadzone.go
Normal file
99
internal/mappingrules/deadzone.go
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
package mappingrules
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.annabunches.net/annabunches/joyful/internal/configparser"
|
||||||
|
"github.com/holoplot/go-evdev"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: need tests for multiple deadzones
|
||||||
|
// TODO: need tests for emitting deadzones
|
||||||
|
|
||||||
|
type Deadzone struct {
|
||||||
|
Start int32
|
||||||
|
End int32
|
||||||
|
Size int32
|
||||||
|
Emit bool
|
||||||
|
EmitValue int32
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeadzoneState indicates whether a value is in a Deadzone and, if it is, whether the deadzone
|
||||||
|
// should emit an event
|
||||||
|
type DeadzoneState int
|
||||||
|
|
||||||
|
const (
|
||||||
|
// DeadzoneClear indicates the value is *not* in the deadzone.
|
||||||
|
DeadzoneClear DeadzoneState = iota
|
||||||
|
DeadzoneEmit
|
||||||
|
DeadzoneNoEmit
|
||||||
|
)
|
||||||
|
|
||||||
|
// calculateDeadzones produces the deadzone start and end values in absolute terms
|
||||||
|
func NewDeadzoneFromConfig(dzConfig configparser.DeadzoneConfig, device Device, axis evdev.EvCode) (Deadzone, error) {
|
||||||
|
dz := Deadzone{}
|
||||||
|
dz.Emit = dzConfig.Emit
|
||||||
|
dz.EmitValue = dzConfig.Value
|
||||||
|
fmt.Printf("DEBUG: %d, %d\n", dzConfig.Value, dz.EmitValue)
|
||||||
|
|
||||||
|
var min, max int32
|
||||||
|
absInfoMap, err := device.AbsInfos()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return dz, err
|
||||||
|
} else {
|
||||||
|
absInfo := absInfoMap[axis]
|
||||||
|
min = absInfo.Minimum
|
||||||
|
max = absInfo.Maximum
|
||||||
|
}
|
||||||
|
|
||||||
|
if dzConfig.Start != 0 || dzConfig.End != 0 {
|
||||||
|
dz.Start = Clamp(dzConfig.Start, min, max)
|
||||||
|
dz.End = Clamp(dzConfig.End, min, max)
|
||||||
|
if dz.Start > dz.End {
|
||||||
|
return dz, errors.New("deadzone end must be greater than deadzone start")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
center := Clamp(dzConfig.Center, min, max)
|
||||||
|
var deadzoneSize int32
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case dzConfig.Size != 0:
|
||||||
|
deadzoneSize = dzConfig.Size
|
||||||
|
case dzConfig.SizePercent != 0:
|
||||||
|
deadzoneSize = (max - min) / dzConfig.SizePercent
|
||||||
|
default:
|
||||||
|
return dz, fmt.Errorf("deadzone configured incorrectly; must define start and end or center and size")
|
||||||
|
}
|
||||||
|
|
||||||
|
dz.Start = center - deadzoneSize/2
|
||||||
|
dz.End = center + deadzoneSize/2
|
||||||
|
dz.Start, dz.End = clampAndShift(dz.Start, dz.End, min, max)
|
||||||
|
}
|
||||||
|
|
||||||
|
dz.Size = dz.End - dz.Start
|
||||||
|
return dz, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CalculateDeadzoneSize(dzs []Deadzone) int32 {
|
||||||
|
var size int32
|
||||||
|
|
||||||
|
for _, dz := range dzs {
|
||||||
|
size += dz.Size
|
||||||
|
}
|
||||||
|
|
||||||
|
return size
|
||||||
|
}
|
||||||
|
|
||||||
|
// Match checks whether the target value is inside the deadzone.
|
||||||
|
// It returns a DeadzoneState enum and possibly an int32.
|
||||||
|
func (dz Deadzone) Match(value int32) (DeadzoneState, int32) {
|
||||||
|
if value < dz.Start || value > dz.End {
|
||||||
|
return DeadzoneClear, value
|
||||||
|
}
|
||||||
|
if dz.Emit {
|
||||||
|
return DeadzoneEmit, dz.EmitValue
|
||||||
|
}
|
||||||
|
return DeadzoneNoEmit, value
|
||||||
|
}
|
||||||
|
|
@ -125,8 +125,12 @@ func (t *MakeRuleTargetsTests) TestMakeRuleTargetAxis() {
|
||||||
t.Run("Invalid deadzone", func() {
|
t.Run("Invalid deadzone", func() {
|
||||||
config := configparser.RuleTargetConfigAxis{Device: "test"}
|
config := configparser.RuleTargetConfigAxis{Device: "test"}
|
||||||
config.Axis = "x"
|
config.Axis = "x"
|
||||||
config.DeadzoneEnd = 100
|
config.Deadzones = []configparser.DeadzoneConfig{
|
||||||
config.DeadzoneStart = 1000
|
{
|
||||||
|
End: 100,
|
||||||
|
Start: 1000,
|
||||||
|
},
|
||||||
|
}
|
||||||
_, err := NewRuleTargetAxisFromConfig(config, t.devs)
|
_, err := NewRuleTargetAxisFromConfig(config, t.devs)
|
||||||
t.NotNil(err)
|
t.NotNil(err)
|
||||||
})
|
})
|
||||||
|
|
@ -145,30 +149,21 @@ func (t *MakeRuleTargetsTests) TestMakeRuleTargetAxis() {
|
||||||
for _, tc := range relDeadzoneTestCases {
|
for _, tc := range relDeadzoneTestCases {
|
||||||
t.Run(fmt.Sprintf("Relative Deadzone %d +- %d", tc.inCenter, tc.inSize), func() {
|
t.Run(fmt.Sprintf("Relative Deadzone %d +- %d", tc.inCenter, tc.inSize), func() {
|
||||||
config := configparser.RuleTargetConfigAxis{
|
config := configparser.RuleTargetConfigAxis{
|
||||||
Device: "test",
|
Device: "test",
|
||||||
Axis: "x",
|
Axis: "x",
|
||||||
DeadzoneCenter: tc.inCenter,
|
Deadzones: []configparser.DeadzoneConfig{{
|
||||||
DeadzoneSize: tc.inSize,
|
Center: tc.inCenter,
|
||||||
|
Size: tc.inSize,
|
||||||
|
}},
|
||||||
}
|
}
|
||||||
rule, err := NewRuleTargetAxisFromConfig(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.Deadzones[0].Start)
|
||||||
t.Equal(tc.outEnd, rule.DeadzoneEnd)
|
t.Equal(tc.outEnd, rule.Deadzones[0].End)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("Deadzone center/size invalid center", func() {
|
|
||||||
config := configparser.RuleTargetConfigAxis{
|
|
||||||
Device: "test",
|
|
||||||
Axis: "x",
|
|
||||||
DeadzoneCenter: 20000,
|
|
||||||
DeadzoneSize: 500,
|
|
||||||
}
|
|
||||||
_, err := NewRuleTargetAxisFromConfig(config, t.devs)
|
|
||||||
t.NotNil(err)
|
|
||||||
})
|
|
||||||
|
|
||||||
relDeadzonePercentTestCases := []struct {
|
relDeadzonePercentTestCases := []struct {
|
||||||
inCenter int32
|
inCenter int32
|
||||||
inSizePercent int32
|
inSizePercent int32
|
||||||
|
|
@ -183,29 +178,20 @@ func (t *MakeRuleTargetsTests) TestMakeRuleTargetAxis() {
|
||||||
for _, tc := range relDeadzonePercentTestCases {
|
for _, tc := range relDeadzonePercentTestCases {
|
||||||
t.Run(fmt.Sprintf("Relative percent deadzone %d +- %d%%", tc.inCenter, tc.inSizePercent), func() {
|
t.Run(fmt.Sprintf("Relative percent deadzone %d +- %d%%", tc.inCenter, tc.inSizePercent), func() {
|
||||||
config := configparser.RuleTargetConfigAxis{
|
config := configparser.RuleTargetConfigAxis{
|
||||||
Device: "test",
|
Device: "test",
|
||||||
Axis: "x",
|
Axis: "x",
|
||||||
DeadzoneCenter: tc.inCenter,
|
Deadzones: []configparser.DeadzoneConfig{{
|
||||||
DeadzoneSizePercent: tc.inSizePercent,
|
Center: tc.inCenter,
|
||||||
|
SizePercent: tc.inSizePercent,
|
||||||
|
}},
|
||||||
}
|
}
|
||||||
rule, err := NewRuleTargetAxisFromConfig(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.Deadzones[0].Start)
|
||||||
t.Equal(tc.outEnd, rule.DeadzoneEnd)
|
t.Equal(tc.outEnd, rule.Deadzones[0].End)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("Deadzone center/percent invalid center", func() {
|
|
||||||
config := configparser.RuleTargetConfigAxis{
|
|
||||||
Device: "test",
|
|
||||||
Axis: "x",
|
|
||||||
DeadzoneCenter: 20000,
|
|
||||||
DeadzoneSizePercent: 10,
|
|
||||||
}
|
|
||||||
_, err := NewRuleTargetAxisFromConfig(config, t.devs)
|
|
||||||
t.NotNil(err)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *MakeRuleTargetsTests) TestMakeRuleTargetRelaxis() {
|
func (t *MakeRuleTargetsTests) TestMakeRuleTargetRelaxis() {
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ func TestRunnerMappingRuleAxisCombined(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *MappingRuleAxisCombinedTests) SetupTest() {
|
func (t *MappingRuleAxisCombinedTests) SetupTest() {
|
||||||
|
noDeadzone := make([]Deadzone, 0)
|
||||||
mode := "*"
|
mode := "*"
|
||||||
t.mode = &mode
|
t.mode = &mode
|
||||||
|
|
||||||
|
|
@ -37,13 +38,13 @@ func (t *MappingRuleAxisCombinedTests) SetupTest() {
|
||||||
evdev.ABS_Y: {Minimum: 0, Maximum: 10000},
|
evdev.ABS_Y: {Minimum: 0, Maximum: 10000},
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
t.inputTargetLower, _ = NewRuleTargetAxis("test-input", t.inputDevice, evdev.ABS_X, true, 0, 0)
|
t.inputTargetLower, _ = NewRuleTargetAxis("test-input", t.inputDevice, evdev.ABS_X, true, noDeadzone)
|
||||||
t.inputTargetLower.OutputMax = 0
|
t.inputTargetLower.OutputMax = 0
|
||||||
t.inputTargetUpper, _ = NewRuleTargetAxis("test-input", t.inputDevice, evdev.ABS_Y, false, 0, 0)
|
t.inputTargetUpper, _ = NewRuleTargetAxis("test-input", t.inputDevice, evdev.ABS_Y, false, noDeadzone)
|
||||||
t.inputTargetUpper.OutputMin = 0
|
t.inputTargetUpper.OutputMin = 0
|
||||||
|
|
||||||
t.outputDevice = &evdev.InputDevice{}
|
t.outputDevice = &evdev.InputDevice{}
|
||||||
t.outputTarget, _ = NewRuleTargetAxis("test-output", t.outputDevice, evdev.ABS_X, false, 0, 0)
|
t.outputTarget, _ = NewRuleTargetAxis("test-output", t.outputDevice, evdev.ABS_X, false, noDeadzone)
|
||||||
|
|
||||||
t.base = NewMappingRuleBase("", []string{"*"})
|
t.base = NewMappingRuleBase("", []string{"*"})
|
||||||
|
|
||||||
|
|
@ -67,10 +68,10 @@ func (t *MappingRuleAxisCombinedTests) TestNewMappingRuleAxisCombined() {
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
rule := &MappingRuleAxisCombined{
|
rule := &MappingRuleAxisCombined{
|
||||||
MappingRuleBase: t.base,
|
// MappingRuleBase: t.base,
|
||||||
InputLower: t.inputTargetLower,
|
InputLower: t.inputTargetLower,
|
||||||
InputUpper: t.inputTargetUpper,
|
InputUpper: t.inputTargetUpper,
|
||||||
Output: t.outputTarget,
|
// Output: t.outputTarget,
|
||||||
}
|
}
|
||||||
t.EqualValues(0, rule.InputLower.OutputMax)
|
t.EqualValues(0, rule.InputLower.OutputMax)
|
||||||
t.EqualValues(0, rule.InputUpper.OutputMin)
|
t.EqualValues(0, rule.InputUpper.OutputMin)
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ func (t *MappingRuleAxisToButtonTests) SetupTest() {
|
||||||
Maximum: 10000,
|
Maximum: 10000,
|
||||||
},
|
},
|
||||||
}, nil)
|
}, nil)
|
||||||
t.inputRule, _ = NewRuleTargetAxis("test-input", t.inputDevice, evdev.ABS_X, false, int32(0), int32(1000))
|
t.inputRule, _ = NewRuleTargetAxis("test-input", t.inputDevice, evdev.ABS_X, false, []Deadzone{{Start: 0, End: 1000}})
|
||||||
|
|
||||||
t.outputDevice = &evdev.InputDevice{}
|
t.outputDevice = &evdev.InputDevice{}
|
||||||
t.outputRule, _ = NewRuleTargetButton("test-output", t.outputDevice, evdev.ABS_X, false)
|
t.outputRule, _ = NewRuleTargetButton("test-output", t.outputDevice, evdev.ABS_X, false)
|
||||||
|
|
@ -113,14 +113,16 @@ func (t *MappingRuleAxisToButtonTests) TestMatchEvent() {
|
||||||
Code: evdev.ABS_X,
|
Code: evdev.ABS_X,
|
||||||
Value: 1001,
|
Value: 1001,
|
||||||
}, t.mode)
|
}, t.mode)
|
||||||
t.True(testRule.nextEvent > time.Duration(700*time.Millisecond))
|
// Allow leeway since time passes during the test
|
||||||
|
t.True(testRule.nextEvent > time.Duration(650*time.Millisecond))
|
||||||
|
|
||||||
testRule.MatchEvent(t.inputDevice, &evdev.InputEvent{
|
testRule.MatchEvent(t.inputDevice, &evdev.InputEvent{
|
||||||
Type: evdev.EV_ABS,
|
Type: evdev.EV_ABS,
|
||||||
Code: evdev.ABS_X,
|
Code: evdev.ABS_X,
|
||||||
Value: 5500,
|
Value: 5500,
|
||||||
}, t.mode)
|
}, t.mode)
|
||||||
t.Equal(time.Duration(500*time.Millisecond), testRule.nextEvent)
|
// Allow up to 50 ms leeway since time passes during the test
|
||||||
|
t.InDelta(time.Duration(500*time.Millisecond), testRule.nextEvent, 50000000)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,16 +10,15 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type RuleTargetAxis struct {
|
type RuleTargetAxis struct {
|
||||||
DeviceName string
|
DeviceName string
|
||||||
Device Device
|
Device Device
|
||||||
Axis evdev.EvCode
|
Axis evdev.EvCode
|
||||||
Inverted bool
|
Inverted bool
|
||||||
DeadzoneStart int32
|
Deadzones []Deadzone
|
||||||
DeadzoneEnd int32
|
OutputMin int32
|
||||||
OutputMin int32
|
OutputMax int32
|
||||||
OutputMax int32
|
axisSize int32
|
||||||
axisSize int32
|
deadzoneSize int32
|
||||||
deadzoneSize int32
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRuleTargetAxisFromConfig(targetConfig configparser.RuleTargetConfigAxis, devs map[string]Device) (*RuleTargetAxis, error) {
|
func NewRuleTargetAxisFromConfig(targetConfig configparser.RuleTargetConfigAxis, devs map[string]Device) (*RuleTargetAxis, error) {
|
||||||
|
|
@ -28,18 +27,18 @@ func NewRuleTargetAxisFromConfig(targetConfig configparser.RuleTargetConfigAxis,
|
||||||
return nil, fmt.Errorf("non-existent device '%s'", targetConfig.Device)
|
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)
|
eventCode, err := eventcodes.ParseCode(targetConfig.Axis, eventcodes.CodePrefixAxis)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
deadzoneStart, deadzoneEnd, err := calculateDeadzones(targetConfig, device, eventCode)
|
deadzones := make([]Deadzone, 0)
|
||||||
if err != nil {
|
for _, dzConfig := range targetConfig.Deadzones {
|
||||||
return nil, err
|
dz, err := NewDeadzoneFromConfig(dzConfig, device, eventCode)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
deadzones = append(deadzones, dz)
|
||||||
}
|
}
|
||||||
|
|
||||||
return NewRuleTargetAxis(
|
return NewRuleTargetAxis(
|
||||||
|
|
@ -47,58 +46,15 @@ func NewRuleTargetAxisFromConfig(targetConfig configparser.RuleTargetConfigAxis,
|
||||||
device,
|
device,
|
||||||
eventCode,
|
eventCode,
|
||||||
targetConfig.Inverted,
|
targetConfig.Inverted,
|
||||||
deadzoneStart,
|
deadzones,
|
||||||
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,
|
||||||
inverted bool,
|
inverted bool,
|
||||||
deadzoneStart int32,
|
deadzones []Deadzone) (*RuleTargetAxis, error) {
|
||||||
deadzoneEnd int32) (*RuleTargetAxis, error) {
|
|
||||||
|
|
||||||
info, err := device.AbsInfos()
|
info, err := device.AbsInfos()
|
||||||
|
|
||||||
|
|
@ -117,11 +73,7 @@ func NewRuleTargetAxis(device_name string,
|
||||||
return nil, fmt.Errorf("device does not support axis %v", axis)
|
return nil, fmt.Errorf("device does not support axis %v", axis)
|
||||||
}
|
}
|
||||||
|
|
||||||
if deadzoneStart > deadzoneEnd {
|
deadzoneSize := CalculateDeadzoneSize(deadzones)
|
||||||
return nil, errors.New("deadzone_end must be a higher value than deadzone_start")
|
|
||||||
}
|
|
||||||
|
|
||||||
deadzoneSize := Abs(deadzoneEnd - deadzoneStart)
|
|
||||||
|
|
||||||
// Our output range is limited to 16 bits, but we represent values internally with 32 bits.
|
// 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
|
// As a result, we shouldn't need to worry about integer overruns
|
||||||
|
|
@ -132,16 +84,15 @@ func NewRuleTargetAxis(device_name string,
|
||||||
}
|
}
|
||||||
|
|
||||||
return &RuleTargetAxis{
|
return &RuleTargetAxis{
|
||||||
DeviceName: device_name,
|
DeviceName: device_name,
|
||||||
Device: device,
|
Device: device,
|
||||||
Axis: axis,
|
Axis: axis,
|
||||||
Inverted: inverted,
|
Inverted: inverted,
|
||||||
OutputMin: AxisValueMin,
|
OutputMin: AxisValueMin,
|
||||||
OutputMax: AxisValueMax,
|
OutputMax: AxisValueMax,
|
||||||
DeadzoneStart: deadzoneStart,
|
Deadzones: deadzones,
|
||||||
DeadzoneEnd: deadzoneEnd,
|
deadzoneSize: deadzoneSize,
|
||||||
deadzoneSize: deadzoneSize,
|
axisSize: axisSize,
|
||||||
axisSize: axisSize,
|
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -150,14 +101,23 @@ func NewRuleTargetAxis(device_name string,
|
||||||
// Axis inputs are normalized to the full signed int32 range to match the virtual device's axis
|
// Axis inputs are normalized to the full signed int32 range to match the virtual device's axis
|
||||||
// characteristics.
|
// characteristics.
|
||||||
//
|
//
|
||||||
|
// If the raw value is inside the deadzone, we either emit no event, or we emit the deadzoneValue.
|
||||||
// Typically this function is called after RuleTargetAxis.MatchEvent, which checks whether we are
|
// Typically this function is called after RuleTargetAxis.MatchEvent, which checks whether we are
|
||||||
// in the deadzone, among other things.
|
// in the deadzone, among other things.
|
||||||
func (target *RuleTargetAxis) NormalizeValue(value int32) int32 {
|
func (target *RuleTargetAxis) NormalizeValue(value int32) int32 {
|
||||||
|
for _, dz := range target.Deadzones {
|
||||||
|
state, dzValue := dz.Match(value)
|
||||||
|
if state == DeadzoneEmit {
|
||||||
|
return Clamp(dzValue, target.OutputMin, target.OutputMax)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
axisStrength := target.GetAxisStrength(value)
|
axisStrength := target.GetAxisStrength(value)
|
||||||
return LerpInt(target.OutputMin, target.OutputMax, axisStrength)
|
return LerpInt(target.OutputMin, target.OutputMax, axisStrength)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (target *RuleTargetAxis) CreateEvent(value int32, mode *string) *evdev.InputEvent {
|
func (target *RuleTargetAxis) CreateEvent(value int32, mode *string) *evdev.InputEvent {
|
||||||
|
fmt.Println("DEBUG: Emitting event")
|
||||||
value = Clamp(value, AxisValueMin, AxisValueMax)
|
value = Clamp(value, AxisValueMin, AxisValueMax)
|
||||||
return &evdev.InputEvent{
|
return &evdev.InputEvent{
|
||||||
Type: evdev.EV_ABS,
|
Type: evdev.EV_ABS,
|
||||||
|
|
@ -178,19 +138,30 @@ func (target *RuleTargetAxis) MatchEventDeviceAndCode(device Device, event *evde
|
||||||
event.Code == target.Axis
|
event.Code == target.Axis
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// InDeadZone checks each deadzone for whether the target value falls within it.
|
||||||
|
// If *any* non-emitting deadzone matches, we return true.
|
||||||
// TODO: Add tests
|
// TODO: Add tests
|
||||||
func (target *RuleTargetAxis) InDeadZone(value int32) bool {
|
func (target *RuleTargetAxis) InDeadZone(value int32) bool {
|
||||||
return target.deadzoneSize > 0 && value >= target.DeadzoneStart && value <= target.DeadzoneEnd
|
for _, dz := range target.Deadzones {
|
||||||
|
state, _ := dz.Match(value)
|
||||||
|
if state == DeadzoneNoEmit {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetAxisStrength returns a float between 0.0 and 1.0, representing the proportional
|
// GetAxisStrength returns a float between 0.0 and 1.0, representing the proportional
|
||||||
// position along the axis' full range. (after factoring in deadzones)
|
// position along the axis' full range. (after factoring in deadzones)
|
||||||
// Calling this function with `value` inside the deadzone range will produce undefined behavior
|
// Calling this function with `value` inside the deadzone range will produce undefined behavior
|
||||||
func (target *RuleTargetAxis) GetAxisStrength(value int32) float64 {
|
func (target *RuleTargetAxis) GetAxisStrength(value int32) float64 {
|
||||||
if value > target.DeadzoneEnd {
|
adjValue := value
|
||||||
value -= target.deadzoneSize
|
for _, dz := range target.Deadzones {
|
||||||
|
if value > dz.End {
|
||||||
|
adjValue -= dz.Size
|
||||||
|
}
|
||||||
}
|
}
|
||||||
strength := float64(value) / float64(target.axisSize)
|
strength := float64(adjValue) / float64(target.axisSize)
|
||||||
if target.Inverted {
|
if target.Inverted {
|
||||||
strength = 1.0 - strength
|
strength = 1.0 - strength
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -38,42 +38,42 @@ func (t *RuleTargetAxisTests) TearDownTest() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *RuleTargetAxisTests) TestNewRuleTargetAxis() {
|
func (t *RuleTargetAxisTests) TestNewRuleTargetAxis() {
|
||||||
|
noDeadzone := make([]Deadzone, 0)
|
||||||
|
|
||||||
// RuleTargets should get created
|
// RuleTargets should get created
|
||||||
ruleTarget, err := NewRuleTargetAxis("", t.mock, evdev.ABS_X, false, 0, 0)
|
ruleTarget, err := NewRuleTargetAxis("", t.mock, evdev.ABS_X, false, noDeadzone)
|
||||||
t.Nil(err)
|
t.Nil(err)
|
||||||
t.EqualValues(10000, ruleTarget.axisSize)
|
t.EqualValues(10000, ruleTarget.axisSize)
|
||||||
|
|
||||||
ruleTarget, err = NewRuleTargetAxis("", t.mock, evdev.ABS_Y, false, 0, 0)
|
ruleTarget, err = NewRuleTargetAxis("", t.mock, evdev.ABS_Y, false, noDeadzone)
|
||||||
t.Nil(err)
|
t.Nil(err)
|
||||||
t.EqualValues(20000, ruleTarget.axisSize)
|
t.EqualValues(20000, ruleTarget.axisSize)
|
||||||
|
|
||||||
// Creating a rule with a deadzone should work and reduce the axisSize
|
// Creating a rule with a deadzone should work and reduce the axisSize
|
||||||
ruleTarget, err = NewRuleTargetAxis("", t.mock, evdev.ABS_Y, false, -500, 500)
|
ruleTarget, err = NewRuleTargetAxis("", t.mock, evdev.ABS_Y, false, []Deadzone{{Start: -500, End: 500, Size: 1000}})
|
||||||
t.Nil(err)
|
t.Nil(err)
|
||||||
t.EqualValues(19000, ruleTarget.axisSize)
|
t.EqualValues(19000, ruleTarget.axisSize)
|
||||||
t.EqualValues(-500, ruleTarget.DeadzoneStart)
|
t.EqualValues(-500, ruleTarget.Deadzones[0].Start)
|
||||||
t.EqualValues(500, ruleTarget.DeadzoneEnd)
|
t.EqualValues(500, ruleTarget.Deadzones[0].End)
|
||||||
|
|
||||||
// Creating a rule with a deadzone should fail if end > start
|
|
||||||
_, err = NewRuleTargetAxis("", t.mock, evdev.ABS_Y, false, 500, -500)
|
|
||||||
t.NotNil(err)
|
|
||||||
|
|
||||||
// Creating a rule on a non-existent axis should err
|
// Creating a rule on a non-existent axis should err
|
||||||
_, err = NewRuleTargetAxis("", t.mock, evdev.ABS_Z, false, 0, 0)
|
_, err = NewRuleTargetAxis("", t.mock, evdev.ABS_Z, false, noDeadzone)
|
||||||
t.NotNil(err)
|
t.NotNil(err)
|
||||||
|
|
||||||
// If Absinfo has an error, we should create a device with permissive bounds
|
// If Absinfo has an error, we should create a device with permissive bounds
|
||||||
t.call.Unset()
|
t.call.Unset()
|
||||||
t.mock.On("AbsInfos").Return(map[evdev.EvCode]evdev.AbsInfo{}, errors.New("Test Error"))
|
t.mock.On("AbsInfos").Return(map[evdev.EvCode]evdev.AbsInfo{}, errors.New("Test Error"))
|
||||||
ruleTarget, err = NewRuleTargetAxis("", t.mock, evdev.ABS_X, false, 0, 0)
|
ruleTarget, err = NewRuleTargetAxis("", t.mock, evdev.ABS_X, false, noDeadzone)
|
||||||
t.Nil(err)
|
t.Nil(err)
|
||||||
t.Equal(AxisValueMax-AxisValueMin, ruleTarget.axisSize)
|
t.Equal(AxisValueMax-AxisValueMin, ruleTarget.axisSize)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *RuleTargetAxisTests) TestNormalizeValue() {
|
func (t *RuleTargetAxisTests) TestNormalizeValue() {
|
||||||
|
noDeadzone := make([]Deadzone, 0)
|
||||||
|
|
||||||
// Basic normalization should work
|
// Basic normalization should work
|
||||||
t.Run("Simple normalization", func() {
|
t.Run("Simple normalization", func() {
|
||||||
ruleTarget, _ := NewRuleTargetAxis("", t.mock, evdev.ABS_X, false, 0, 0)
|
ruleTarget, _ := NewRuleTargetAxis("", t.mock, evdev.ABS_X, false, noDeadzone)
|
||||||
t.Equal(AxisValueMax, ruleTarget.NormalizeValue(int32(10000)))
|
t.Equal(AxisValueMax, ruleTarget.NormalizeValue(int32(10000)))
|
||||||
t.Equal(AxisValueMin, ruleTarget.NormalizeValue(int32(0)))
|
t.Equal(AxisValueMin, ruleTarget.NormalizeValue(int32(0)))
|
||||||
t.EqualValues(0, ruleTarget.NormalizeValue(int32(5000)))
|
t.EqualValues(0, ruleTarget.NormalizeValue(int32(5000)))
|
||||||
|
|
@ -81,26 +81,26 @@ func (t *RuleTargetAxisTests) TestNormalizeValue() {
|
||||||
|
|
||||||
// Normalization with a deadzone should work
|
// Normalization with a deadzone should work
|
||||||
t.Run("With Deadzone", func() {
|
t.Run("With Deadzone", func() {
|
||||||
ruleTarget, _ := NewRuleTargetAxis("", t.mock, evdev.ABS_X, false, 0, 5000)
|
ruleTarget, _ := NewRuleTargetAxis("", t.mock, evdev.ABS_X, false, []Deadzone{{Start: 0, End: 5000, Size: 5000}})
|
||||||
t.Equal(AxisValueMax, ruleTarget.NormalizeValue(int32(10000)))
|
t.Equal(AxisValueMax, ruleTarget.NormalizeValue(int32(10000)))
|
||||||
t.True(ruleTarget.NormalizeValue(int32(5001)) < int32(-31000))
|
t.InDelta(int32(-32000), ruleTarget.NormalizeValue(int32(5001)), 1000)
|
||||||
t.EqualValues(0, ruleTarget.NormalizeValue(int32(7500)))
|
t.EqualValues(0, ruleTarget.NormalizeValue(int32(7500)))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Inverted", func() {
|
t.Run("Inverted", func() {
|
||||||
ruleTarget, _ := NewRuleTargetAxis("", t.mock, evdev.ABS_X, true, 0, 0)
|
ruleTarget, _ := NewRuleTargetAxis("", t.mock, evdev.ABS_X, true, noDeadzone)
|
||||||
t.Equal(AxisValueMax, ruleTarget.NormalizeValue(int32(0)))
|
t.Equal(AxisValueMax, ruleTarget.NormalizeValue(int32(0)))
|
||||||
t.Equal(AxisValueMin, ruleTarget.NormalizeValue(int32(10000)))
|
t.Equal(AxisValueMin, ruleTarget.NormalizeValue(int32(10000)))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Out of bounds", func() { // Normalization past the stated axis bounds should clamp
|
t.Run("Out of bounds", func() { // Normalization past the stated axis bounds should clamp
|
||||||
ruleTarget, _ := NewRuleTargetAxis("", t.mock, evdev.ABS_X, false, 0, 0)
|
ruleTarget, _ := NewRuleTargetAxis("", t.mock, evdev.ABS_X, false, noDeadzone)
|
||||||
t.Equal(AxisValueMin, ruleTarget.NormalizeValue(int32(-30000)))
|
t.Equal(AxisValueMin, ruleTarget.NormalizeValue(int32(-30000)))
|
||||||
t.Equal(AxisValueMax, ruleTarget.NormalizeValue(int32(30000)))
|
t.Equal(AxisValueMax, ruleTarget.NormalizeValue(int32(30000)))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("With partial output range", func() {
|
t.Run("With partial output range", func() {
|
||||||
ruleTarget, _ := NewRuleTargetAxis("", t.mock, evdev.ABS_X, false, 0, 0)
|
ruleTarget, _ := NewRuleTargetAxis("", t.mock, evdev.ABS_X, false, noDeadzone)
|
||||||
ruleTarget.OutputMin = 0
|
ruleTarget.OutputMin = 0
|
||||||
ruleTarget.OutputMax = AxisValueMax
|
ruleTarget.OutputMax = AxisValueMax
|
||||||
t.EqualValues(0, ruleTarget.NormalizeValue(int32(0)))
|
t.EqualValues(0, ruleTarget.NormalizeValue(int32(0)))
|
||||||
|
|
@ -110,7 +110,7 @@ func (t *RuleTargetAxisTests) TestNormalizeValue() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *RuleTargetAxisTests) TestMatchEvent() {
|
func (t *RuleTargetAxisTests) TestMatchEvent() {
|
||||||
ruleTarget, _ := NewRuleTargetAxis("", t.mock, evdev.ABS_Y, false, -500, 500)
|
ruleTarget, _ := NewRuleTargetAxis("", t.mock, evdev.ABS_Y, false, []Deadzone{{Start: -500, End: 500}})
|
||||||
validEvent := &evdev.InputEvent{
|
validEvent := &evdev.InputEvent{
|
||||||
Type: evdev.EV_ABS,
|
Type: evdev.EV_ABS,
|
||||||
Code: evdev.ABS_Y,
|
Code: evdev.ABS_Y,
|
||||||
|
|
@ -133,7 +133,9 @@ func (t *RuleTargetAxisTests) TestMatchEvent() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *RuleTargetAxisTests) TestCreateEvent() {
|
func (t *RuleTargetAxisTests) TestCreateEvent() {
|
||||||
ruleTarget, _ := NewRuleTargetAxis("", t.mock, evdev.ABS_X, false, 0, 0)
|
noDeadzone := make([]Deadzone, 0)
|
||||||
|
|
||||||
|
ruleTarget, _ := NewRuleTargetAxis("", t.mock, evdev.ABS_X, false, noDeadzone)
|
||||||
expected := &evdev.InputEvent{
|
expected := &evdev.InputEvent{
|
||||||
Type: evdev.EV_ABS,
|
Type: evdev.EV_ABS,
|
||||||
Code: evdev.ABS_X,
|
Code: evdev.ABS_X,
|
||||||
|
|
@ -155,43 +157,45 @@ func (t *RuleTargetAxisTests) TestCreateEvent() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *RuleTargetAxisTests) TestGetAxisStrength() {
|
func (t *RuleTargetAxisTests) TestGetAxisStrength() {
|
||||||
|
noDeadzone := make([]Deadzone, 0)
|
||||||
|
|
||||||
t.Run("With no deadzone", func() {
|
t.Run("With no deadzone", func() {
|
||||||
ruleTarget, _ := NewRuleTargetAxis("", t.mock, evdev.ABS_X, false, 0, 0)
|
ruleTarget, _ := NewRuleTargetAxis("", t.mock, evdev.ABS_X, false, noDeadzone)
|
||||||
t.Equal(0.0, ruleTarget.GetAxisStrength(0))
|
t.Equal(0.0, ruleTarget.GetAxisStrength(0))
|
||||||
t.Equal(1.0, ruleTarget.GetAxisStrength(10000))
|
t.Equal(1.0, ruleTarget.GetAxisStrength(10000))
|
||||||
t.Equal(0.5, ruleTarget.GetAxisStrength(5000))
|
t.Equal(0.5, ruleTarget.GetAxisStrength(5000))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("With low deadzone", func() {
|
t.Run("With low deadzone", func() {
|
||||||
ruleTarget, _ := NewRuleTargetAxis("", t.mock, evdev.ABS_X, false, 0, 5000)
|
ruleTarget, _ := NewRuleTargetAxis("", t.mock, evdev.ABS_X, false, []Deadzone{{Start: 0, End: 5000, Size: 5000}})
|
||||||
t.InDelta(0.0, ruleTarget.GetAxisStrength(5001), 0.01)
|
t.InDelta(0.0, ruleTarget.GetAxisStrength(5001), 0.01)
|
||||||
t.InDelta(0.5, ruleTarget.GetAxisStrength(7500), 0.01)
|
t.InDelta(0.5, ruleTarget.GetAxisStrength(7500), 0.01)
|
||||||
t.Equal(1.0, ruleTarget.GetAxisStrength(10000))
|
t.Equal(1.0, ruleTarget.GetAxisStrength(10000))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("With high deadzone", func() {
|
t.Run("With high deadzone", func() {
|
||||||
ruleTarget, _ := NewRuleTargetAxis("", t.mock, evdev.ABS_X, false, 5000, 10000)
|
ruleTarget, _ := NewRuleTargetAxis("", t.mock, evdev.ABS_X, false, []Deadzone{{Start: 5000, End: 10000, Size: 5000}})
|
||||||
t.Equal(0.0, ruleTarget.GetAxisStrength(0))
|
t.Equal(0.0, ruleTarget.GetAxisStrength(0))
|
||||||
t.InDelta(0.5, ruleTarget.GetAxisStrength(2500), 0.01)
|
t.InDelta(0.5, ruleTarget.GetAxisStrength(2500), 0.01)
|
||||||
t.InDelta(1.0, ruleTarget.GetAxisStrength(4999), 0.01)
|
t.InDelta(1.0, ruleTarget.GetAxisStrength(4999), 0.01)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Inverted", func() {
|
t.Run("Inverted", func() {
|
||||||
ruleTarget, _ := NewRuleTargetAxis("", t.mock, evdev.ABS_X, true, 0, 0)
|
ruleTarget, _ := NewRuleTargetAxis("", t.mock, evdev.ABS_X, true, noDeadzone)
|
||||||
t.Equal(1.0, ruleTarget.GetAxisStrength(0))
|
t.Equal(1.0, ruleTarget.GetAxisStrength(0))
|
||||||
t.Equal(0.5, ruleTarget.GetAxisStrength(5000))
|
t.Equal(0.5, ruleTarget.GetAxisStrength(5000))
|
||||||
t.Equal(0.0, ruleTarget.GetAxisStrength(10000))
|
t.Equal(0.0, ruleTarget.GetAxisStrength(10000))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Inverted with low deadzone", func() {
|
t.Run("Inverted with low deadzone", func() {
|
||||||
ruleTarget, _ := NewRuleTargetAxis("", t.mock, evdev.ABS_X, true, 0, 5000)
|
ruleTarget, _ := NewRuleTargetAxis("", t.mock, evdev.ABS_X, true, []Deadzone{{Start: 0, End: 5000, Size: 5000}})
|
||||||
t.InDelta(1.0, ruleTarget.GetAxisStrength(5001), 0.01)
|
t.InDelta(1.0, ruleTarget.GetAxisStrength(5001), 0.01)
|
||||||
t.InDelta(0.5, ruleTarget.GetAxisStrength(7500), 0.01)
|
t.InDelta(0.5, ruleTarget.GetAxisStrength(7500), 0.01)
|
||||||
t.Equal(0.0, ruleTarget.GetAxisStrength(10000))
|
t.Equal(0.0, ruleTarget.GetAxisStrength(10000))
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Inverted with high deadzone", func() {
|
t.Run("Inverted with high deadzone", func() {
|
||||||
ruleTarget, _ := NewRuleTargetAxis("", t.mock, evdev.ABS_X, true, 5000, 10000)
|
ruleTarget, _ := NewRuleTargetAxis("", t.mock, evdev.ABS_X, true, []Deadzone{{Start: 5000, End: 10000, Size: 5000}})
|
||||||
t.InDelta(0.0, ruleTarget.GetAxisStrength(4999), 0.01)
|
t.InDelta(0.0, ruleTarget.GetAxisStrength(4999), 0.01)
|
||||||
t.InDelta(0.5, ruleTarget.GetAxisStrength(2500), 0.01)
|
t.InDelta(0.5, ruleTarget.GetAxisStrength(2500), 0.01)
|
||||||
t.Equal(1.0, ruleTarget.GetAxisStrength(0))
|
t.Equal(1.0, ruleTarget.GetAxisStrength(0))
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue