Skip to content

Safety and performance constraints ​

For input, rendering, or asynchronous work, preserve three things: immediate key response, no stuck input after exit, and no stale work affecting a new session.

Core invariants ​

AreaRequired behavior
Input hookReturn Consume/Forward immediately; no scans, config I/O, process waits, or sleep. Defer remains for compatibility and acts as Forward in the built-in runtime
Key ownershipPhysical down/up events retain one owner; that owner ends held actions after mode switches
Modes / pluginsUse ModeEvent, Command, and HostContext; native calls stay in the Backend
Rust boundariesapi, app, config, modes, plugins, and presentation use safe Rust; FFI stays in the platform layer
Command storageSafe inline CommandBatch storage for common 0/1/2-command results; no raw pointers, MaybeUninit, or handwritten drop
Session cleanupHeld buttons, click indicators, sequences, scans, overlays, and clocks have completion, cancellation, reload, disable, and exit paths

Invalid reloads preserve the current session

Failed validation keeps the active configuration and runtime state. A valid reload restarts the runtime. Both paths preserve physical down/up pairing.

Continuous movement and display frames ​

Compute distance from each frame's actual elapsed, not frame count or an assumed 16 ms interval. Equal durations should give consistent total movement on 60 Hz, 144 Hz, and occasionally delayed frames.

PlatformFrame source
Windows 11DirectComposition compositor clock, waiting on a stop event too
Older WindowsCurrent output's DXGI VBlank, then DwmFlush
macOSCADisplayLink attached to the overlay View, following its display

Frame waits belong on workers, without blocking the hook, Engine, or overlay thread or polling refresh rates. Stopping wakes, joins, and releases the worker.

Smooth acceleration uses smootherstep; disabling it selects linear acceleration. Releasing the last direction stops immediately. Long frames crossing the acceleration endpoint integrate the accelerating and constant-speed segments separately. Subpixel remainders, diagonal normalization, and speed multipliers also use actual frame intervals.

Overlays and asynchronous tasks ​

PathPreserve this behavior
Windows scene submissionLatest-frame slot and shared OverlayScene; submission does not wait for drawing
Static labels and shapesCopy-on-write; pointer/indicator updates do not copy, sort, or compare the whole Grid
Windows high-DPI cacheRebuild only for static-label or scale changes; release on empty scenes, 100% scale, Hide/Shutdown
macOS overlaysUI work runs on the correct thread; Hide detaches the content view/layer tree without retaining a large scene
UIA / AX / Vision scansWorkers return partial results with scan ID and owner; discard old modes, displays, and cancelled results
Scan memoryReuse arrays while active, release large allocations on exit

Before adding asynchronous work, define result ownership, cancellation, queue/result bounds, and delivery to the Engine. Delivery must not block the event loop.

Change checklist ​

Test on the target system and run the checks below:

  • Down/up, repeats, mode switches, and exit leave no stuck keys or mouse buttons.
  • Movement remains time-based at low/high refresh rates, across displays, and after dropped frames.
  • Failure recovery leaves no overlays, click colors, timers, scans, or frame workers.
  • Windows and macOS have corresponding behavior; unsupported returns clear errors.

Windows input failures ​

CheckRequirement
EvidenceRetain actual SendInput insertion count and immediate GetLastError; zero does not always mean a permission failure
Diagnostic costQuery session, integrity, elevation, and UIAccess and format diagnostics only on failure; no extra diagnostic allocations or system queries on success
Click fallbackPrefer complete batches; per-edge retry only when zero were inserted, never replay partial success
Release safeguardConservatively Release after fallback failure; retain button release at process exit

Avoid mutual hook and Engine waits ​

After deciding key disposition, the Engine must not synchronously wait for the low-level hook to execute SendInput. The next input may already have the hook waiting for the Engine, causing deadlock.

Mouse-button, wheel, and keyboard injection use a preallocated 32-entry bounded FIFO. The hook executes it serially after returning to its message loop. Adjacent requests share a wakeup; failures reset input asynchronously. Keep the queue bounded, without polling, sleep, extra injection threads, or waits on movement hot paths.

Before release, run:

bash
cargo fmt --check
cargo test --all-features --lib --tests -- --test-threads=1
cargo clippy --all-targets --all-features -- -D warnings
cargo doc --no-deps
pnpm docs:check
pnpm docs:build

Use isolated local adapters for benchmarks without expanding production APIs. See Workflow and tests for commands.