Skip to content

MOB-518: per-node multi-pointer on_touch - #238

Merged
GenericJam merged 1 commit into
masterfrom
mob-518-on-touch
Nov 13, 2026
Merged

GenericJam merged 1 commit into
masterfrom
mob-518-on-touch

Conversation

@GenericJam

Copy link
Copy Markdown
Owner

MOB-518: on_touch on any node. Each finger arrives as its own {:touch, tag, %{phase: :down | :move | :up | :cancel, pointer_id, x, y, width, height, ts}} stream. x and y are in dp relative to the node. A finger stays with the node it went down on until it lifts. Moves are opt-in (moves: true, throttled per pointer natively). The down is delivered without a disambiguation delay, and the observer never consumes events. Design: decisions/2026-11-12-per-node-multi-pointer-on-touch.md.

Android bridge: GenericJam/mob_new (branch MOB-518-on-touch, linked PR). Linear: MOB-518.

Verification

  • Host tests: the full mob suite passes (2185), credo --strict is clean, zig test touch_throttle.zig passes, test/native touch_throttle_test passes, and the iOS NIF (debug + release, with Swift) compiles.
  • iPhone SE (physical), driven by XCTest touches through agent-device:
    • The down/up position in the node is correct (tap at pad-local 84,79 reported as 84,79).
    • A one-finger drag from pad_1 to pad_2 was captured by pad_1: 58 throttled moves, up at x=268 outside its 169.5 width. pad_2 received nothing from it.
    • on_tap works on plain buttons and on an on_tap-only pad.
    • On a pad with on_touch + on_tap, the tap was lost before the tolerant-tap fix. After the fix, 10/10 taps arrived, each as down, up, then tap.
  • iOS simulator, two-finger synthesis: two distinct pointer_ids went down on one pad at the same time. Their ups were only delivered on the next touch, which looks like an artifact of the synthesizer (see the report).
  • Not yet verified: two real fingers on iOS (asked Kevin) and Android on a device (the Moto was leased by another agent the whole time).

Release gate (review finding, deliberately deferred): generated apps call the new mob_touch_* symbols. So mob_new's mob floor must move to the mob release that contains this PR, at release time. That version doesn't exist yet, and pinning it now would break generator CI at deps.get.

Reviewer: reviewer subagent (Codex rate-limited until ~9:31 PM). First pass DO NOT SHIP. Fixed since: on_touch is kept on gpu_view, and the touch observer has a distinct Compose key. The dependency floor is deferred as above. Re-review is pending.

@GenericJam

Copy link
Copy Markdown
Owner Author

Android device verification (Moto G Power 2021, Android 11), fixture touch_fix built from this branch + mob_new#115's current bridge. Multi-pointer input via agent-device (UiAutomation injection; raw sendevent is blocked by SELinux on this build). Events read from the fixture's logcat lines.

Check Result
Two fingers at once on two different on_touch nodes (pinch centred on the gap between pad_1 and pad_2) ✅ pad_2 down id=0, pad_1 down id=1, one up each, x/y in dp relative to each node (w 188 = 329 px ÷ 1.75), moves only on the node with moves: true (pad_1, 3–7), none on pad_2
One finger dragged from pad_1 onto pad_2 ✅ pad_1: down, 40 moves (~17 ms apart over 700 ms, the 16 ms throttle), up at x=308 (past its 187 dp width); pad_2: nothing
Two fingers on one node ✅ pad_3 ids 3 and 4, distinct y, each with its own up
on_tap on an on_touch node ✅ 10/10 (down, up, tap) after the tolerant-tap change in mobIsPressNode. A fixture generated before that commit lost 10/10 (MobNIF: rejected stale event handle), so the change is load-bearing on Android too
on_tap-only node, plain button ✅ 3/3, 2/2
Cancel path: app sent to background mid-touch (HOME) ✅ down then exactly one cancel, no later up

Note, not a regression: a finger that lifts inside an on_tap node after moving within it still counts as a tap (Compose's detectTapGestures only cancels on leaving the node or consumption); same as any on_tap node today.

Still open: real two-finger on the iPhone (needs a person; XCTest can't synthesize two pointers on a physical device), iOS 17, release builds.

@GenericJam
GenericJam merged commit 7f20f54 into master Nov 13, 2026
4 checks passed
@GenericJam GenericJam mentioned this pull request Nov 13, 2026
GenericJam added a commit to GenericJam/mob_new that referenced this pull request Nov 13, 2026
MOB-518. Android verified on a Moto G Power 2021; see GenericJam/mob#238.
@GenericJam
GenericJam deleted the mob-518-on-touch branch November 13, 2026 07:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant