Implement rule matching for a couple of basic rule types.

This commit is contained in:
Anna Rose Wiggins 2025-07-01 17:49:23 -04:00
parent 5b3b70da14
commit 970b3ded6e
3 changed files with 64 additions and 15 deletions

View file

@ -22,7 +22,8 @@ type DeviceConfig struct {
type RuleConfig struct { type RuleConfig struct {
Name string `yaml:"name,omitempty"` Name string `yaml:"name,omitempty"`
Type string `yaml:"type"` Type string `yaml:"type"`
Input []RuleInputConfig `yaml:"input"` Input RuleInputConfig `yaml:"input,omitempty"`
Inputs []RuleInputConfig `yaml:"inputs,omitempty"`
Output RuleOutputConfig `yaml:"output"` Output RuleOutputConfig `yaml:"output"`
} }

View file

@ -9,6 +9,10 @@ func Log(msg string) {
fmt.Println(msg) fmt.Println(msg)
} }
func Logf(msg string, params ...interface{}) {
fmt.Printf(msg, params...)
}
func LogError(err error, msg string) { func LogError(err error, msg string) {
if msg == "" { if msg == "" {
fmt.Printf("%s\n", err.Error()) fmt.Printf("%s\n", err.Error())

View file

@ -9,42 +9,86 @@ type KeyMappingRule interface {
MatchEvent(*evdev.InputDevice, *evdev.InputEvent) MatchEvent(*evdev.InputDevice, *evdev.InputEvent)
} }
// A Simple Mapping Rule can map a button to a button or an axis to an axis.
type SimpleMappingRule struct { type SimpleMappingRule struct {
Input RuleTarget Input RuleTarget
Output RuleTarget Output RuleTarget
} }
// A Combo Mapping Rule can require multiple physical button presses for a single output button
type ComboMappingRule struct { type ComboMappingRule struct {
Input []RuleTarget Input []RuleTarget
Output RuleTarget Output RuleTarget
State int
} }
func (rule *SimpleMappingRule) MatchEvent(device *evdev.InputDevice, event *evdev.InputEvent) *evdev.InputEvent { func eventFromTarget(output RuleTarget, value int32) *evdev.InputEvent {
if event.Type != evdev.EV_KEY || return &evdev.InputEvent{
device != rule.Input.Device || Type: output.Type,
event.Code != rule.Input.Code { Code: output.Code,
return nil Value: value,
} }
}
func valueFromTarget(rule RuleTarget, event *evdev.InputEvent) int32 {
// how we process inverted rules depends on the event type // how we process inverted rules depends on the event type
value := event.Value value := event.Value
if rule.Input.Inverted { if rule.Inverted {
switch rule.Input.Type { switch rule.Type {
case evdev.EV_KEY: case evdev.EV_KEY:
if value == 0 { if value == 0 {
value = 1 value = 1
} else { } else {
value = 0 value = 0
} }
case evdev.EV_ABS:
// TODO: how would we invert axes?
default: default:
logger.Log("Tried to handle inverted rule for unknown event type. Skipping rule.") logger.Logf("Inverted rule for unknown event type '%d'. Not inverting value\n", event.Type)
return nil
} }
} }
return &evdev.InputEvent{ return value
Type: rule.Output.Type, }
Code: rule.Output.Code,
Value: value, func (rule *SimpleMappingRule) MatchEvent(device *evdev.InputDevice, event *evdev.InputEvent) *evdev.InputEvent {
} if device != rule.Input.Device ||
event.Code != rule.Input.Code {
return nil
}
return eventFromTarget(rule.Output, valueFromTarget(rule.Input, event))
}
func (rule *ComboMappingRule) MatchEvent(device *evdev.InputDevice, event *evdev.InputEvent) *evdev.InputEvent {
// Check each of the inputs, and if we find a match, proceed
var match *RuleTarget
for _, input := range rule.Input {
if device == input.Device &&
event.Code == input.Code {
match = &input
}
}
if match == nil {
return nil
}
// Get the value and add/subtract it from State
inputValue := valueFromTarget(*match, event)
oldState := rule.State
if inputValue == 0 {
rule.State--
}
if inputValue == 1 {
rule.State++
}
targetState := len(rule.Input)
if oldState == targetState-1 && rule.State == targetState {
return eventFromTarget(rule.Output, 1)
}
if oldState == targetState && rule.State == targetState-1 {
return eventFromTarget(rule.Output, 0)
}
return nil
} }