CapsLockX — Core Trigger Behaviors

Specification of all CapsLock & Space key-combination cases. 2026-05-15.

1. Solo trigger taps

#InputSequenceResultNotes
1 Space single tap (< 200 ms, no other key) Space↓ → Space↑ native One space character typed. Engine passthrough via key_tap(Space) on key-up.
2 Space hold > 200 ms (no other key) Space↓  (held) native Space character emitted, then auto-repeats at the OS key-repeat rate until release. Behaves identically to a normal Space key. 200 ms timeout closes the chord-detect window; engine then injects key_down(Space) and lets the OS auto-repeat, emitting key_up(Space) on release.
3 CapsLock single tap (no other key) CapsLock↓ → CapsLock↑ native OS AlphaShift state toggles. When the hidutil remap is active, the engine routes the tap to IOHIDSetModifierLockState instead of CGEvent (which can't toggle AlphaShift).
4 CapsLock hold (no other key) CapsLock↓  (held) → CapsLock↑ native One AlphaShift toggle on release. No auto-repeat. CapsLock has no 200 ms timeout; tap and hold behave the same.

2. Trigger + action key (CLX FN mode)

#InputSequenceResultNotes
5 Space + action key (HJKL, WASD, V, B, M, Z, 1–9, …) Space↓ → key↓ → key↑ → Space↑ FN Action runs (e.g. cursor left for H). No space character emitted on release. fn_acted=true suppresses native passthrough on Space↑.
6 CapsLock + action key CapsLock↓ → key↓ → key↑ → CapsLock↑ FN Same as #5. AlphaShift is not toggled. Space and CapsLock are interchangeable as triggers.

3. Chord — Space + CapsLock (CLX LOCK mode)

#InputSequenceResultNotes
7 Space then CapsLock Space↓ → CapsLock↓ LOCK Enter LOCK mode. AlphaShift unchanged. Chord detected in clx_dn; fn_acted=true prevents either key emitting on release.
8 CapsLock then Space CapsLock↓ → Space↓ LOCK Same as #7 — order doesn't matter. Symmetric chord detection.
9 Inside LOCK: tap Space or CapsLock alone Space↑  or  CapsLock↑ unlock Exit LOCK mode. AlphaShift unchanged. The unlock path in clx_up takes precedence over native passthrough.

4. Modifier + trigger (system shortcuts)

Rule: any modifier + Space bypasses CLX so OS shortcuts work unchanged.

#InputSequenceResultNotes
10 Shift + Space Shift↓ → Space↓ bypass OS receives Shift+Space (IME switching on macOS). Down and up both pass through.
11 Ctrl + Space Ctrl↓ → Space↓ bypass OS receives Ctrl+Space (IME switching, AHK-compatible).
12 Cmd + Space Cmd↓ → Space↓ bypass OS receives Cmd+Space (Spotlight).
13 Alt + Space Alt↓ → Space↓ bypass OS receives Alt+Space. Aligned with other modifiers — the rule is uniform.
14 Modifier + CapsLock mod↓ → CapsLock↓ FN Enters CLX FN mode (CapsLock is not bypassed by modifiers). Modifier bypass applies only to Space, where it collides with OS shortcuts.

5. Accidental trigger while typing

#InputSequenceResultNotes
15 Non-modifier key already held when Space is pressed A↓ → Space↓ bypass OS receives a normal Space. Protects fast-typing scenarios. Triggered by non_modifier_held check.
16 Non-modifier key already held when CapsLock is pressed A↓ → CapsLock↓ bypass OS receives a normal CapsLock (toggles AlphaShift). Same protection for CapsLock.

6. Lifecycle & safety

#EventBehaviorNotes
17 CLX startup If use_capslock is enabled: force AlphaShift OFF via IOHIDSetModifierLockState, then install the hidutil CapsLock → F18 remap. Forcing AlphaShift off prevents a stuck state (user with AlphaShift on couldn't toggle it back once the physical key is remapped).
18 CLX graceful exit (Quit menu, terminate:, SIGINT, SIGTERM, SIGHUP) hidutil remap is reset via signal handler / atexit hook. Best-effort cleanup. AlphaShift state is left as-is.
19 CLX killed by SIGKILL (kill -9) Remap persists. Recovery: launch CLX again, or run hidutil property --set '{"UserKeyMapping":[]}' manually. SIGKILL cannot be caught.
20 CGEventTap disabled by macOS (secure input, password fields, watchdog timeout) Tap is re-enabled automatically; all held keys are released; modules stopped. emergency_stop() prevents stuck-key states from AccModel physics.

Invariants

Source of truth: rs/core/src/engine.rs (state machine), rs/adapters/macos/src/output.rs (key injection), rs/adapters/macos/src/capslock_remap.rs (hidutil + IOKit). Last updated 2026-05-15.