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. # and a button press every 10 milliseconds at the axis' maximum value.
repeat_rate_min: 1000 repeat_rate_min: 1000
repeat_rate_max: 10 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: input:
device: flightstick device: flightstick
axis: ABS_RY # This axis commonly represents thumbsticks axis: ABS_RY # This axis commonly represents thumbsticks

View file

@ -52,6 +52,7 @@ type RuleConfigAxisCombined struct {
} }
type RuleConfigAxisToButton struct { type RuleConfigAxisToButton struct {
Hold bool
RepeatRateMin int `yaml:"repeat_rate_min,omitempty"` RepeatRateMin int `yaml:"repeat_rate_min,omitempty"`
RepeatRateMax int `yaml:"repeat_rate_max,omitempty"` RepeatRateMax int `yaml:"repeat_rate_max,omitempty"`
Input RuleTargetConfigAxis Input RuleTargetConfigAxis

View file

@ -14,6 +14,7 @@ type MappingRuleAxisToButton struct {
MappingRuleBase MappingRuleBase
Input *RuleTargetAxis Input *RuleTargetAxis
Output *RuleTargetButton Output *RuleTargetButton
Hold bool
RepeatRateMin int RepeatRateMin int
RepeatRateMax int RepeatRateMax int
nextEvent time.Duration nextEvent time.Duration
@ -43,6 +44,7 @@ func NewMappingRuleAxisToButton(ruleConfig configparser.RuleConfigAxisToButton,
MappingRuleBase: base, MappingRuleBase: base,
Input: input, Input: input,
Output: output, Output: output,
Hold: ruleConfig.Hold,
RepeatRateMin: ruleConfig.RepeatRateMin, RepeatRateMin: ruleConfig.RepeatRateMin,
RepeatRateMax: ruleConfig.RepeatRateMax, RepeatRateMax: ruleConfig.RepeatRateMax,
lastEvent: time.Now(), lastEvent: time.Now(),
@ -61,6 +63,10 @@ func (rule *MappingRuleAxisToButton) MatchEvent(device Device, event *evdev.Inpu
return nil, nil return nil, nil
} }
if rule.Hold {
return rule.matchHoldMode(device, event)
}
// If we're inside the deadzone, unset the next event // If we're inside the deadzone, unset the next event
if rule.Input.InDeadZone(event.Value) { if rule.Input.InDeadZone(event.Value) {
rule.nextEvent = NoNextEvent rule.nextEvent = NoNextEvent
@ -86,9 +92,31 @@ func (rule *MappingRuleAxisToButton) MatchEvent(device Device, event *evdev.Inpu
return nil, nil 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) // TimerEvent returns an event when enough time has passed (compared to the last recorded axis value)
// to emit an event. // to emit an event.
func (rule *MappingRuleAxisToButton) TimerEvent() *evdev.InputEvent { 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 we pressed the button last tick, release it before doing anything else
if rule.pressed { if rule.pressed {
rule.pressed = false rule.pressed = false