Add more deadzone specification options. (#9)
Reviewed-on: #9 Co-authored-by: Anna Rose Wiggins <annabunches@gmail.com> Co-committed-by: Anna Rose Wiggins <annabunches@gmail.com>
This commit is contained in:
parent
5b9dfe0967
commit
97a1acd228
20 changed files with 344 additions and 108 deletions
17
.vscode/tasks.json
vendored
17
.vscode/tasks.json
vendored
|
@ -28,6 +28,23 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"problemMatcher": []
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"label": "Test Project",
|
||||||
|
"args": [
|
||||||
|
"test",
|
||||||
|
"./..."
|
||||||
|
],
|
||||||
|
"group": {
|
||||||
|
"kind": "test",
|
||||||
|
"isDefault": true
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"env": {
|
||||||
|
"CGO_ENABLED": "0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"problemMatcher": []
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
}
|
}
|
|
@ -26,6 +26,34 @@ rules:
|
||||||
device: main
|
device: main
|
||||||
axis: ABS_X
|
axis: ABS_X
|
||||||
|
|
||||||
|
- type: axis
|
||||||
|
input:
|
||||||
|
device: flightstick
|
||||||
|
# An alternate way to specify deadzones is to define the deadzone's center and then a
|
||||||
|
# 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
|
||||||
|
# of the axis, the total size will still be as given; the deadzone will be "shifted" into bounds.
|
||||||
|
deadzone_center: 29000
|
||||||
|
deadzone_size: 2000
|
||||||
|
inverted: false
|
||||||
|
axis: Y # The ABS_ prefix is optional
|
||||||
|
output:
|
||||||
|
device: main
|
||||||
|
axis: ABS_Y
|
||||||
|
|
||||||
|
- type: axis
|
||||||
|
input:
|
||||||
|
device: flightstick
|
||||||
|
# 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.
|
||||||
|
deadzone_center: 29000
|
||||||
|
deadzone_size_percent: 5
|
||||||
|
inverted: false
|
||||||
|
axis: Y # The ABS_ prefix is optional
|
||||||
|
output:
|
||||||
|
device: main
|
||||||
|
axis: ABS_Y
|
||||||
|
|
||||||
# Straightforward button mapping
|
# Straightforward button mapping
|
||||||
- type: button
|
- type: button
|
||||||
input:
|
input:
|
||||||
|
|
|
@ -55,6 +55,16 @@ For input, you can figure out what event codes your device is emitting by runnin
|
||||||
evtest | grep BTN_
|
evtest | grep BTN_
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Axis Deadzones
|
||||||
|
|
||||||
|
For most axis inputs, you will want to define deadzones. There are three possible approaches:
|
||||||
|
|
||||||
|
* Define `deadzone_start` and `deadzone_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 `deadzone_center` and `deadzone_size_percent` to use a percentage of the total axis size.
|
||||||
|
|
||||||
|
See <examples/ruletypes.yml> for usage examples.
|
||||||
|
|
||||||
## Modes
|
## Modes
|
||||||
|
|
||||||
Modes are optional, and also have the simplest configuration. To define modes, add this to your configuration:
|
Modes are optional, and also have the simplest configuration. To define modes, add this to your configuration:
|
||||||
|
|
7
internal/config/interfaces.go
Normal file
7
internal/config/interfaces.go
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
package config
|
||||||
|
|
||||||
|
import "github.com/holoplot/go-evdev"
|
||||||
|
|
||||||
|
type Device interface {
|
||||||
|
AbsInfos() (map[evdev.EvCode]evdev.AbsInfo, error)
|
||||||
|
}
|
|
@ -4,11 +4,12 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"git.annabunches.net/annabunches/joyful/internal/logger"
|
||||||
"git.annabunches.net/annabunches/joyful/internal/mappingrules"
|
"git.annabunches.net/annabunches/joyful/internal/mappingrules"
|
||||||
"github.com/holoplot/go-evdev"
|
"github.com/holoplot/go-evdev"
|
||||||
)
|
)
|
||||||
|
|
||||||
func makeRuleTargetButton(targetConfig RuleTargetConfig, devs map[string]*evdev.InputDevice) (*mappingrules.RuleTargetButton, error) {
|
func makeRuleTargetButton(targetConfig RuleTargetConfig, devs map[string]Device) (*mappingrules.RuleTargetButton, error) {
|
||||||
device, ok := devs[targetConfig.Device]
|
device, ok := devs[targetConfig.Device]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("non-existent device '%s'", targetConfig.Device)
|
return nil, fmt.Errorf("non-existent device '%s'", targetConfig.Device)
|
||||||
|
@ -27,7 +28,7 @@ func makeRuleTargetButton(targetConfig RuleTargetConfig, devs map[string]*evdev.
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeRuleTargetAxis(targetConfig RuleTargetConfig, devs map[string]*evdev.InputDevice) (*mappingrules.RuleTargetAxis, error) {
|
func makeRuleTargetAxis(targetConfig RuleTargetConfig, devs map[string]Device) (*mappingrules.RuleTargetAxis, error) {
|
||||||
device, ok := devs[targetConfig.Device]
|
device, ok := devs[targetConfig.Device]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("non-existent device '%s'", targetConfig.Device)
|
return nil, fmt.Errorf("non-existent device '%s'", targetConfig.Device)
|
||||||
|
@ -42,17 +43,22 @@ func makeRuleTargetAxis(targetConfig RuleTargetConfig, devs map[string]*evdev.In
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
deadzoneStart, deadzoneEnd, err := calculateDeadzones(targetConfig, device, eventCode)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return mappingrules.NewRuleTargetAxis(
|
return mappingrules.NewRuleTargetAxis(
|
||||||
targetConfig.Device,
|
targetConfig.Device,
|
||||||
device,
|
device,
|
||||||
eventCode,
|
eventCode,
|
||||||
targetConfig.Inverted,
|
targetConfig.Inverted,
|
||||||
targetConfig.DeadzoneStart,
|
deadzoneStart,
|
||||||
targetConfig.DeadzoneEnd,
|
deadzoneEnd,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeRuleTargetRelaxis(targetConfig RuleTargetConfig, devs map[string]*evdev.InputDevice) (*mappingrules.RuleTargetRelaxis, error) {
|
func makeRuleTargetRelaxis(targetConfig RuleTargetConfig, devs map[string]Device) (*mappingrules.RuleTargetRelaxis, error) {
|
||||||
device, ok := devs[targetConfig.Device]
|
device, ok := devs[targetConfig.Device]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, fmt.Errorf("non-existent device '%s'", targetConfig.Device)
|
return nil, fmt.Errorf("non-existent device '%s'", targetConfig.Device)
|
||||||
|
@ -83,3 +89,61 @@ func makeRuleTargetModeSelect(targetConfig RuleTargetConfig, allModes []string)
|
||||||
func hasError(_ any, err error) bool {
|
func hasError(_ any, err error) bool {
|
||||||
return err != nil
|
return err != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// calculateDeadzones produces the deadzone start and end values in absolute terms
|
||||||
|
// TODO: on the one hand, this logic feels betten encapsulated in mappingrules. On the other hand,
|
||||||
|
// passing even more parameters to NewRuleTargetAxis feels terrible
|
||||||
|
func calculateDeadzones(targetConfig RuleTargetConfig, 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 = mappingrules.AxisValueMin
|
||||||
|
max = mappingrules.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) {
|
||||||
|
logger.Logf("DEBUG: %d %d %d %d", start, end, min, max)
|
||||||
|
if start < min {
|
||||||
|
end += min - start
|
||||||
|
start = min
|
||||||
|
logger.Logf("DEBUG: %d %d %d %d", start, end, min, max)
|
||||||
|
}
|
||||||
|
if end > max {
|
||||||
|
start -= end - max
|
||||||
|
end = max
|
||||||
|
}
|
||||||
|
|
||||||
|
return start, end
|
||||||
|
}
|
||||||
|
|
|
@ -4,12 +4,24 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/holoplot/go-evdev"
|
"github.com/holoplot/go-evdev"
|
||||||
|
"github.com/stretchr/testify/mock"
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
)
|
)
|
||||||
|
|
||||||
type MakeRuleTargetsTests struct {
|
type MakeRuleTargetsTests struct {
|
||||||
suite.Suite
|
suite.Suite
|
||||||
devs map[string]*evdev.InputDevice
|
devs map[string]Device
|
||||||
|
deviceMock *DeviceMock
|
||||||
|
config RuleTargetConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
type DeviceMock struct {
|
||||||
|
mock.Mock
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *DeviceMock) AbsInfos() (map[evdev.EvCode]evdev.AbsInfo, error) {
|
||||||
|
args := m.Called()
|
||||||
|
return args.Get(0).(map[evdev.EvCode]evdev.AbsInfo), args.Error(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestRunnerMakeRuleTargets(t *testing.T) {
|
func TestRunnerMakeRuleTargets(t *testing.T) {
|
||||||
|
@ -17,131 +29,216 @@ func TestRunnerMakeRuleTargets(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *MakeRuleTargetsTests) SetupSuite() {
|
func (t *MakeRuleTargetsTests) SetupSuite() {
|
||||||
t.devs = map[string]*evdev.InputDevice{
|
t.deviceMock = new(DeviceMock)
|
||||||
"test": {},
|
t.deviceMock.On("AbsInfos").Return(
|
||||||
|
map[evdev.EvCode]evdev.AbsInfo{
|
||||||
|
evdev.ABS_X: {
|
||||||
|
Minimum: 0,
|
||||||
|
Maximum: 10000,
|
||||||
|
},
|
||||||
|
evdev.ABS_Y: {
|
||||||
|
Minimum: 0,
|
||||||
|
Maximum: 10000,
|
||||||
|
},
|
||||||
|
}, nil,
|
||||||
|
)
|
||||||
|
t.devs = map[string]Device{
|
||||||
|
"test": t.deviceMock,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *MakeRuleTargetsTests) SetupSubTest() {
|
||||||
|
t.config = RuleTargetConfig{
|
||||||
|
Device: "test",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *MakeRuleTargetsTests) TestMakeRuleTargetButton() {
|
func (t *MakeRuleTargetsTests) TestMakeRuleTargetButton() {
|
||||||
config := RuleTargetConfig{
|
|
||||||
Device: "test",
|
|
||||||
}
|
|
||||||
t.Run("Standard keycode", func() {
|
t.Run("Standard keycode", func() {
|
||||||
config.Button = "BTN_TRIGGER"
|
t.config.Button = "BTN_TRIGGER"
|
||||||
rule, err := makeRuleTargetButton(config, t.devs)
|
rule, err := makeRuleTargetButton(t.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"
|
t.config.Button = "0x2fd"
|
||||||
rule, err := makeRuleTargetButton(config, t.devs)
|
rule, err := makeRuleTargetButton(t.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"
|
t.config.Button = "3"
|
||||||
rule, err := makeRuleTargetButton(config, t.devs)
|
rule, err := makeRuleTargetButton(t.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"
|
t.config.Button = "74"
|
||||||
_, err := makeRuleTargetButton(config, t.devs)
|
_, err := makeRuleTargetButton(t.config, t.devs)
|
||||||
t.NotNil(err)
|
t.NotNil(err)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Un-prefixed keycode", func() {
|
t.Run("Un-prefixed keycode", func() {
|
||||||
config.Button = "pinkie"
|
t.config.Button = "pinkie"
|
||||||
rule, err := makeRuleTargetButton(config, t.devs)
|
rule, err := makeRuleTargetButton(t.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"
|
t.config.Button = "foo"
|
||||||
_, err := makeRuleTargetButton(config, t.devs)
|
_, err := makeRuleTargetButton(t.config, t.devs)
|
||||||
t.NotNil(err)
|
t.NotNil(err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *MakeRuleTargetsTests) TestMakeRuleTargetAxis() {
|
func (t *MakeRuleTargetsTests) TestMakeRuleTargetAxis() {
|
||||||
config := RuleTargetConfig{
|
t.Run("Standard code", func() {
|
||||||
Device: "test",
|
t.config.Axis = "ABS_X"
|
||||||
}
|
rule, err := makeRuleTargetAxis(t.config, t.devs)
|
||||||
|
|
||||||
t.Run("Standard keycode", func() {
|
|
||||||
config.Axis = "ABS_X"
|
|
||||||
rule, err := makeRuleTargetAxis(config, t.devs)
|
|
||||||
t.Nil(err)
|
t.Nil(err)
|
||||||
t.EqualValues(evdev.ABS_X, rule.Axis)
|
t.EqualValues(evdev.ABS_X, rule.Axis)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Hex keycode", func() {
|
t.Run("Hex code", func() {
|
||||||
config.Axis = "0x01"
|
t.config.Axis = "0x01"
|
||||||
rule, err := makeRuleTargetAxis(config, t.devs)
|
rule, err := makeRuleTargetAxis(t.config, t.devs)
|
||||||
t.Nil(err)
|
t.Nil(err)
|
||||||
t.EqualValues(evdev.ABS_Y, rule.Axis)
|
t.EqualValues(evdev.ABS_Y, rule.Axis)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Un-prefixed keycode", func() {
|
t.Run("Un-prefixed code", func() {
|
||||||
config.Axis = "x"
|
t.config.Axis = "x"
|
||||||
rule, err := makeRuleTargetAxis(config, t.devs)
|
rule, err := makeRuleTargetAxis(t.config, t.devs)
|
||||||
t.Nil(err)
|
t.Nil(err)
|
||||||
t.EqualValues(evdev.ABS_X, rule.Axis)
|
t.EqualValues(evdev.ABS_X, rule.Axis)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Invalid keycode", func() {
|
t.Run("Invalid code", func() {
|
||||||
config.Axis = "foo"
|
t.config.Axis = "foo"
|
||||||
_, err := makeRuleTargetAxis(config, t.devs)
|
_, err := makeRuleTargetAxis(t.config, t.devs)
|
||||||
t.NotNil(err)
|
t.NotNil(err)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Invalid deadzone", func() {
|
t.Run("Invalid deadzone", func() {
|
||||||
config.DeadzoneEnd = 100
|
t.config.Axis = "x"
|
||||||
config.DeadzoneStart = 1000
|
t.config.DeadzoneEnd = 100
|
||||||
_, err := makeRuleTargetAxis(config, t.devs)
|
t.config.DeadzoneStart = 1000
|
||||||
|
_, err := makeRuleTargetAxis(t.config, t.devs)
|
||||||
|
t.NotNil(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Deadzone center/size", func() {
|
||||||
|
t.config.Axis = "x"
|
||||||
|
t.config.DeadzoneCenter = 5000
|
||||||
|
t.config.DeadzoneSize = 1000
|
||||||
|
rule, err := makeRuleTargetAxis(t.config, t.devs)
|
||||||
|
t.Nil(err)
|
||||||
|
t.EqualValues(4500, rule.DeadzoneStart)
|
||||||
|
t.EqualValues(5500, rule.DeadzoneEnd)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Deadzone center/size lower boundary", func() {
|
||||||
|
t.config.Axis = "x"
|
||||||
|
t.config.DeadzoneCenter = 0
|
||||||
|
t.config.DeadzoneSize = 500
|
||||||
|
rule, err := makeRuleTargetAxis(t.config, t.devs)
|
||||||
|
t.Nil(err)
|
||||||
|
t.EqualValues(0, rule.DeadzoneStart)
|
||||||
|
t.EqualValues(500, rule.DeadzoneEnd)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Deadzone center/size upper boundary", func() {
|
||||||
|
t.config.Axis = "x"
|
||||||
|
t.config.DeadzoneCenter = 10000
|
||||||
|
t.config.DeadzoneSize = 500
|
||||||
|
rule, err := makeRuleTargetAxis(t.config, t.devs)
|
||||||
|
t.Nil(err)
|
||||||
|
t.EqualValues(9500, rule.DeadzoneStart)
|
||||||
|
t.EqualValues(10000, rule.DeadzoneEnd)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Deadzone center/size invalid center", func() {
|
||||||
|
t.config.Axis = "x"
|
||||||
|
t.config.DeadzoneCenter = 20000
|
||||||
|
t.config.DeadzoneSize = 500
|
||||||
|
_, err := makeRuleTargetAxis(t.config, t.devs)
|
||||||
|
t.NotNil(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Deadzone center/percent", func() {
|
||||||
|
t.config.Axis = "x"
|
||||||
|
t.config.DeadzoneCenter = 5000
|
||||||
|
t.config.DeadzoneSizePercent = 10
|
||||||
|
rule, err := makeRuleTargetAxis(t.config, t.devs)
|
||||||
|
t.Nil(err)
|
||||||
|
t.EqualValues(4500, rule.DeadzoneStart)
|
||||||
|
t.EqualValues(5500, rule.DeadzoneEnd)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Deadzone center/percent lower boundary", func() {
|
||||||
|
t.config.Axis = "x"
|
||||||
|
t.config.DeadzoneCenter = 0
|
||||||
|
t.config.DeadzoneSizePercent = 10
|
||||||
|
rule, err := makeRuleTargetAxis(t.config, t.devs)
|
||||||
|
t.Nil(err)
|
||||||
|
t.EqualValues(0, rule.DeadzoneStart)
|
||||||
|
t.EqualValues(1000, rule.DeadzoneEnd)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Deadzone center/percent upper boundary", func() {
|
||||||
|
t.config.Axis = "x"
|
||||||
|
t.config.DeadzoneCenter = 10000
|
||||||
|
t.config.DeadzoneSizePercent = 10
|
||||||
|
rule, err := makeRuleTargetAxis(t.config, t.devs)
|
||||||
|
t.Nil(err)
|
||||||
|
t.EqualValues(9000, rule.DeadzoneStart)
|
||||||
|
t.EqualValues(10000, rule.DeadzoneEnd)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Deadzone center/percent invalid center", func() {
|
||||||
|
t.config.Axis = "x"
|
||||||
|
t.config.DeadzoneCenter = 20000
|
||||||
|
t.config.DeadzoneSizePercent = 10
|
||||||
|
_, err := makeRuleTargetAxis(t.config, t.devs)
|
||||||
t.NotNil(err)
|
t.NotNil(err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *MakeRuleTargetsTests) TestMakeRuleTargetRelaxis() {
|
func (t *MakeRuleTargetsTests) TestMakeRuleTargetRelaxis() {
|
||||||
config := RuleTargetConfig{
|
|
||||||
Device: "test",
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Run("Standard keycode", func() {
|
t.Run("Standard keycode", func() {
|
||||||
config.Axis = "REL_WHEEL"
|
t.config.Axis = "REL_WHEEL"
|
||||||
rule, err := makeRuleTargetRelaxis(config, t.devs)
|
rule, err := makeRuleTargetRelaxis(t.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"
|
t.config.Axis = "0x00"
|
||||||
rule, err := makeRuleTargetRelaxis(config, t.devs)
|
rule, err := makeRuleTargetRelaxis(t.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"
|
t.config.Axis = "wheel"
|
||||||
rule, err := makeRuleTargetRelaxis(config, t.devs)
|
rule, err := makeRuleTargetRelaxis(t.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"
|
t.config.Axis = "foo"
|
||||||
_, err := makeRuleTargetRelaxis(config, t.devs)
|
_, err := makeRuleTargetRelaxis(t.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"
|
t.config.Axis = "ABS_X"
|
||||||
_, err := makeRuleTargetRelaxis(config, t.devs)
|
_, err := makeRuleTargetRelaxis(t.config, t.devs)
|
||||||
t.NotNil(err)
|
t.NotNil(err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,14 +10,25 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: At some point it would *very likely* make sense to map each rule to all of the physical devices that can
|
// TODO: At some point it would *very likely* make sense to map each rule to all of the physical devices that can
|
||||||
// trigger it, and return that instead. Something like a map[*evdev.InputDevice][]mappingrule.MappingRule.
|
// trigger it, and return that instead. Something like a map[Device][]mappingrule.MappingRule.
|
||||||
// This would speed up rule matching by only checking relevant rules for a given input event.
|
// This would speed up rule matching by only checking relevant rules for a given input event.
|
||||||
// We could take this further and make it a map[<struct of *inputdevice, type, and code>][]rule
|
// We could take this further and make it a map[<struct of *inputdevice, type, and code>][]rule
|
||||||
// For very large rule-bases this may be helpful for staying performant.
|
// For very large rule-bases this may be helpful for staying performant.
|
||||||
func (parser *ConfigParser) BuildRules(pDevs map[string]*evdev.InputDevice, vDevs map[string]*evdev.InputDevice) []mappingrules.MappingRule {
|
func (parser *ConfigParser) BuildRules(pInputDevs map[string]*evdev.InputDevice, vInputDevs map[string]*evdev.InputDevice) []mappingrules.MappingRule {
|
||||||
rules := make([]mappingrules.MappingRule, 0)
|
rules := make([]mappingrules.MappingRule, 0)
|
||||||
modes := parser.GetModes()
|
modes := parser.GetModes()
|
||||||
|
|
||||||
|
// Golang can't inspect the concrete map type to determine interface conformance,
|
||||||
|
// so we handle that here.
|
||||||
|
pDevs := make(map[string]Device)
|
||||||
|
for name, dev := range pInputDevs {
|
||||||
|
pDevs[name] = dev
|
||||||
|
}
|
||||||
|
vDevs := make(map[string]Device)
|
||||||
|
for name, dev := range vInputDevs {
|
||||||
|
vDevs[name] = dev
|
||||||
|
}
|
||||||
|
|
||||||
for _, ruleConfig := range parser.config.Rules {
|
for _, ruleConfig := range parser.config.Rules {
|
||||||
var newRule mappingrules.MappingRule
|
var newRule mappingrules.MappingRule
|
||||||
var err error
|
var err error
|
||||||
|
@ -60,8 +71,8 @@ func (parser *ConfigParser) BuildRules(pDevs map[string]*evdev.InputDevice, vDev
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeMappingRuleButton(ruleConfig RuleConfig,
|
func makeMappingRuleButton(ruleConfig RuleConfig,
|
||||||
pDevs map[string]*evdev.InputDevice,
|
pDevs map[string]Device,
|
||||||
vDevs map[string]*evdev.InputDevice,
|
vDevs map[string]Device,
|
||||||
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleButton, error) {
|
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleButton, error) {
|
||||||
|
|
||||||
input, err := makeRuleTargetButton(ruleConfig.Input, pDevs)
|
input, err := makeRuleTargetButton(ruleConfig.Input, pDevs)
|
||||||
|
@ -78,8 +89,8 @@ func makeMappingRuleButton(ruleConfig RuleConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeMappingRuleCombo(ruleConfig RuleConfig,
|
func makeMappingRuleCombo(ruleConfig RuleConfig,
|
||||||
pDevs map[string]*evdev.InputDevice,
|
pDevs map[string]Device,
|
||||||
vDevs map[string]*evdev.InputDevice,
|
vDevs map[string]Device,
|
||||||
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleButtonCombo, error) {
|
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleButtonCombo, error) {
|
||||||
|
|
||||||
inputs := make([]*mappingrules.RuleTargetButton, 0)
|
inputs := make([]*mappingrules.RuleTargetButton, 0)
|
||||||
|
@ -100,8 +111,8 @@ func makeMappingRuleCombo(ruleConfig RuleConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeMappingRuleLatched(ruleConfig RuleConfig,
|
func makeMappingRuleLatched(ruleConfig RuleConfig,
|
||||||
pDevs map[string]*evdev.InputDevice,
|
pDevs map[string]Device,
|
||||||
vDevs map[string]*evdev.InputDevice,
|
vDevs map[string]Device,
|
||||||
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleButtonLatched, error) {
|
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleButtonLatched, error) {
|
||||||
|
|
||||||
input, err := makeRuleTargetButton(ruleConfig.Input, pDevs)
|
input, err := makeRuleTargetButton(ruleConfig.Input, pDevs)
|
||||||
|
@ -118,8 +129,8 @@ func makeMappingRuleLatched(ruleConfig RuleConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeMappingRuleAxis(ruleConfig RuleConfig,
|
func makeMappingRuleAxis(ruleConfig RuleConfig,
|
||||||
pDevs map[string]*evdev.InputDevice,
|
pDevs map[string]Device,
|
||||||
vDevs map[string]*evdev.InputDevice,
|
vDevs map[string]Device,
|
||||||
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleAxis, error) {
|
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleAxis, error) {
|
||||||
|
|
||||||
input, err := makeRuleTargetAxis(ruleConfig.Input, pDevs)
|
input, err := makeRuleTargetAxis(ruleConfig.Input, pDevs)
|
||||||
|
@ -136,8 +147,8 @@ func makeMappingRuleAxis(ruleConfig RuleConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeMappingRuleAxisToButton(ruleConfig RuleConfig,
|
func makeMappingRuleAxisToButton(ruleConfig RuleConfig,
|
||||||
pDevs map[string]*evdev.InputDevice,
|
pDevs map[string]Device,
|
||||||
vDevs map[string]*evdev.InputDevice,
|
vDevs map[string]Device,
|
||||||
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleAxisToButton, error) {
|
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleAxisToButton, error) {
|
||||||
|
|
||||||
input, err := makeRuleTargetAxis(ruleConfig.Input, pDevs)
|
input, err := makeRuleTargetAxis(ruleConfig.Input, pDevs)
|
||||||
|
@ -154,8 +165,8 @@ func makeMappingRuleAxisToButton(ruleConfig RuleConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeMappingRuleAxisToRelaxis(ruleConfig RuleConfig,
|
func makeMappingRuleAxisToRelaxis(ruleConfig RuleConfig,
|
||||||
pDevs map[string]*evdev.InputDevice,
|
pDevs map[string]Device,
|
||||||
vDevs map[string]*evdev.InputDevice,
|
vDevs map[string]Device,
|
||||||
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleAxisToRelaxis, error) {
|
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleAxisToRelaxis, error) {
|
||||||
|
|
||||||
input, err := makeRuleTargetAxis(ruleConfig.Input, pDevs)
|
input, err := makeRuleTargetAxis(ruleConfig.Input, pDevs)
|
||||||
|
@ -176,7 +187,7 @@ func makeMappingRuleAxisToRelaxis(ruleConfig RuleConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
func makeMappingRuleModeSelect(ruleConfig RuleConfig,
|
func makeMappingRuleModeSelect(ruleConfig RuleConfig,
|
||||||
pDevs map[string]*evdev.InputDevice,
|
pDevs map[string]Device,
|
||||||
modes []string,
|
modes []string,
|
||||||
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleModeSelect, error) {
|
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleModeSelect, error) {
|
||||||
|
|
||||||
|
|
|
@ -41,11 +41,14 @@ type RuleConfig struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type RuleTargetConfig struct {
|
type RuleTargetConfig struct {
|
||||||
Device string `yaml:"device,omitempty"`
|
Device string `yaml:"device,omitempty"`
|
||||||
Button string `yaml:"button,omitempty"`
|
Button string `yaml:"button,omitempty"`
|
||||||
Axis string `yaml:"axis,omitempty"`
|
Axis string `yaml:"axis,omitempty"`
|
||||||
DeadzoneStart int32 `yaml:"deadzone_start,omitempty"`
|
DeadzoneCenter int32 `yaml:"deadzone_center,omitempty"`
|
||||||
DeadzoneEnd int32 `yaml:"deadzone_end,omitempty"`
|
DeadzoneSize int32 `yaml:"deadzone_size,omitempty"`
|
||||||
Inverted bool `yaml:"inverted,omitempty"`
|
DeadzoneSizePercent int32 `yaml:"deadzone_size_percent,omitempty"`
|
||||||
Modes []string `yaml:"modes,omitempty"`
|
DeadzoneStart int32 `yaml:"deadzone_start,omitempty"`
|
||||||
|
DeadzoneEnd int32 `yaml:"deadzone_end,omitempty"`
|
||||||
|
Inverted bool `yaml:"inverted,omitempty"`
|
||||||
|
Modes []string `yaml:"modes,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type MappingRule interface {
|
type MappingRule interface {
|
||||||
MatchEvent(RuleTargetDevice, *evdev.InputEvent, *string) (*evdev.InputDevice, *evdev.InputEvent)
|
MatchEvent(Device, *evdev.InputEvent, *string) (*evdev.InputDevice, *evdev.InputEvent)
|
||||||
}
|
}
|
||||||
|
|
||||||
type TimedEventEmitter interface {
|
type TimedEventEmitter interface {
|
||||||
|
@ -35,13 +35,13 @@ type RuleTarget interface {
|
||||||
// for most implementations.
|
// for most implementations.
|
||||||
CreateEvent(int32, *string) *evdev.InputEvent
|
CreateEvent(int32, *string) *evdev.InputEvent
|
||||||
|
|
||||||
MatchEvent(device RuleTargetDevice, event *evdev.InputEvent) bool
|
MatchEvent(device Device, event *evdev.InputEvent) bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// RuleTargetDevice is an interface abstraction on top of evdev.InputDevice, implementing
|
// Device is an interface abstraction on top of evdev.InputDevice, implementing
|
||||||
// only the methods we need in this package. This is used for testing, and the
|
// only the methods we need in this package. This is used for testing, and the
|
||||||
// RuleTargetDevice can be safely cast to an *evdev.InputDevice when necessary.
|
// Device can be safely cast to an *evdev.InputDevice when necessary.
|
||||||
type RuleTargetDevice interface {
|
type Device interface {
|
||||||
AbsInfos() (map[evdev.EvCode]evdev.AbsInfo, error)
|
AbsInfos() (map[evdev.EvCode]evdev.AbsInfo, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ func NewMappingRuleAxis(base MappingRuleBase, input *RuleTargetAxis, output *Rul
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rule *MappingRuleAxis) MatchEvent(device RuleTargetDevice, event *evdev.InputEvent, mode *string) (*evdev.InputDevice, *evdev.InputEvent) {
|
func (rule *MappingRuleAxis) MatchEvent(device Device, event *evdev.InputEvent, mode *string) (*evdev.InputDevice, *evdev.InputEvent) {
|
||||||
if !rule.MappingRuleBase.modeCheck(mode) ||
|
if !rule.MappingRuleBase.modeCheck(mode) ||
|
||||||
!rule.Input.MatchEvent(device, event) {
|
!rule.Input.MatchEvent(device, event) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
|
@ -39,7 +39,7 @@ func NewMappingRuleAxisToButton(base MappingRuleBase, input *RuleTargetAxis, out
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rule *MappingRuleAxisToButton) MatchEvent(device RuleTargetDevice, event *evdev.InputEvent, mode *string) (*evdev.InputDevice, *evdev.InputEvent) {
|
func (rule *MappingRuleAxisToButton) MatchEvent(device Device, event *evdev.InputEvent, mode *string) (*evdev.InputDevice, *evdev.InputEvent) {
|
||||||
|
|
||||||
if !rule.MappingRuleBase.modeCheck(mode) ||
|
if !rule.MappingRuleBase.modeCheck(mode) ||
|
||||||
!rule.Input.MatchEventDeviceAndCode(device, event) {
|
!rule.Input.MatchEventDeviceAndCode(device, event) {
|
||||||
|
@ -105,5 +105,5 @@ func (rule *MappingRuleAxisToButton) TimerEvent() *evdev.InputEvent {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rule *MappingRuleAxisToButton) GetOutputDevice() *evdev.InputDevice {
|
func (rule *MappingRuleAxisToButton) GetOutputDevice() *evdev.InputDevice {
|
||||||
return rule.Output.Device
|
return rule.Output.Device.(*evdev.InputDevice)
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ func NewMappingRuleAxisToRelaxis(
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rule *MappingRuleAxisToRelaxis) MatchEvent(
|
func (rule *MappingRuleAxisToRelaxis) MatchEvent(
|
||||||
device RuleTargetDevice,
|
device Device,
|
||||||
event *evdev.InputEvent,
|
event *evdev.InputEvent,
|
||||||
mode *string) (*evdev.InputDevice, *evdev.InputEvent) {
|
mode *string) (*evdev.InputDevice, *evdev.InputEvent) {
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ func NewMappingRuleButton(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rule *MappingRuleButton) MatchEvent(device RuleTargetDevice, event *evdev.InputEvent, mode *string) (*evdev.InputDevice, *evdev.InputEvent) {
|
func (rule *MappingRuleButton) MatchEvent(device Device, event *evdev.InputEvent, mode *string) (*evdev.InputDevice, *evdev.InputEvent) {
|
||||||
if !rule.MappingRuleBase.modeCheck(mode) {
|
if !rule.MappingRuleBase.modeCheck(mode) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
@ -31,5 +31,5 @@ func (rule *MappingRuleButton) MatchEvent(device RuleTargetDevice, event *evdev.
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return rule.Output.Device, rule.Output.CreateEvent(rule.Input.NormalizeValue(event.Value), mode)
|
return rule.Output.Device.(*evdev.InputDevice), rule.Output.CreateEvent(rule.Input.NormalizeValue(event.Value), mode)
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,7 @@ func NewMappingRuleButtonCombo(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rule *MappingRuleButtonCombo) MatchEvent(device RuleTargetDevice, event *evdev.InputEvent, mode *string) (*evdev.InputDevice, *evdev.InputEvent) {
|
func (rule *MappingRuleButtonCombo) MatchEvent(device Device, event *evdev.InputEvent, mode *string) (*evdev.InputDevice, *evdev.InputEvent) {
|
||||||
if !rule.MappingRuleBase.modeCheck(mode) {
|
if !rule.MappingRuleBase.modeCheck(mode) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
@ -53,10 +53,10 @@ func (rule *MappingRuleButtonCombo) MatchEvent(device RuleTargetDevice, event *e
|
||||||
targetState := len(rule.Inputs)
|
targetState := len(rule.Inputs)
|
||||||
|
|
||||||
if oldState == targetState-1 && rule.State == targetState {
|
if oldState == targetState-1 && rule.State == targetState {
|
||||||
return rule.Output.Device, rule.Output.CreateEvent(1, mode)
|
return rule.Output.Device.(*evdev.InputDevice), rule.Output.CreateEvent(1, mode)
|
||||||
}
|
}
|
||||||
if oldState == targetState && rule.State == targetState-1 {
|
if oldState == targetState && rule.State == targetState-1 {
|
||||||
return rule.Output.Device, rule.Output.CreateEvent(0, mode)
|
return rule.Output.Device.(*evdev.InputDevice), rule.Output.CreateEvent(0, mode)
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ func NewMappingRuleButtonLatched(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rule *MappingRuleButtonLatched) MatchEvent(device RuleTargetDevice, event *evdev.InputEvent, mode *string) (*evdev.InputDevice, *evdev.InputEvent) {
|
func (rule *MappingRuleButtonLatched) MatchEvent(device Device, event *evdev.InputEvent, mode *string) (*evdev.InputDevice, *evdev.InputEvent) {
|
||||||
if !rule.MappingRuleBase.modeCheck(mode) {
|
if !rule.MappingRuleBase.modeCheck(mode) {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
@ -42,5 +42,5 @@ func (rule *MappingRuleButtonLatched) MatchEvent(device RuleTargetDevice, event
|
||||||
value = 0
|
value = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
return rule.Output.Device, rule.Output.CreateEvent(value, mode)
|
return rule.Output.Device.(*evdev.InputDevice), rule.Output.CreateEvent(value, mode)
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ func NewMappingRuleModeSelect(
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rule *MappingRuleModeSelect) MatchEvent(
|
func (rule *MappingRuleModeSelect) MatchEvent(
|
||||||
device RuleTargetDevice,
|
device Device,
|
||||||
event *evdev.InputEvent,
|
event *evdev.InputEvent,
|
||||||
mode *string) (*evdev.InputDevice, *evdev.InputEvent) {
|
mode *string) (*evdev.InputDevice, *evdev.InputEvent) {
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
|
|
||||||
type RuleTargetAxis struct {
|
type RuleTargetAxis struct {
|
||||||
DeviceName string
|
DeviceName string
|
||||||
Device RuleTargetDevice
|
Device Device
|
||||||
Axis evdev.EvCode
|
Axis evdev.EvCode
|
||||||
Inverted bool
|
Inverted bool
|
||||||
DeadzoneStart int32
|
DeadzoneStart int32
|
||||||
|
@ -19,7 +19,7 @@ type RuleTargetAxis struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRuleTargetAxis(device_name string,
|
func NewRuleTargetAxis(device_name string,
|
||||||
device RuleTargetDevice,
|
device Device,
|
||||||
axis evdev.EvCode,
|
axis evdev.EvCode,
|
||||||
inverted bool,
|
inverted bool,
|
||||||
deadzoneStart int32,
|
deadzoneStart int32,
|
||||||
|
@ -89,13 +89,13 @@ func (target *RuleTargetAxis) CreateEvent(value int32, mode *string) *evdev.Inpu
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (target *RuleTargetAxis) MatchEvent(device RuleTargetDevice, event *evdev.InputEvent) bool {
|
func (target *RuleTargetAxis) MatchEvent(device Device, event *evdev.InputEvent) bool {
|
||||||
return target.MatchEventDeviceAndCode(device, event) &&
|
return target.MatchEventDeviceAndCode(device, event) &&
|
||||||
!target.InDeadZone(event.Value)
|
!target.InDeadZone(event.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Add tests
|
// TODO: Add tests
|
||||||
func (target *RuleTargetAxis) MatchEventDeviceAndCode(device RuleTargetDevice, event *evdev.InputEvent) bool {
|
func (target *RuleTargetAxis) MatchEventDeviceAndCode(device Device, event *evdev.InputEvent) bool {
|
||||||
return device == target.Device &&
|
return device == target.Device &&
|
||||||
event.Type == evdev.EV_ABS &&
|
event.Type == evdev.EV_ABS &&
|
||||||
event.Code == target.Axis
|
event.Code == target.Axis
|
||||||
|
|
|
@ -4,12 +4,12 @@ import "github.com/holoplot/go-evdev"
|
||||||
|
|
||||||
type RuleTargetButton struct {
|
type RuleTargetButton struct {
|
||||||
DeviceName string
|
DeviceName string
|
||||||
Device *evdev.InputDevice
|
Device Device
|
||||||
Button evdev.EvCode
|
Button evdev.EvCode
|
||||||
Inverted bool
|
Inverted bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRuleTargetButton(device_name string, device *evdev.InputDevice, 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,
|
||||||
Device: device,
|
Device: device,
|
||||||
|
@ -36,7 +36,7 @@ func (target *RuleTargetButton) CreateEvent(value int32, _ *string) *evdev.Input
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (target *RuleTargetButton) MatchEvent(device RuleTargetDevice, event *evdev.InputEvent) bool {
|
func (target *RuleTargetButton) MatchEvent(device Device, event *evdev.InputEvent) bool {
|
||||||
return device == target.Device &&
|
return device == target.Device &&
|
||||||
event.Type == evdev.EV_KEY &&
|
event.Type == evdev.EV_KEY &&
|
||||||
event.Code == target.Button
|
event.Code == target.Button
|
||||||
|
|
|
@ -6,13 +6,13 @@ import (
|
||||||
|
|
||||||
type RuleTargetRelaxis struct {
|
type RuleTargetRelaxis struct {
|
||||||
DeviceName string
|
DeviceName string
|
||||||
Device RuleTargetDevice
|
Device Device
|
||||||
Axis evdev.EvCode
|
Axis evdev.EvCode
|
||||||
Inverted bool
|
Inverted bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewRuleTargetRelaxis(device_name string,
|
func NewRuleTargetRelaxis(device_name string,
|
||||||
device RuleTargetDevice,
|
device Device,
|
||||||
axis evdev.EvCode,
|
axis evdev.EvCode,
|
||||||
inverted bool) (*RuleTargetRelaxis, error) {
|
inverted bool) (*RuleTargetRelaxis, error) {
|
||||||
|
|
||||||
|
@ -41,6 +41,6 @@ func (target *RuleTargetRelaxis) CreateEvent(value int32, mode *string) *evdev.I
|
||||||
}
|
}
|
||||||
|
|
||||||
// Relative axis is only supported for output.
|
// Relative axis is only supported for output.
|
||||||
func (target *RuleTargetRelaxis) MatchEvent(device RuleTargetDevice, event *evdev.InputEvent) bool {
|
func (target *RuleTargetRelaxis) MatchEvent(device Device, event *evdev.InputEvent) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,8 +17,8 @@ Joyful is ideal for Linux gamers who enjoy space and flight sims and miss the fe
|
||||||
* "Split" axis mapping: map sections of an axis to different outputs using deadzones.
|
* "Split" axis mapping: map sections of an axis to different outputs using deadzones.
|
||||||
* Axis -> button mapping with optional "proportional" repeat speed (i.e. repeat faster as the axis is engaged further)
|
* Axis -> button mapping with optional "proportional" repeat speed (i.e. repeat faster as the axis is engaged further)
|
||||||
* Axis -> Relative Axis mapping, for converting a joystick axis to mouse movement and scrollwheel events.
|
* Axis -> Relative Axis mapping, for converting a joystick axis to mouse movement and scrollwheel events.
|
||||||
|
* Configure per-rule configurable deadzones for axes, with multiple ways to specify deadzones.
|
||||||
* Define multiple modes with per-mode behavior.
|
* Define multiple modes with per-mode behavior.
|
||||||
* Configure per-rule configurable deadzones for axes.
|
|
||||||
|
|
||||||
### Possible Future Features
|
### Possible Future Features
|
||||||
|
|
||||||
|
@ -27,7 +27,6 @@ Joyful is ideal for Linux gamers who enjoy space and flight sims and miss the fe
|
||||||
* Output keyboard button presses
|
* Output keyboard button presses
|
||||||
* Hat support
|
* Hat support
|
||||||
* HIDRAW support for more button options.
|
* HIDRAW support for more button options.
|
||||||
* Percentage-based deadzones.
|
|
||||||
* Sensitivity Curves.
|
* Sensitivity Curves.
|
||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
@ -40,7 +39,7 @@ Configuration can be fairly complicated and repetitive. If anyone wants to creat
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
After building (see below) and writing your configuration (see above), just run `joyful`. (Feel free to move this somewhere in your path. You can use `--config <directory>` to specify different configuration profiles.
|
After building (see below) and writing your configuration (see above), just run `joyful`. You can use `joyful --config <directory>` to specify different configuration profiles; just put all the YAML files for a given profile in a unique directory.
|
||||||
|
|
||||||
## Technical details
|
## Technical details
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue