Back to logs
2026-03-10 enginemapping-core

ComboEngine and cross-device combo resolution

The ComboEngine is the most complex piece of MapXr's core logic. It has to correctly distinguish between a single tap, a double tap, and a two-handed combo. All from a stream of raw tap events arriving over Bluetooth with no inherent timing guarantees.

The resolution problem

When a tap event arrives, the engine can't immediately know whether it's:

  • A standalone single tap
  • The first tap of a double-tap
  • The first half of a cross-device combo

It has to wait for a short window to see if more events arrive before resolving. This window (300 ms by default) is long enough to catch intentional combos but short enough that single taps feel instantaneous.

State machine

The engine tracks a TapPending state per profile type:

  • None: no pending events
  • One(event, deadline): one event received, waiting for a possible second
  • Two(event1, event2): two events received, resolving as double-tap or combo

When the deadline passes without a second event, the pending single tap resolves. When a second event arrives before the deadline, the engine checks whether they form a valid cross-device combo (different devices, matching a combo trigger) or a double-tap (same device, same fingers).

Deterministic tests with tokio::time

Timing-sensitive tests use tokio::time::pause() and tokio::time::advance(Duration) to control the clock without actually sleeping. This makes the test suite fast and deterministic. The 300 ms combo window can be "waited out" in zero real time.

The test suite covers single, double, cross-device combos, timeout resolution, rapid alternating events, and the dual-profile stacking behaviour where same-device events intentionally accumulate.