Fix combo rules and add some example config.

This commit is contained in:
Anna Rose Wiggins 2025-07-02 14:53:52 -04:00
parent 428749a519
commit a5b59bf39e
6 changed files with 93 additions and 6 deletions

11
examples/devices.yml Normal file
View file

@ -0,0 +1,11 @@
devices:
- name: main
type: virtual
buttons: 56
axes: 8
- name: right-stick
type: physical
device_name: VIRPIL Controls 20220407 R-VPC Stick MT-50CM2
- name: left-stick
type: physical
device_name: VIRPIL Controls 20220407 L-VPC Stick MT-50CM2

70
examples/rules.yml Normal file
View file

@ -0,0 +1,70 @@
rules:
# A simple 1:1 mapping of 2 inputs
- name: basic mapping
type: simple
input:
device: right-stick
button: BTN_BASE6
output:
device: main
button: BTN_BASE6
# A couple of axis mappings
- name: x axis
type: simple
input:
device: right-stick
axis: ABS_X
output:
device: main
axis: ABS_X
- name: y axis
type: simple
input:
device: right-stick
axis: ABS_Y
output:
device: main
axis: ABS_Y
# 3-stage trigger for a VPC Constellation Alpha
# This only sends a trigger input when the
# flip trigger is pulled all the way in *and* the inner trigger
# is activated
- name: 3 stage trigger
type: combo
inputs:
- device: right-stick
button: BTN_TRIGGER
inverted: true
- device: right-stick
button: BTN_THUMB
- device: right-stick
button: BTN_THUMB2
output:
device: main
button: BTN_TRIGGER
# Of course, BTN_TRIGGER above is unnecessary, because on the Constellation Alpha
# it's impossible to press BTN_THUMB without disabling BTN_TRIGGER. So this is a
# simpler form of the same rule for the left stick
- name: 2 stage trigger
type: combo
inputs:
- device: left-stick
button: BTN_THUMB
- device: left-stick
button: BTN_THUMB2
output:
device: main
button: BTN_THUMB
# You can use the same input in multiple rules. Be careful with this!
# In this example we use the same 2-stage guard, but trigger an additional output
# when the final stage of the VPC's trigger is depressed
- name: alternate 2 stage trigger
type: combo
inputs:
- device: left-stick
button: BTN_THUMB
- device: left-stick
button: BTN_TOP
output:
device: main
button: BTN_THUMB2

View file

@ -21,6 +21,7 @@ func makeSimpleRule(ruleConfig RuleConfig, pDevs map[string]*evdev.InputDevice,
return &mappingrules.SimpleMappingRule{ return &mappingrules.SimpleMappingRule{
Input: input, Input: input,
Output: output, Output: output,
Name: ruleConfig.Name,
}, nil }, nil
} }
@ -42,6 +43,7 @@ func makeComboRule(ruleConfig RuleConfig, pDevs map[string]*evdev.InputDevice, v
return &mappingrules.ComboMappingRule{ return &mappingrules.ComboMappingRule{
Inputs: inputs, Inputs: inputs,
Output: output, Output: output,
Name: ruleConfig.Name,
}, nil }, nil
} }
@ -61,6 +63,7 @@ func makeRuleTarget(targetConfig RuleTargetConfig, devs map[string]*evdev.InputD
} }
ruleTarget.Type = eventType ruleTarget.Type = eventType
ruleTarget.Code = eventCode ruleTarget.Code = eventCode
ruleTarget.Inverted = targetConfig.Inverted
return ruleTarget, nil return ruleTarget, nil
} }

View file

@ -10,7 +10,7 @@ func Log(msg string) {
} }
func Logf(msg string, params ...interface{}) { func Logf(msg string, params ...interface{}) {
fmt.Printf(msg, params...) fmt.Printf(msg+"\n", params...)
} }
func LogError(err error, msg string) { func LogError(err error, msg string) {

View file

@ -27,16 +27,16 @@ func valueFromTarget(rule RuleTarget, event *evdev.InputEvent) int32 {
value = 0 value = 0
} }
case evdev.EV_ABS: case evdev.EV_ABS:
// TODO: how would we invert axes? logger.Logf("STUB: Inverting axes is not yet implemented.")
default: default:
logger.Logf("Inverted rule for unknown event type '%d'. Not inverting value\n", event.Type) logger.Logf("Inverted rule for unknown event type '%d'. Not inverting value", event.Type)
} }
} }
return value return value
} }
func (rule SimpleMappingRule) MatchEvent(device *evdev.InputDevice, event *evdev.InputEvent) *evdev.InputEvent { func (rule *SimpleMappingRule) MatchEvent(device *evdev.InputDevice, event *evdev.InputEvent) *evdev.InputEvent {
if device != rule.Input.Device || if device != rule.Input.Device ||
event.Code != rule.Input.Code { event.Code != rule.Input.Code {
return nil return nil
@ -45,7 +45,7 @@ func (rule SimpleMappingRule) MatchEvent(device *evdev.InputDevice, event *evdev
return eventFromTarget(rule.Output, valueFromTarget(rule.Input, event)) return eventFromTarget(rule.Output, valueFromTarget(rule.Input, event))
} }
func (rule ComboMappingRule) MatchEvent(device *evdev.InputDevice, event *evdev.InputEvent) *evdev.InputEvent { func (rule *ComboMappingRule) MatchEvent(device *evdev.InputDevice, event *evdev.InputEvent) *evdev.InputEvent {
// Check each of the inputs, and if we find a match, proceed // Check each of the inputs, and if we find a match, proceed
var match *RuleTarget var match *RuleTarget
for _, input := range rule.Inputs { for _, input := range rule.Inputs {
@ -63,12 +63,13 @@ func (rule ComboMappingRule) MatchEvent(device *evdev.InputDevice, event *evdev.
inputValue := valueFromTarget(*match, event) inputValue := valueFromTarget(*match, event)
oldState := rule.State oldState := rule.State
if inputValue == 0 { if inputValue == 0 {
rule.State-- rule.State = max(rule.State-1, 0)
} }
if inputValue == 1 { if inputValue == 1 {
rule.State++ rule.State++
} }
targetState := len(rule.Inputs) targetState := len(rule.Inputs)
if oldState == targetState-1 && rule.State == targetState { if oldState == targetState-1 && rule.State == targetState {
return eventFromTarget(rule.Output, 1) return eventFromTarget(rule.Output, 1)
} }

View file

@ -10,12 +10,14 @@ type MappingRule interface {
type SimpleMappingRule struct { type SimpleMappingRule struct {
Input RuleTarget Input RuleTarget
Output RuleTarget Output RuleTarget
Name string
} }
// A Combo Mapping Rule can require multiple physical button presses for a single output button // A Combo Mapping Rule can require multiple physical button presses for a single output button
type ComboMappingRule struct { type ComboMappingRule struct {
Inputs []RuleTarget Inputs []RuleTarget
Output RuleTarget Output RuleTarget
Name string
State int State int
} }