diff --git a/CHANGELOG b/CHANGELOG new file mode 100644 index 0000000..25ae3dc --- /dev/null +++ b/CHANGELOG @@ -0,0 +1,7 @@ +# Changelog + +### Added +- Add `hold` parameter for button-to-axis mappings. + +### Fixed +- Fixed possible crash when TTS is disabled. (thanks [Tsudico](https://codeberg.org/Tsudico)) \ No newline at end of file diff --git a/docs/examples/ruletypes.yml b/docs/examples/ruletypes.yml index 974988d..cd7a17a 100644 --- a/docs/examples/ruletypes.yml +++ b/docs/examples/ruletypes.yml @@ -119,6 +119,9 @@ rules: # and a button press every 10 milliseconds at the axis' maximum value. repeat_rate_min: 1000 repeat_rate_max: 10 + # If hold is set to true, when the rule is triggered the button will be active until the rule is no longer triggered. + # Repeat rate settings are ignored when hold is true. + hold: false input: device: flightstick axis: ABS_RY # This axis commonly represents thumbsticks diff --git a/internal/configparser/schema.go b/internal/configparser/schema.go index 55ddb24..1a47172 100644 --- a/internal/configparser/schema.go +++ b/internal/configparser/schema.go @@ -52,6 +52,7 @@ type RuleConfigAxisCombined struct { } type RuleConfigAxisToButton struct { + Hold bool RepeatRateMin int `yaml:"repeat_rate_min,omitempty"` RepeatRateMax int `yaml:"repeat_rate_max,omitempty"` Input RuleTargetConfigAxis diff --git a/internal/mappingrules/mapping_rule_axis_to_button.go b/internal/mappingrules/mapping_rule_axis_to_button.go index 82862ee..40517bc 100644 --- a/internal/mappingrules/mapping_rule_axis_to_button.go +++ b/internal/mappingrules/mapping_rule_axis_to_button.go @@ -14,6 +14,7 @@ type MappingRuleAxisToButton struct { MappingRuleBase Input *RuleTargetAxis Output *RuleTargetButton + Hold bool RepeatRateMin int RepeatRateMax int nextEvent time.Duration @@ -43,6 +44,7 @@ func NewMappingRuleAxisToButton(ruleConfig configparser.RuleConfigAxisToButton, MappingRuleBase: base, Input: input, Output: output, + Hold: ruleConfig.Hold, RepeatRateMin: ruleConfig.RepeatRateMin, RepeatRateMax: ruleConfig.RepeatRateMax, lastEvent: time.Now(), @@ -61,6 +63,10 @@ func (rule *MappingRuleAxisToButton) MatchEvent(device Device, event *evdev.Inpu return nil, nil } + if rule.Hold { + return rule.matchHoldMode(device, event) + } + // If we're inside the deadzone, unset the next event if rule.Input.InDeadZone(event.Value) { rule.nextEvent = NoNextEvent @@ -86,9 +92,31 @@ func (rule *MappingRuleAxisToButton) MatchEvent(device Device, event *evdev.Inpu return nil, nil } +func (rule *MappingRuleAxisToButton) matchHoldMode(device Device, event *evdev.InputEvent) (*evdev.InputDevice, *evdev.InputEvent) { + if rule.Input.InDeadZone(event.Value) { + if !rule.pressed { + return nil, nil + } + rule.pressed = false + return rule.Output.Device.(*evdev.InputDevice), rule.Output.CreateEvent(0, nil) + } + + if rule.pressed { + return nil, nil + } + + rule.pressed = true + return rule.Output.Device.(*evdev.InputDevice), rule.Output.CreateEvent(1, 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 { + // If Hold is true, we want to act like a rule with no timer component + if rule.Hold { + return nil + } + // If we pressed the button last tick, release it before doing anything else if rule.pressed { rule.pressed = false