Add hold option to axis-to-button (#13)

Reviewed-on: https://codeberg.org/annabunches/joyful/pulls/13
This commit is contained in:
Anna Rose Wiggins 2026-09-12 16:09:10 +02:00 committed by annabunches
parent add27f17e2
commit 77ee020b2a
4 changed files with 39 additions and 0 deletions

7
CHANGELOG Normal file
View file

@ -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))

View file

@ -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

View file

@ -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

View file

@ -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