Start rulemapping refactor to be more explicit about typing intentions.

This commit is contained in:
Anna Rose Wiggins 2025-07-06 17:22:05 -04:00
parent 08fc828b46
commit a0949e719f
10 changed files with 128 additions and 100 deletions

View file

@ -3,8 +3,7 @@ package mappingrules
import "github.com/holoplot/go-evdev"
type MappingRule interface {
MatchEvent(*evdev.InputDevice, *evdev.InputEvent, *string) *evdev.InputEvent
OutputName() string
MatchEvent(*evdev.InputDevice, *evdev.InputEvent, *string) (*evdev.InputDevice, *evdev.InputEvent)
}
// RuleTargets represent either a device input to match on, or an output to produce.
@ -21,8 +20,4 @@ type RuleTarget interface {
//
// TODO: should we normalize inside this function to simplify the interface?
CreateEvent(int32, *string) *evdev.InputEvent
GetCode() evdev.EvCode
GetDeviceName() string
GetDevice() *evdev.InputDevice
}

View file

@ -3,13 +3,8 @@ package mappingrules
import "slices"
type MappingRuleBase struct {
Name string
Output RuleTarget
Modes []string
}
func (rule *MappingRuleBase) OutputName() string {
return rule.Output.GetDeviceName()
Name string
Modes []string
}
func (rule *MappingRuleBase) modeCheck(mode *string) bool {

View file

@ -0,0 +1,23 @@
package mappingrules
import "github.com/holoplot/go-evdev"
// A Simple Mapping Rule can map a button to a button or an axis to an axis.
type MappingRuleButton struct {
MappingRuleBase
Input *RuleTargetButton
Output *RuleTargetButton
}
func (rule *MappingRuleButton) MatchEvent(device *evdev.InputDevice, event *evdev.InputEvent, mode *string) (*evdev.InputDevice, *evdev.InputEvent) {
if !rule.MappingRuleBase.modeCheck(mode) {
return nil, nil
}
if device != rule.Input.GetDevice() ||
event.Code != rule.Input.GetCode() {
return nil, nil
}
return rule.Output.Device, rule.Output.CreateEvent(rule.Input.NormalizeValue(event.Value), mode)
}

View file

@ -7,17 +7,17 @@ import (
"github.com/stretchr/testify/suite"
)
type SimpleMappingRuleTests struct {
type MappingRuleButtonTests struct {
suite.Suite
inputDevice *evdev.InputDevice
wrongInputDevice *evdev.InputDevice
outputDevice *evdev.InputDevice
mode *string
sampleRule *MappingRuleSimple
invertedRule *MappingRuleSimple
sampleRule *MappingRuleButton
invertedRule *MappingRuleButton
}
func (t *SimpleMappingRuleTests) SetupTest() {
func (t *MappingRuleButtonTests) SetupTest() {
t.inputDevice = &evdev.InputDevice{}
t.wrongInputDevice = &evdev.InputDevice{}
t.outputDevice = &evdev.InputDevice{}
@ -25,24 +25,24 @@ func (t *SimpleMappingRuleTests) SetupTest() {
t.mode = &mode
// TODO: implement a constructor function...
t.sampleRule = &MappingRuleSimple{
t.sampleRule = &MappingRuleButton{
MappingRuleBase: MappingRuleBase{
Output: NewRuleTargetButton("", t.outputDevice, evdev.BTN_TRIGGER, false),
Modes: []string{"*"},
Modes: []string{"*"},
},
Input: NewRuleTargetButton("", t.inputDevice, evdev.BTN_TRIGGER, false),
Input: NewRuleTargetButton("", t.inputDevice, evdev.BTN_TRIGGER, false),
Output: NewRuleTargetButton("", t.outputDevice, evdev.BTN_TRIGGER, false),
}
t.invertedRule = &MappingRuleSimple{
t.invertedRule = &MappingRuleButton{
MappingRuleBase: MappingRuleBase{
Output: NewRuleTargetButton("", t.outputDevice, evdev.BTN_TRIGGER, false),
Modes: []string{"*"},
Modes: []string{"*"},
},
Input: NewRuleTargetButton("", t.inputDevice, evdev.BTN_TRIGGER, true),
Output: NewRuleTargetButton("", t.outputDevice, evdev.BTN_TRIGGER, false),
Input: NewRuleTargetButton("", t.inputDevice, evdev.BTN_TRIGGER, true),
}
}
func (t *SimpleMappingRuleTests) TestMatchEvent() {
func (t *MappingRuleButtonTests) TestMatchEvent() {
// A matching input event should produce an output event
correctOutput := &evdev.InputEvent{
Type: evdev.EV_KEY,
@ -50,25 +50,25 @@ func (t *SimpleMappingRuleTests) TestMatchEvent() {
Value: 1,
}
event := t.sampleRule.MatchEvent(
_, event := t.sampleRule.MatchEvent(
t.inputDevice,
&evdev.InputEvent{Code: evdev.BTN_TRIGGER, Value: 1}, t.mode)
t.EqualValues(correctOutput, event)
// An input event from the wrong device should produce a nil event
event = t.sampleRule.MatchEvent(
_, event = t.sampleRule.MatchEvent(
t.wrongInputDevice,
&evdev.InputEvent{Code: evdev.BTN_TRIGGER, Value: 1}, t.mode)
t.Nil(event)
// An input event from the wrong button should produce a nil event
event = t.sampleRule.MatchEvent(
_, event = t.sampleRule.MatchEvent(
t.inputDevice,
&evdev.InputEvent{Code: evdev.BTN_TOP, Value: 1}, t.mode)
t.Nil(event)
}
func (t *SimpleMappingRuleTests) TestMatchEventInverted() {
func (t *MappingRuleButtonTests) TestMatchEventInverted() {
// A matching input event should produce an output event
correctOutput := &evdev.InputEvent{
Type: evdev.EV_KEY,
@ -77,18 +77,18 @@ func (t *SimpleMappingRuleTests) TestMatchEventInverted() {
// Should get the opposite value out that we send in
correctOutput.Value = 0
event := t.invertedRule.MatchEvent(
_, event := t.invertedRule.MatchEvent(
t.inputDevice,
&evdev.InputEvent{Code: evdev.BTN_TRIGGER, Value: 1}, t.mode)
t.EqualValues(correctOutput, event)
correctOutput.Value = 1
event = t.invertedRule.MatchEvent(
_, event = t.invertedRule.MatchEvent(
t.inputDevice,
&evdev.InputEvent{Code: evdev.BTN_TRIGGER, Value: 0}, t.mode)
t.EqualValues(correctOutput, event)
}
func TestRunnerMatching(t *testing.T) {
suite.Run(t, new(SimpleMappingRuleTests))
suite.Run(t, new(MappingRuleButtonTests))
}

View file

@ -5,13 +5,14 @@ import "github.com/holoplot/go-evdev"
// A Combo Mapping Rule can require multiple physical button presses for a single output button
type MappingRuleCombo struct {
MappingRuleBase
Inputs []RuleTarget
Inputs []*RuleTargetButton
Output *RuleTargetButton
State int
}
func (rule *MappingRuleCombo) MatchEvent(device *evdev.InputDevice, event *evdev.InputEvent, mode *string) *evdev.InputEvent {
func (rule *MappingRuleCombo) MatchEvent(device *evdev.InputDevice, event *evdev.InputEvent, mode *string) (*evdev.InputDevice, *evdev.InputEvent) {
if !rule.MappingRuleBase.modeCheck(mode) {
return nil
return nil, nil
}
// Check each of the inputs, and if we find a match, proceed
@ -24,7 +25,7 @@ func (rule *MappingRuleCombo) MatchEvent(device *evdev.InputDevice, event *evdev
}
if match == nil {
return nil
return nil, nil
}
// Get the value and add/subtract it from State
@ -39,10 +40,10 @@ func (rule *MappingRuleCombo) MatchEvent(device *evdev.InputDevice, event *evdev
targetState := len(rule.Inputs)
if oldState == targetState-1 && rule.State == targetState {
return rule.Output.CreateEvent(1, mode)
return rule.Output.GetDevice(), rule.Output.CreateEvent(1, mode)
}
if oldState == targetState && rule.State == targetState-1 {
return rule.Output.CreateEvent(0, mode)
return rule.Output.GetDevice(), rule.Output.CreateEvent(0, mode)
}
return nil
return nil, nil
}

View file

@ -4,19 +4,20 @@ import "github.com/holoplot/go-evdev"
type MappingRuleLatched struct {
MappingRuleBase
Input RuleTarget
State bool
Input *RuleTargetButton
Output *RuleTargetButton
State bool
}
func (rule *MappingRuleLatched) MatchEvent(device *evdev.InputDevice, event *evdev.InputEvent, mode *string) *evdev.InputEvent {
func (rule *MappingRuleLatched) MatchEvent(device *evdev.InputDevice, event *evdev.InputEvent, mode *string) (*evdev.InputDevice, *evdev.InputEvent) {
if !rule.MappingRuleBase.modeCheck(mode) {
return nil
return nil, nil
}
if device != rule.Input.GetDevice() ||
event.Code != rule.Input.GetCode() ||
if device != rule.Input.Device ||
event.Code != rule.Input.Code ||
rule.Input.NormalizeValue(event.Value) == 0 {
return nil
return nil, nil
}
// Input is pressed, so toggle state and emit event
@ -28,5 +29,5 @@ func (rule *MappingRuleLatched) MatchEvent(device *evdev.InputDevice, event *evd
value = 0
}
return rule.Output.CreateEvent(value, mode)
return rule.Output.Device, rule.Output.CreateEvent(value, mode)
}

View file

@ -16,19 +16,19 @@ type MappingRuleProportionalAxis struct {
LastEvent time.Time
}
func (rule *MappingRuleProportionalAxis) MatchEvent(device *evdev.InputDevice, event *evdev.InputEvent, mode *string) *evdev.InputEvent {
func (rule *MappingRuleProportionalAxis) MatchEvent(device *evdev.InputDevice, event *evdev.InputEvent, mode *string) (*evdev.InputDevice, *evdev.InputEvent) {
if !rule.MappingRuleBase.modeCheck(mode) {
return nil
return nil, nil
}
if device != rule.Input.GetDevice() ||
event.Code != rule.Input.GetCode() {
return nil
return nil, nil
}
// set the last value to the normalized input value
rule.LastValue = rule.Input.NormalizeValue(event.Value)
return nil
return nil, nil
}
// TimerEvent returns an event when enough time has passed (compared to the last recorded axis value)

View file

@ -1,22 +0,0 @@
package mappingrules
import "github.com/holoplot/go-evdev"
// A Simple Mapping Rule can map a button to a button or an axis to an axis.
type MappingRuleSimple struct {
MappingRuleBase
Input RuleTarget
}
func (rule *MappingRuleSimple) MatchEvent(device *evdev.InputDevice, event *evdev.InputEvent, mode *string) *evdev.InputEvent {
if !rule.MappingRuleBase.modeCheck(mode) {
return nil
}
if device != rule.Input.GetDevice() ||
event.Code != rule.Input.GetCode() {
return nil
}
return rule.Output.CreateEvent(rule.Input.NormalizeValue(event.Value), mode)
}