Build rules from config.
This commit is contained in:
parent
50474f9fb2
commit
428749a519
7 changed files with 186 additions and 106 deletions
|
@ -8,6 +8,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"git.annabunches.net/annabunches/joyful/internal/logger"
|
||||
"git.annabunches.net/annabunches/joyful/internal/mappingrules"
|
||||
"github.com/goccy/go-yaml"
|
||||
"github.com/holoplot/go-evdev"
|
||||
)
|
||||
|
@ -131,6 +132,29 @@ func (parser *ConfigParser) ConnectPhysicalDevices() map[string]*evdev.InputDevi
|
|||
return deviceMap
|
||||
}
|
||||
|
||||
func (parser *ConfigParser) BuildRules(pDevs map[string]*evdev.InputDevice, vDevs map[string]*evdev.InputDevice) []mappingrules.MappingRule {
|
||||
rules := make([]mappingrules.MappingRule, 0)
|
||||
|
||||
for _, ruleConfig := range parser.config.Rules {
|
||||
var newRule mappingrules.MappingRule
|
||||
var err error
|
||||
switch strings.ToLower(ruleConfig.Type) {
|
||||
case RuleTypeSimple:
|
||||
newRule, err = makeSimpleRule(ruleConfig, pDevs, vDevs)
|
||||
case RuleTypeCombo:
|
||||
newRule, err = makeComboRule(ruleConfig, pDevs, vDevs)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logger.LogError(err, "")
|
||||
continue
|
||||
}
|
||||
rules = append(rules, newRule)
|
||||
}
|
||||
|
||||
return rules
|
||||
}
|
||||
|
||||
func makeButtons(numButtons int) []evdev.EvCode {
|
||||
if numButtons > 56 {
|
||||
numButtons = 56
|
||||
|
|
93
internal/config/rules.go
Normal file
93
internal/config/rules.go
Normal file
|
@ -0,0 +1,93 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.annabunches.net/annabunches/joyful/internal/mappingrules"
|
||||
"github.com/holoplot/go-evdev"
|
||||
)
|
||||
|
||||
func makeSimpleRule(ruleConfig RuleConfig, pDevs map[string]*evdev.InputDevice, vDevs map[string]*evdev.InputDevice) (mappingrules.MappingRule, error) {
|
||||
input, err := makeRuleTarget(ruleConfig.Input, pDevs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
output, err := makeRuleTarget(ruleConfig.Output, vDevs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &mappingrules.SimpleMappingRule{
|
||||
Input: input,
|
||||
Output: output,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func makeComboRule(ruleConfig RuleConfig, pDevs map[string]*evdev.InputDevice, vDevs map[string]*evdev.InputDevice) (mappingrules.MappingRule, error) {
|
||||
inputs := make([]mappingrules.RuleTarget, 0)
|
||||
for _, inputConfig := range ruleConfig.Inputs {
|
||||
input, err := makeRuleTarget(inputConfig, pDevs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
inputs = append(inputs, input)
|
||||
}
|
||||
|
||||
output, err := makeRuleTarget(ruleConfig.Output, vDevs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &mappingrules.ComboMappingRule{
|
||||
Inputs: inputs,
|
||||
Output: output,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// makeInputRuleTarget takes an Input declaration from the YAML and returns a fully formed RuleTarget.
|
||||
func makeRuleTarget(targetConfig RuleTargetConfig, devs map[string]*evdev.InputDevice) (mappingrules.RuleTarget, error) {
|
||||
ruleTarget := mappingrules.RuleTarget{}
|
||||
|
||||
device, ok := devs[targetConfig.Device]
|
||||
if !ok {
|
||||
return mappingrules.RuleTarget{}, fmt.Errorf("couldn't build rule due to non-existent device '%s'", targetConfig.Device)
|
||||
}
|
||||
ruleTarget.Device = device
|
||||
|
||||
eventType, eventCode, err := decodeRuleTargetValues(targetConfig)
|
||||
if err != nil {
|
||||
return ruleTarget, err
|
||||
}
|
||||
ruleTarget.Type = eventType
|
||||
ruleTarget.Code = eventCode
|
||||
|
||||
return ruleTarget, nil
|
||||
}
|
||||
|
||||
// decodeRuleTargetValues returns the appropriate evdev.EvType and evdev.EvCode values
|
||||
// for a given RuleTargetConfig, converting the config file strings into appropriate constants
|
||||
//
|
||||
// Todo: support different formats for key specification
|
||||
func decodeRuleTargetValues(target RuleTargetConfig) (evdev.EvType, evdev.EvCode, error) {
|
||||
var eventType evdev.EvType
|
||||
var eventCode evdev.EvCode
|
||||
var ok bool
|
||||
|
||||
if target.Button != "" {
|
||||
eventType = evdev.EV_KEY
|
||||
eventCode, ok = evdev.KEYFromString[target.Button]
|
||||
if !ok {
|
||||
return 0, 0, fmt.Errorf("skipping rule due to invalid button code '%s'", target.Button)
|
||||
}
|
||||
}
|
||||
if target.Axis != "" {
|
||||
eventType = evdev.EV_ABS
|
||||
eventCode, ok = evdev.ABSFromString[target.Axis]
|
||||
if !ok {
|
||||
return 0, 0, fmt.Errorf("skipping rule due to invalid axis code '%s'", target.Button)
|
||||
}
|
||||
}
|
||||
|
||||
return eventType, eventCode, nil
|
||||
}
|
|
@ -20,24 +20,17 @@ type DeviceConfig struct {
|
|||
}
|
||||
|
||||
type RuleConfig struct {
|
||||
Name string `yaml:"name,omitempty"`
|
||||
Type string `yaml:"type"`
|
||||
Input RuleInputConfig `yaml:"input,omitempty"`
|
||||
Inputs []RuleInputConfig `yaml:"inputs,omitempty"`
|
||||
Output RuleOutputConfig `yaml:"output"`
|
||||
Name string `yaml:"name,omitempty"`
|
||||
Type string `yaml:"type"`
|
||||
Input RuleTargetConfig `yaml:"input,omitempty"`
|
||||
Inputs []RuleTargetConfig `yaml:"inputs,omitempty"`
|
||||
Output RuleTargetConfig `yaml:"output"`
|
||||
}
|
||||
|
||||
type RuleInputConfig struct {
|
||||
type RuleTargetConfig struct {
|
||||
Device string `yaml:"device"`
|
||||
Button string `yaml:"button,omitempty"`
|
||||
Buttons []string `yaml:"buttons,omitempty"`
|
||||
Axis string `yaml:"axis,omitempty"`
|
||||
Inverted bool `yaml:"inverted,omitempty"`
|
||||
}
|
||||
|
||||
type RuleOutputConfig struct {
|
||||
Device string `yaml:"device,omitempty"`
|
||||
Button string `yaml:"button,omitempty"`
|
||||
Axis string `yaml:"axis,omitempty"`
|
||||
Groups string `yaml:"groups,omitempty"`
|
||||
Groups []string `yaml:"groups,omitempty"`
|
||||
}
|
||||
|
|
|
@ -1,27 +1,11 @@
|
|||
package rules
|
||||
package mappingrules
|
||||
|
||||
import (
|
||||
"git.annabunches.net/annabunches/joyful/internal/logger"
|
||||
"github.com/holoplot/go-evdev"
|
||||
)
|
||||
|
||||
type KeyMappingRule interface {
|
||||
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 {
|
||||
Input RuleTarget
|
||||
Output RuleTarget
|
||||
}
|
||||
|
||||
// A Combo Mapping Rule can require multiple physical button presses for a single output button
|
||||
type ComboMappingRule struct {
|
||||
Input []RuleTarget
|
||||
Output RuleTarget
|
||||
State int
|
||||
}
|
||||
|
||||
// eventFromTarget creates an outputtable event from a RuleTarget
|
||||
func eventFromTarget(output RuleTarget, value int32) *evdev.InputEvent {
|
||||
return &evdev.InputEvent{
|
||||
Type: output.Type,
|
||||
|
@ -30,6 +14,7 @@ func eventFromTarget(output RuleTarget, value int32) *evdev.InputEvent {
|
|||
}
|
||||
}
|
||||
|
||||
// valueFromTarget determines the value to output from an input specification,given a RuleTarget's constraints
|
||||
func valueFromTarget(rule RuleTarget, event *evdev.InputEvent) int32 {
|
||||
// how we process inverted rules depends on the event type
|
||||
value := event.Value
|
||||
|
@ -51,7 +36,7 @@ func valueFromTarget(rule RuleTarget, event *evdev.InputEvent) int32 {
|
|||
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 ||
|
||||
event.Code != rule.Input.Code {
|
||||
return nil
|
||||
|
@ -60,10 +45,10 @@ func (rule *SimpleMappingRule) MatchEvent(device *evdev.InputDevice, event *evde
|
|||
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
|
||||
var match *RuleTarget
|
||||
for _, input := range rule.Input {
|
||||
for _, input := range rule.Inputs {
|
||||
if device == input.Device &&
|
||||
event.Code == input.Code {
|
||||
match = &input
|
||||
|
@ -83,7 +68,7 @@ func (rule *ComboMappingRule) MatchEvent(device *evdev.InputDevice, event *evdev
|
|||
if inputValue == 1 {
|
||||
rule.State++
|
||||
}
|
||||
targetState := len(rule.Input)
|
||||
targetState := len(rule.Inputs)
|
||||
if oldState == targetState-1 && rule.State == targetState {
|
||||
return eventFromTarget(rule.Output, 1)
|
||||
}
|
27
internal/mappingrules/types.go
Normal file
27
internal/mappingrules/types.go
Normal file
|
@ -0,0 +1,27 @@
|
|||
package mappingrules
|
||||
|
||||
import "github.com/holoplot/go-evdev"
|
||||
|
||||
type MappingRule interface {
|
||||
MatchEvent(*evdev.InputDevice, *evdev.InputEvent) *evdev.InputEvent
|
||||
}
|
||||
|
||||
// A Simple Mapping Rule can map a button to a button or an axis to an axis.
|
||||
type SimpleMappingRule struct {
|
||||
Input RuleTarget
|
||||
Output RuleTarget
|
||||
}
|
||||
|
||||
// A Combo Mapping Rule can require multiple physical button presses for a single output button
|
||||
type ComboMappingRule struct {
|
||||
Inputs []RuleTarget
|
||||
Output RuleTarget
|
||||
State int
|
||||
}
|
||||
|
||||
type RuleTarget struct {
|
||||
Device *evdev.InputDevice
|
||||
Type evdev.EvType
|
||||
Code evdev.EvCode
|
||||
Inverted bool
|
||||
}
|
|
@ -1,10 +0,0 @@
|
|||
package rules
|
||||
|
||||
import "github.com/holoplot/go-evdev"
|
||||
|
||||
type RuleTarget struct {
|
||||
Device *evdev.InputDevice
|
||||
Type evdev.EvType
|
||||
Code evdev.EvCode
|
||||
Inverted bool
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue