-
Notifications
You must be signed in to change notification settings - Fork 1
Timer Model
FreeJoyX has two kinds of timers that affect button behaviour: global gesture timers (one set per device, in the Shifts & Timers tab) and per-slot timers (one per logical button slot, in the Button Config tab). This page explains which kind drives which slot type.
Located in the Shifts & Timers tab. Two values:
| Name | Default | Effect |
|---|---|---|
tap_cutoff_ms |
500 ms | Maximum press duration that counts as a "tap". Press held longer is reclassified as a hold. |
double_tap_window_ms |
300 ms | After the first tap's release, how long the firmware waits for a second rising edge before deciding the gesture is a single tap. |
These run the per-physical gesture state machine and apply only to physicals that have at least one TAP or DOUBLE_TAP slot bound to them.
Located in the Button Config tab as the DelayTimer and PressTimer
columns. Each column picks one of BUTTON_TIMER_1, BUTTON_TIMER_2,
BUTTON_TIMER_3 (whose millisecond values are configured globally in
the Shifts & Timers tab) or BUTTON_TIMER_NONE.
Effect depends on the slot's type:
| Slot type | DelayTimer | PressTimer |
|---|---|---|
Standalone NORMAL (no gesture sister) |
Delay before rise after press | Minimum duration in PRESS phase |
NORMAL with TAP or DT sister |
Disabled — cleared, no effect | Minimum-hold floor on logical output |
TAP |
Disabled — cleared, no effect | Minimum-hold floor on the TAP pulse |
DOUBLE_TAP |
Disabled — cleared, no effect | Minimum-hold floor on second-tap release |
TOGGLE, RADIO, SEQUENTIAL, etc. |
Slot-specific (see Button Types) | Slot-specific |
When a slot's logical output would normally drop to 0, the floor holds
it high for at least press_timer ms past that drop event. Two
guarantees:
- A
press_timer = 0slot still produces a 20 ms minimum pulse (MIN_PULSE_MS), enough for any HID host polling at 125 Hz to sample it at least twice. - The floor only extends output — it never blocks the gesture state machine from detecting the next physical edge. The state machine reads physical edges, not logical outputs.
This means a slot can be configured to guarantee, e.g., a 100 ms visible TAP pulse regardless of how briefly the user actually pressed the button.
The gesture state machine already imposes implicit delays:
- TAP fires either immediately on release (no DT sister) or after
double_tap_window_ms(with a DT sister, to give the second tap a chance to arrive). - DT fires only on the second rising edge.
- NORMAL fires
tap_cutoff_msafter rising (the decision window).
A user-tunable delay_timer on top of these implicit delays would
compose strangely and isn't useful — so the configurator clears it to
None and disables the column for gesture-managed slots.
New slots default to BUTTON_TIMER_NONE for both DelayTimer and
PressTimer. Setting them is opt-in.
BUTTON_TIMER_1, _2, _3 have configurable millisecond values in
the Shifts & Timers tab (defaults: 50 / 100 / 200 ms).
When a NORMAL slot's physical has no TAP or DT sister, both timers behave exactly as in upstream FreeJoy:
rising edge -> DELAY phase (lasts delay_timer ms)
|
v
PRESS phase (lasts press_timer ms, output = on_state)
|
v
IDLE (output = physical passthrough)
This is the model used by upstream FreeJoy for all button types and preserved here for backward compatibility on plain NORMAL slots.
- Button Types — what each Function dropdown entry does
- Gesture Coexistence — how the gesture state machine resolves between coexisting NORMAL / TAP / DOUBLE_TAP slots
- anpeaco/FreeJoyX#22 — the issue that introduced the minimum-hold floor semantics