Big Refactor (#2)
Refactor Everything. Co-authored-by: Anna Rose Wiggins <annabunches@gmail.com> Co-committed-by: Anna Rose Wiggins <annabunches@gmail.com>
This commit is contained in:
parent
a0949e719f
commit
ff38db6596
21 changed files with 413 additions and 309 deletions
|
@ -75,7 +75,7 @@ func main() {
|
|||
|
||||
timerCount := 0
|
||||
for _, rule := range rules {
|
||||
if timedRule, ok := rule.(*mappingrules.MappingRuleProportionalAxis); ok {
|
||||
if timedRule, ok := rule.(*mappingrules.MappingRuleAxisToButton); ok {
|
||||
go timerWatcher(timedRule, eventChannel)
|
||||
timerCount++
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ func eventWatcher(device *evdev.InputDevice, channel chan<- ChannelEvent) {
|
|||
}
|
||||
}
|
||||
|
||||
func timerWatcher(rule *mappingrules.MappingRuleProportionalAxis, channel chan<- ChannelEvent) {
|
||||
func timerWatcher(rule *mappingrules.MappingRuleAxisToButton, channel chan<- ChannelEvent) {
|
||||
for {
|
||||
event := rule.TimerEvent()
|
||||
if event != nil {
|
||||
|
|
56
internal/config/make_rule_targets.go
Normal file
56
internal/config/make_rule_targets.go
Normal file
|
@ -0,0 +1,56 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"git.annabunches.net/annabunches/joyful/internal/mappingrules"
|
||||
"github.com/holoplot/go-evdev"
|
||||
)
|
||||
|
||||
func makeRuleTargetButton(targetConfig RuleTargetConfig, devs map[string]*evdev.InputDevice) (*mappingrules.RuleTargetButton, error) {
|
||||
device, ok := devs[targetConfig.Device]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("non-existent device '%s'", targetConfig.Device)
|
||||
}
|
||||
|
||||
eventCode, ok := evdev.KEYFromString[targetConfig.Button]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid button code '%s'", targetConfig.Button)
|
||||
}
|
||||
|
||||
return mappingrules.NewRuleTargetButton(
|
||||
targetConfig.Device,
|
||||
device,
|
||||
eventCode,
|
||||
targetConfig.Inverted,
|
||||
), nil
|
||||
}
|
||||
|
||||
func makeRuleTargetAxis(targetConfig RuleTargetConfig, devs map[string]*evdev.InputDevice) (*mappingrules.RuleTargetAxis, error) {
|
||||
device, ok := devs[targetConfig.Device]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("non-existent device '%s'", targetConfig.Device)
|
||||
}
|
||||
|
||||
eventCode, ok := evdev.ABSFromString[targetConfig.Axis]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid button code '%s'", targetConfig.Button)
|
||||
}
|
||||
|
||||
return mappingrules.NewRuleTargetAxis(
|
||||
targetConfig.Device,
|
||||
device,
|
||||
eventCode,
|
||||
targetConfig.Inverted,
|
||||
0, 0, 0, // TODO: replace these with real values
|
||||
), nil
|
||||
}
|
||||
|
||||
func makeRuleTargetModeSelect(targetConfig RuleTargetConfig, allModes []string) (*mappingrules.RuleTargetModeSelect, error) {
|
||||
if ok := validateModes(targetConfig.Modes, allModes); !ok {
|
||||
return nil, errors.New("undefined mode in mode select list")
|
||||
}
|
||||
|
||||
return mappingrules.NewRuleTargetModeSelect(targetConfig.Modes)
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
package config
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"git.annabunches.net/annabunches/joyful/internal/logger"
|
||||
|
@ -23,23 +23,32 @@ func (parser *ConfigParser) BuildRules(pDevs map[string]*evdev.InputDevice, vDev
|
|||
var newRule mappingrules.MappingRule
|
||||
var err error
|
||||
|
||||
baseParams, err := setBaseRuleParameters(ruleConfig, modes)
|
||||
if err != nil {
|
||||
logger.LogErrorf(err, "couldn't set output parameters, skipping rule '%s'", ruleConfig.Name)
|
||||
if ok := validateModes(ruleConfig.Modes, modes); !ok {
|
||||
logger.Logf("Skipping rule '%s', mode list specifies undefined mode.", ruleConfig.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
base := mappingrules.NewMappingRuleBase(ruleConfig.Name, ruleConfig.Modes)
|
||||
|
||||
switch strings.ToLower(ruleConfig.Type) {
|
||||
case RuleTypeSimple:
|
||||
newRule, err = makeMappingRuleButton(ruleConfig, pDevs, vDevs, baseParams)
|
||||
case RuleTypeCombo:
|
||||
newRule, err = makeComboRule(ruleConfig, pDevs, vDevs, baseParams)
|
||||
case RuleTypeButton:
|
||||
newRule, err = makeMappingRuleButton(ruleConfig, pDevs, vDevs, base)
|
||||
case RuleTypeButtonCombo:
|
||||
newRule, err = makeMappingRuleCombo(ruleConfig, pDevs, vDevs, base)
|
||||
case RuleTypeLatched:
|
||||
newRule, err = makeLatchedRule(ruleConfig, pDevs, vDevs, baseParams)
|
||||
newRule, err = makeMappingRuleLatched(ruleConfig, pDevs, vDevs, base)
|
||||
case RuleTypeAxis:
|
||||
newRule, err = makeMappingRuleAxis(ruleConfig, pDevs, vDevs, base)
|
||||
case RuleTypeAxisToButton:
|
||||
newRule, err = makeMappingRuleAxisToButton(ruleConfig, pDevs, vDevs, base)
|
||||
case RuleTypeModeSelect:
|
||||
newRule, err = makeMappingRuleModeSelect(ruleConfig, pDevs, modes, base)
|
||||
default:
|
||||
err = fmt.Errorf("bad rule type '%s' for rule '%s'", ruleConfig.Type, ruleConfig.Name)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
logger.LogError(err, "failed to build rule")
|
||||
logger.LogErrorf(err, "Failed to build rule '%s'", ruleConfig.Name)
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -49,56 +58,28 @@ func (parser *ConfigParser) BuildRules(pDevs map[string]*evdev.InputDevice, vDev
|
|||
return rules
|
||||
}
|
||||
|
||||
func setBaseRuleParameters(ruleConfig RuleConfig, modes []string) (mappingrules.MappingRuleBase, error) {
|
||||
// We perform this check here instead of in makeRuleTarget because only Output targets
|
||||
// can meaningfully have ModeSelect; this lets us avoid plumbing the modes in on every
|
||||
// makeRuleTarget call.
|
||||
if len(ruleConfig.Output.ModeSelect) > 0 {
|
||||
err := validateModes(ruleConfig.Output.ModeSelect, modes)
|
||||
if err != nil {
|
||||
return mappingrules.MappingRuleBase{}, err
|
||||
}
|
||||
}
|
||||
|
||||
err := validateModes(ruleConfig.Modes, modes)
|
||||
if err != nil {
|
||||
return mappingrules.MappingRuleBase{}, err
|
||||
}
|
||||
|
||||
ruleModes := ensureModes(ruleConfig.Modes)
|
||||
|
||||
return mappingrules.MappingRuleBase{
|
||||
Modes: ruleModes,
|
||||
Name: ruleConfig.Name,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func makeMappingRuleButton(ruleConfig RuleConfig,
|
||||
pDevs map[string]*evdev.InputDevice,
|
||||
vDevs map[string]*evdev.InputDevice,
|
||||
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleButton, error) {
|
||||
|
||||
input, err := makeRuleTarget(ruleConfig.Input, pDevs)
|
||||
input, err := makeRuleTargetButton(ruleConfig.Input, pDevs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
output, err := makeRuleTarget(ruleConfig.Output, vDevs)
|
||||
output, err := makeRuleTargetButton(ruleConfig.Output, vDevs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &mappingrules.MappingRuleButton{
|
||||
MappingRuleBase: base,
|
||||
Input: input.(*mappingrules.RuleTargetButton),
|
||||
Output: output.(*mappingrules.RuleTargetButton),
|
||||
}, nil
|
||||
return mappingrules.NewMappingRuleButton(base, input, output), nil
|
||||
}
|
||||
|
||||
func makeComboRule(ruleConfig RuleConfig,
|
||||
func makeMappingRuleCombo(ruleConfig RuleConfig,
|
||||
pDevs map[string]*evdev.InputDevice,
|
||||
vDevs map[string]*evdev.InputDevice,
|
||||
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleCombo, error) {
|
||||
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleButtonCombo, error) {
|
||||
|
||||
inputs := make([]*mappingrules.RuleTargetButton, 0)
|
||||
for _, inputConfig := range ruleConfig.Inputs {
|
||||
|
@ -109,142 +90,73 @@ func makeComboRule(ruleConfig RuleConfig,
|
|||
inputs = append(inputs, input)
|
||||
}
|
||||
|
||||
return &mappingrules.MappingRuleCombo{
|
||||
MappingRuleBase: base,
|
||||
Inputs: inputs,
|
||||
State: 0,
|
||||
}, nil
|
||||
output, err := makeRuleTargetButton(ruleConfig.Output, vDevs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return mappingrules.NewMappingRuleButtonCombo(base, inputs, output), nil
|
||||
}
|
||||
|
||||
func makeLatchedRule(ruleConfig RuleConfig,
|
||||
func makeMappingRuleLatched(ruleConfig RuleConfig,
|
||||
pDevs map[string]*evdev.InputDevice,
|
||||
vDevs map[string]*evdev.InputDevice,
|
||||
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleLatched, error) {
|
||||
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleButtonLatched, error) {
|
||||
|
||||
input, err := makeRuleTargetButton(ruleConfig.Input, pDevs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &mappingrules.MappingRuleLatched{
|
||||
MappingRuleBase: base,
|
||||
Input: input,
|
||||
State: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func makeRuleTargetButton(targetConfig RuleTargetConfig, devs map[string]*evdev.InputDevice) (*mappingrules.RuleTargetButton, error) {
|
||||
device, ok := devs[targetConfig.Device]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("couldn't build rule due to non-existent device '%s'", targetConfig.Device)
|
||||
}
|
||||
|
||||
_, eventCode, err := decodeRuleTargetValues(targetConfig)
|
||||
output, err := makeRuleTargetButton(ruleConfig.Output, vDevs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
baseParams := mappingrules.RuleTargetBase{
|
||||
DeviceName: targetConfig.Device,
|
||||
Device: device,
|
||||
Inverted: targetConfig.Inverted,
|
||||
Code: eventCode,
|
||||
}
|
||||
|
||||
return &mappingrules.RuleTargetButton{
|
||||
RuleTargetBase: baseParams,
|
||||
}, nil
|
||||
return mappingrules.NewMappingRuleButtonLatched(base, input, output), nil
|
||||
}
|
||||
|
||||
// makeRuleTarget 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) {
|
||||
if len(targetConfig.ModeSelect) > 0 {
|
||||
return &mappingrules.RuleTargetModeSelect{
|
||||
ModeSelect: targetConfig.ModeSelect,
|
||||
}, nil
|
||||
}
|
||||
func makeMappingRuleAxis(ruleConfig RuleConfig,
|
||||
pDevs map[string]*evdev.InputDevice,
|
||||
vDevs map[string]*evdev.InputDevice,
|
||||
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleAxis, error) {
|
||||
|
||||
device, ok := devs[targetConfig.Device]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("couldn't build rule due to non-existent device '%s'", targetConfig.Device)
|
||||
}
|
||||
|
||||
eventType, eventCode, err := decodeRuleTargetValues(targetConfig)
|
||||
input, err := makeRuleTargetAxis(ruleConfig.Input, pDevs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
baseParams := mappingrules.RuleTargetBase{
|
||||
DeviceName: targetConfig.Device,
|
||||
Device: device,
|
||||
Inverted: targetConfig.Inverted,
|
||||
Code: eventCode,
|
||||
output, err := makeRuleTargetAxis(ruleConfig.Output, vDevs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
switch eventType {
|
||||
case evdev.EV_KEY:
|
||||
return &mappingrules.RuleTargetButton{
|
||||
RuleTargetBase: baseParams,
|
||||
}, nil
|
||||
|
||||
case evdev.EV_ABS:
|
||||
return &mappingrules.RuleTargetAxis{
|
||||
RuleTargetBase: baseParams,
|
||||
AxisStart: targetConfig.AxisStart,
|
||||
AxisEnd: targetConfig.AxisEnd,
|
||||
}, nil
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("skipping rule due to unsupported event type '%d'", eventType)
|
||||
}
|
||||
return mappingrules.NewMappingRuleAxis(base, input, output), 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
|
||||
// STUB
|
||||
func makeMappingRuleAxisToButton(ruleConfig RuleConfig,
|
||||
pDevs map[string]*evdev.InputDevice,
|
||||
vDevs map[string]*evdev.InputDevice,
|
||||
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleAxisToButton, error) {
|
||||
|
||||
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)
|
||||
}
|
||||
} else 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
|
||||
return nil, errors.New("stub: makeMappingRuleAxisToButton")
|
||||
}
|
||||
|
||||
// ensureModes either returns the mode list, or if it is empty returns []string{"*"}
|
||||
func ensureModes(modes []string) []string {
|
||||
if len(modes) == 0 {
|
||||
return []string{"*"}
|
||||
func makeMappingRuleModeSelect(ruleConfig RuleConfig,
|
||||
pDevs map[string]*evdev.InputDevice,
|
||||
modes []string,
|
||||
base mappingrules.MappingRuleBase) (*mappingrules.MappingRuleModeSelect, error) {
|
||||
|
||||
input, err := makeRuleTargetButton(ruleConfig.Input, pDevs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return modes
|
||||
}
|
||||
|
||||
// validateModes checks the provided modes against a larger subset of modes (usually all defined ones)
|
||||
// and returns an error if any of the modes are not defined.
|
||||
func validateModes(modes []string, allModes []string) error {
|
||||
if len(modes) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, mode := range modes {
|
||||
if !slices.Contains(allModes, mode) {
|
||||
return fmt.Errorf("mode list specifies undefined mode '%s'", mode)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
output, err := makeRuleTargetModeSelect(ruleConfig.Output, modes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return mappingrules.NewMappingRuleModeSelect(base, input, output), nil
|
||||
}
|
||||
|
|
19
internal/config/modes.go
Normal file
19
internal/config/modes.go
Normal file
|
@ -0,0 +1,19 @@
|
|||
package config
|
||||
|
||||
import "slices"
|
||||
|
||||
// validateModes checks the provided modes against a larger subset of modes (usually all defined ones)
|
||||
// and returns false if any of the modes are not defined.
|
||||
func validateModes(modes []string, allModes []string) bool {
|
||||
if len(modes) == 0 {
|
||||
return true
|
||||
}
|
||||
|
||||
for _, mode := range modes {
|
||||
if !slices.Contains(allModes, mode) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
|
@ -28,11 +28,11 @@ type RuleConfig struct {
|
|||
}
|
||||
|
||||
type RuleTargetConfig struct {
|
||||
Device string `yaml:"device,omitempty"`
|
||||
Button string `yaml:"button,omitempty"`
|
||||
Axis string `yaml:"axis,omitempty"`
|
||||
AxisStart int32 `yaml:"axis_start,omitempty"`
|
||||
AxisEnd int32 `yaml:"axis_end,omitempty"`
|
||||
Inverted bool `yaml:"inverted,omitempty"`
|
||||
ModeSelect []string `yaml:"mode_select,omitempty"`
|
||||
Device string `yaml:"device,omitempty"`
|
||||
Button string `yaml:"button,omitempty"`
|
||||
Axis string `yaml:"axis,omitempty"`
|
||||
DeadzoneStart int32 `yaml:"axis_start,omitempty"`
|
||||
DeadzoneEnd int32 `yaml:"axis_end,omitempty"`
|
||||
Inverted bool `yaml:"inverted,omitempty"`
|
||||
Modes []string `yaml:"modes,omitempty"`
|
||||
}
|
||||
|
|
|
@ -4,6 +4,18 @@ import (
|
|||
"github.com/holoplot/go-evdev"
|
||||
)
|
||||
|
||||
const (
|
||||
DeviceTypePhysical = "physical"
|
||||
DeviceTypeVirtual = "virtual"
|
||||
|
||||
RuleTypeButton = "button"
|
||||
RuleTypeButtonCombo = "button-combo"
|
||||
RuleTypeLatched = "button-latched"
|
||||
RuleTypeAxis = "axis"
|
||||
RuleTypeModeSelect = "mode-select"
|
||||
RuleTypeAxisToButton = "axis-to-button"
|
||||
)
|
||||
|
||||
var (
|
||||
ButtonFromIndex = []evdev.EvCode{
|
||||
evdev.BTN_TRIGGER,
|
||||
|
@ -60,12 +72,3 @@ var (
|
|||
evdev.BTN_TRIGGER_HAPPY40,
|
||||
}
|
||||
)
|
||||
|
||||
const (
|
||||
DeviceTypePhysical = "physical"
|
||||
DeviceTypeVirtual = "virtual"
|
||||
|
||||
RuleTypeSimple = "simple"
|
||||
RuleTypeCombo = "combo"
|
||||
RuleTypeLatched = "latched"
|
||||
)
|
||||
|
|
|
@ -25,7 +25,7 @@ func LogErrorf(err error, msg string, params ...interface{}) {
|
|||
if msg == "" {
|
||||
fmt.Printf("%s\n", err.Error())
|
||||
} else {
|
||||
fmt.Printf("%s: %s\n", err.Error(), fmt.Sprintf(msg, params...))
|
||||
fmt.Printf("%s: %s\n", fmt.Sprintf(msg, params...), err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,11 +13,16 @@ type RuleTarget interface {
|
|||
// (e.g., inverting the value if Inverted == true)
|
||||
NormalizeValue(int32) int32
|
||||
|
||||
// CreateEvent typically takes the (probably normalized) value and returns an event that can be emitted
|
||||
// on a virtual device.
|
||||
//
|
||||
// MatchEvent returns true if the provided device and input event are a match for this rule target
|
||||
ValidateEvent(*evdev.InputDevice, *evdev.InputEvent) bool
|
||||
|
||||
// CreateEvent creates an event that can be emitted on a virtual device.
|
||||
// For RuleTargetModeSelect, this method modifies the active mode and returns nil.
|
||||
//
|
||||
// TODO: should we normalize inside this function to simplify the interface?
|
||||
// CreateEvent does not do any error checking; it assumes it is receiving verified and sanitized output.
|
||||
// The caller is responsible for determining whether an event *should* be emitted.
|
||||
//
|
||||
// Typically int32 is the input event's normalized value. *string is the current mode, but is optional
|
||||
// for most implementations.
|
||||
CreateEvent(int32, *string) *evdev.InputEvent
|
||||
}
|
||||
|
|
27
internal/mappingrules/mapping_rule_axis.go
Normal file
27
internal/mappingrules/mapping_rule_axis.go
Normal file
|
@ -0,0 +1,27 @@
|
|||
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 MappingRuleAxis struct {
|
||||
MappingRuleBase
|
||||
Input *RuleTargetAxis
|
||||
Output *RuleTargetAxis
|
||||
}
|
||||
|
||||
func NewMappingRuleAxis(base MappingRuleBase, input *RuleTargetAxis, output *RuleTargetAxis) *MappingRuleAxis {
|
||||
return &MappingRuleAxis{
|
||||
MappingRuleBase: base,
|
||||
Input: input,
|
||||
Output: output,
|
||||
}
|
||||
}
|
||||
|
||||
func (rule *MappingRuleAxis) MatchEvent(device *evdev.InputDevice, event *evdev.InputEvent, mode *string) (*evdev.InputDevice, *evdev.InputEvent) {
|
||||
if !rule.MappingRuleBase.modeCheck(mode) ||
|
||||
!rule.Input.MatchEvent(device, event) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return rule.Output.Device, rule.Output.CreateEvent(rule.Input.NormalizeValue(event.Value), mode)
|
||||
}
|
51
internal/mappingrules/mapping_rule_axis_to_button.go
Normal file
51
internal/mappingrules/mapping_rule_axis_to_button.go
Normal file
|
@ -0,0 +1,51 @@
|
|||
package mappingrules
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/holoplot/go-evdev"
|
||||
)
|
||||
|
||||
// TODO: This whole file is still WIP
|
||||
type MappingRuleAxisToButton struct {
|
||||
MappingRuleBase
|
||||
Input *RuleTargetAxis
|
||||
Output *RuleTargetButton
|
||||
RepeatSpeedMin int32
|
||||
RepeatSpeedMax int32
|
||||
lastValue int32
|
||||
lastEvent time.Time
|
||||
}
|
||||
|
||||
func (rule *MappingRuleAxisToButton) MatchEvent(device *evdev.InputDevice, event *evdev.InputEvent, mode *string) (*evdev.InputDevice, *evdev.InputEvent) {
|
||||
if !rule.MappingRuleBase.modeCheck(mode) ||
|
||||
!rule.Input.MatchEvent(device, event) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// set the last value to the normalized input value
|
||||
rule.lastValue = rule.Input.NormalizeValue(event.Value)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// TimerEvent returns an event when enough time has passed (compared to the last recorded axis value)
|
||||
// to emit an event.
|
||||
func (rule *MappingRuleAxisToButton) TimerEvent() *evdev.InputEvent {
|
||||
// This is tighter coupling than we'd like, but it will do for now.
|
||||
// TODO: maybe it would be better to just be more declarative about event types and their inputs and outputs.
|
||||
if rule.lastValue < rule.Input.DeadzoneStart {
|
||||
rule.lastEvent = time.Now()
|
||||
return nil
|
||||
}
|
||||
|
||||
// calculate target time until next event press
|
||||
// nextEvent := rule.LastEvent + (rule.LastValue)
|
||||
|
||||
// TODO: figure out what the condition should be
|
||||
if false {
|
||||
// TODO: emit event
|
||||
rule.lastEvent = time.Now()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -7,6 +7,20 @@ type MappingRuleBase struct {
|
|||
Modes []string
|
||||
}
|
||||
|
||||
func NewMappingRuleBase(
|
||||
name string,
|
||||
modes []string,
|
||||
) MappingRuleBase {
|
||||
if len(modes) == 0 {
|
||||
modes = []string{"*"}
|
||||
}
|
||||
|
||||
return MappingRuleBase{
|
||||
Name: name,
|
||||
Modes: modes,
|
||||
}
|
||||
}
|
||||
|
||||
func (rule *MappingRuleBase) modeCheck(mode *string) bool {
|
||||
if rule.Modes[0] == "*" {
|
||||
return true
|
||||
|
|
|
@ -9,13 +9,25 @@ type MappingRuleButton struct {
|
|||
Output *RuleTargetButton
|
||||
}
|
||||
|
||||
func NewMappingRuleButton(
|
||||
base MappingRuleBase,
|
||||
input *RuleTargetButton,
|
||||
output *RuleTargetButton) *MappingRuleButton {
|
||||
|
||||
return &MappingRuleButton{
|
||||
MappingRuleBase: base,
|
||||
Input: input,
|
||||
Output: output,
|
||||
}
|
||||
}
|
||||
|
||||
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() {
|
||||
if device != rule.Input.Device ||
|
||||
event.Code != rule.Input.Button {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
|
|
@ -3,24 +3,37 @@ package mappingrules
|
|||
import "github.com/holoplot/go-evdev"
|
||||
|
||||
// A Combo Mapping Rule can require multiple physical button presses for a single output button
|
||||
type MappingRuleCombo struct {
|
||||
type MappingRuleButtonCombo struct {
|
||||
MappingRuleBase
|
||||
Inputs []*RuleTargetButton
|
||||
Output *RuleTargetButton
|
||||
State int
|
||||
}
|
||||
|
||||
func (rule *MappingRuleCombo) MatchEvent(device *evdev.InputDevice, event *evdev.InputEvent, mode *string) (*evdev.InputDevice, *evdev.InputEvent) {
|
||||
func NewMappingRuleButtonCombo(
|
||||
base MappingRuleBase,
|
||||
inputs []*RuleTargetButton,
|
||||
output *RuleTargetButton) *MappingRuleButtonCombo {
|
||||
|
||||
return &MappingRuleButtonCombo{
|
||||
MappingRuleBase: base,
|
||||
Inputs: inputs,
|
||||
Output: output,
|
||||
State: 0,
|
||||
}
|
||||
}
|
||||
|
||||
func (rule *MappingRuleButtonCombo) MatchEvent(device *evdev.InputDevice, event *evdev.InputEvent, mode *string) (*evdev.InputDevice, *evdev.InputEvent) {
|
||||
if !rule.MappingRuleBase.modeCheck(mode) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Check each of the inputs, and if we find a match, proceed
|
||||
var match RuleTarget
|
||||
var match *RuleTargetButton
|
||||
for _, input := range rule.Inputs {
|
||||
if device == input.GetDevice() &&
|
||||
event.Code == input.GetCode() {
|
||||
if input.MatchEvent(device, event) {
|
||||
match = input
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -40,10 +53,10 @@ func (rule *MappingRuleCombo) MatchEvent(device *evdev.InputDevice, event *evdev
|
|||
targetState := len(rule.Inputs)
|
||||
|
||||
if oldState == targetState-1 && rule.State == targetState {
|
||||
return rule.Output.GetDevice(), rule.Output.CreateEvent(1, mode)
|
||||
return rule.Output.Device, rule.Output.CreateEvent(1, mode)
|
||||
}
|
||||
if oldState == targetState && rule.State == targetState-1 {
|
||||
return rule.Output.GetDevice(), rule.Output.CreateEvent(0, mode)
|
||||
return rule.Output.Device, rule.Output.CreateEvent(0, mode)
|
||||
}
|
||||
return nil, nil
|
||||
}
|
|
@ -2,20 +2,33 @@ package mappingrules
|
|||
|
||||
import "github.com/holoplot/go-evdev"
|
||||
|
||||
type MappingRuleLatched struct {
|
||||
type MappingRuleButtonLatched struct {
|
||||
MappingRuleBase
|
||||
Input *RuleTargetButton
|
||||
Output *RuleTargetButton
|
||||
State bool
|
||||
}
|
||||
|
||||
func (rule *MappingRuleLatched) MatchEvent(device *evdev.InputDevice, event *evdev.InputEvent, mode *string) (*evdev.InputDevice, *evdev.InputEvent) {
|
||||
func NewMappingRuleButtonLatched(
|
||||
base MappingRuleBase,
|
||||
input *RuleTargetButton,
|
||||
output *RuleTargetButton) *MappingRuleButtonLatched {
|
||||
|
||||
return &MappingRuleButtonLatched{
|
||||
MappingRuleBase: base,
|
||||
Input: input,
|
||||
Output: output,
|
||||
State: false,
|
||||
}
|
||||
}
|
||||
|
||||
func (rule *MappingRuleButtonLatched) 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.Device ||
|
||||
event.Code != rule.Input.Code ||
|
||||
event.Code != rule.Input.Button ||
|
||||
rule.Input.NormalizeValue(event.Value) == 0 {
|
||||
return nil, nil
|
||||
}
|
40
internal/mappingrules/mapping_rule_mode_select.go
Normal file
40
internal/mappingrules/mapping_rule_mode_select.go
Normal file
|
@ -0,0 +1,40 @@
|
|||
package mappingrules
|
||||
|
||||
import "github.com/holoplot/go-evdev"
|
||||
|
||||
type MappingRuleModeSelect struct {
|
||||
MappingRuleBase
|
||||
Input *RuleTargetButton
|
||||
Output *RuleTargetModeSelect
|
||||
}
|
||||
|
||||
func NewMappingRuleModeSelect(
|
||||
base MappingRuleBase,
|
||||
input *RuleTargetButton,
|
||||
output *RuleTargetModeSelect,
|
||||
) *MappingRuleModeSelect {
|
||||
|
||||
return &MappingRuleModeSelect{
|
||||
MappingRuleBase: base,
|
||||
Input: input,
|
||||
Output: output,
|
||||
}
|
||||
}
|
||||
|
||||
func (rule *MappingRuleModeSelect) 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.Device ||
|
||||
event.Code != rule.Input.Button ||
|
||||
rule.Input.NormalizeValue(event.Value) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return nil, rule.Output.CreateEvent(event.Value, mode)
|
||||
}
|
|
@ -1,54 +0,0 @@
|
|||
package mappingrules
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/holoplot/go-evdev"
|
||||
)
|
||||
|
||||
// TODO: How are we going to implement this? It needs to operate on a timer...
|
||||
type MappingRuleProportionalAxis struct {
|
||||
MappingRuleBase
|
||||
Input *RuleTargetAxis
|
||||
Output *RuleTargetButton
|
||||
Sensitivity int32
|
||||
LastValue int32
|
||||
LastEvent time.Time
|
||||
}
|
||||
|
||||
func (rule *MappingRuleProportionalAxis) 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
|
||||
}
|
||||
|
||||
// set the last value to the normalized input value
|
||||
rule.LastValue = rule.Input.NormalizeValue(event.Value)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// TimerEvent returns an event when enough time has passed (compared to the last recorded axis value)
|
||||
// to emit an event.
|
||||
func (rule *MappingRuleProportionalAxis) TimerEvent() *evdev.InputEvent {
|
||||
// This is tighter coupling than we'd like, but it will do for now.
|
||||
// TODO: maybe it would be better to just be more declarative about event types and their inputs and outputs.
|
||||
if rule.LastValue < rule.Input.AxisStart {
|
||||
rule.LastEvent = time.Now()
|
||||
return nil
|
||||
}
|
||||
|
||||
// calculate target time until next event press
|
||||
// nextEvent := rule.LastEvent + (rule.LastValue)
|
||||
|
||||
// TODO: figure out what the condition should be
|
||||
if false {
|
||||
// TODO: emit event
|
||||
rule.LastEvent = time.Now()
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
|
@ -5,35 +5,43 @@ import (
|
|||
)
|
||||
|
||||
type RuleTargetAxis struct {
|
||||
RuleTargetBase
|
||||
AxisStart int32
|
||||
AxisEnd int32
|
||||
Sensitivity float64
|
||||
DeviceName string
|
||||
Device *evdev.InputDevice
|
||||
Axis evdev.EvCode
|
||||
Inverted bool
|
||||
DeadzoneStart int32
|
||||
DeadzoneEnd int32
|
||||
Sensitivity float64
|
||||
}
|
||||
|
||||
func NewRuleTargetAxis(device_name string,
|
||||
device *evdev.InputDevice,
|
||||
code evdev.EvCode,
|
||||
axis evdev.EvCode,
|
||||
inverted bool,
|
||||
axis_start int32,
|
||||
axis_end int32,
|
||||
deadzone_start int32,
|
||||
deadzone_end int32,
|
||||
sensitivity float64) *RuleTargetAxis {
|
||||
|
||||
return &RuleTargetAxis{
|
||||
RuleTargetBase: NewRuleTargetBase(device_name, device, code, inverted),
|
||||
AxisStart: axis_start,
|
||||
AxisEnd: axis_end,
|
||||
Sensitivity: sensitivity,
|
||||
DeviceName: device_name,
|
||||
Device: device,
|
||||
Axis: axis,
|
||||
Inverted: inverted,
|
||||
DeadzoneStart: deadzone_start,
|
||||
DeadzoneEnd: deadzone_end,
|
||||
Sensitivity: sensitivity,
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: lots of fixes and decisions to make here. Should we normalize all axes to the same range?
|
||||
// How do we handle deadzones in light of that?
|
||||
func (target *RuleTargetAxis) NormalizeValue(value int32) int32 {
|
||||
if !target.Inverted {
|
||||
return value
|
||||
}
|
||||
|
||||
axisRange := target.AxisEnd - target.AxisStart
|
||||
axisMid := target.AxisEnd - axisRange/2
|
||||
axisRange := target.DeadzoneEnd - target.DeadzoneStart
|
||||
axisMid := target.DeadzoneEnd - axisRange/2
|
||||
delta := value - axisMid
|
||||
if delta < 0 {
|
||||
delta = -delta
|
||||
|
@ -55,7 +63,13 @@ func (target *RuleTargetAxis) CreateEvent(value int32, mode *string) *evdev.Inpu
|
|||
// TODO: oh no we need center deadzones actually...
|
||||
return &evdev.InputEvent{
|
||||
Type: evdev.EV_ABS,
|
||||
Code: target.Code,
|
||||
Code: target.Axis,
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
|
||||
func (target *RuleTargetAxis) MatchEvent(device *evdev.InputDevice, event *evdev.InputEvent) bool {
|
||||
return device == target.Device &&
|
||||
event.Type == evdev.EV_ABS &&
|
||||
event.Code == target.Axis
|
||||
}
|
||||
|
|
|
@ -1,35 +0,0 @@
|
|||
package mappingrules
|
||||
|
||||
import "github.com/holoplot/go-evdev"
|
||||
|
||||
type RuleTargetBase struct {
|
||||
DeviceName string
|
||||
Device *evdev.InputDevice
|
||||
Code evdev.EvCode
|
||||
Inverted bool
|
||||
}
|
||||
|
||||
func NewRuleTargetBase(device_name string,
|
||||
device *evdev.InputDevice,
|
||||
code evdev.EvCode,
|
||||
inverted bool) RuleTargetBase {
|
||||
|
||||
return RuleTargetBase{
|
||||
DeviceName: device_name,
|
||||
Device: device,
|
||||
Code: code,
|
||||
Inverted: inverted,
|
||||
}
|
||||
}
|
||||
|
||||
func (target *RuleTargetBase) GetCode() evdev.EvCode {
|
||||
return target.Code
|
||||
}
|
||||
|
||||
func (target *RuleTargetBase) GetDeviceName() string {
|
||||
return target.DeviceName
|
||||
}
|
||||
|
||||
func (target *RuleTargetBase) GetDevice() *evdev.InputDevice {
|
||||
return target.Device
|
||||
}
|
|
@ -3,12 +3,18 @@ package mappingrules
|
|||
import "github.com/holoplot/go-evdev"
|
||||
|
||||
type RuleTargetButton struct {
|
||||
RuleTargetBase
|
||||
DeviceName string
|
||||
Device *evdev.InputDevice
|
||||
Button evdev.EvCode
|
||||
Inverted bool
|
||||
}
|
||||
|
||||
func NewRuleTargetButton(device_name string, device *evdev.InputDevice, code evdev.EvCode, inverted bool) *RuleTargetButton {
|
||||
return &RuleTargetButton{
|
||||
RuleTargetBase: NewRuleTargetBase(device_name, device, code, inverted),
|
||||
DeviceName: device_name,
|
||||
Device: device,
|
||||
Button: code,
|
||||
Inverted: inverted,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,10 +28,16 @@ func (target *RuleTargetButton) NormalizeValue(value int32) int32 {
|
|||
return value
|
||||
}
|
||||
|
||||
func (target *RuleTargetButton) CreateEvent(value int32, mode *string) *evdev.InputEvent {
|
||||
func (target *RuleTargetButton) CreateEvent(value int32, _ *string) *evdev.InputEvent {
|
||||
return &evdev.InputEvent{
|
||||
Type: evdev.EV_KEY,
|
||||
Code: target.Code,
|
||||
Code: target.Button,
|
||||
Value: value,
|
||||
}
|
||||
}
|
||||
|
||||
func (target *RuleTargetButton) MatchEvent(device *evdev.InputDevice, event *evdev.InputEvent) bool {
|
||||
return device == target.Device &&
|
||||
event.Type == evdev.EV_KEY &&
|
||||
event.Code == target.Button
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package mappingrules
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"slices"
|
||||
|
||||
"git.annabunches.net/annabunches/joyful/internal/logger"
|
||||
|
@ -8,34 +9,35 @@ import (
|
|||
)
|
||||
|
||||
type RuleTargetModeSelect struct {
|
||||
RuleTargetBase
|
||||
ModeSelect []string
|
||||
Modes []string
|
||||
}
|
||||
|
||||
func NewRuleTargetModeSelect(modes []string) *RuleTargetModeSelect {
|
||||
return &RuleTargetModeSelect{
|
||||
RuleTargetBase: NewRuleTargetBase("", nil, 0, false),
|
||||
ModeSelect: modes,
|
||||
func NewRuleTargetModeSelect(modes []string) (*RuleTargetModeSelect, error) {
|
||||
if len(modes) == 0 {
|
||||
return nil, errors.New("cannot create RuleTargetModeSelect: mode list is empty")
|
||||
}
|
||||
return &RuleTargetModeSelect{
|
||||
Modes: modes,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// RuleTargetModeSelect doesn't make sense as an input type
|
||||
func (target *RuleTargetModeSelect) NormalizeValue(value int32) int32 {
|
||||
func (target *RuleTargetModeSelect) NormalizeValue(_ int32) int32 {
|
||||
return -1
|
||||
}
|
||||
|
||||
func (target *RuleTargetModeSelect) CreateEvent(value int32, mode *string) *evdev.InputEvent {
|
||||
if value == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (target *RuleTargetModeSelect) CreateEvent(_ int32, mode *string) *evdev.InputEvent {
|
||||
index := 0
|
||||
if currentMode := slices.Index(target.ModeSelect, *mode); currentMode != -1 {
|
||||
if currentMode := slices.Index(target.Modes, *mode); currentMode != -1 {
|
||||
// find the next mode
|
||||
index = (currentMode + 1) % len(target.ModeSelect)
|
||||
index = (currentMode + 1) % len(target.Modes)
|
||||
}
|
||||
|
||||
*mode = target.ModeSelect[index]
|
||||
*mode = target.Modes[index]
|
||||
logger.Logf("Mode changed to '%s'", *mode)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (target *RuleTargetModeSelect) MatchEvent(_ *evdev.InputDevice, _ *evdev.InputEvent) bool {
|
||||
return false
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue