From 39526728733cbd174d8e67f98e99dcfee639c7f5 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Thu, 9 Jul 2026 22:18:21 +0200 Subject: [PATCH] =?UTF-8?q?fix(clients/android):=203-finger=20stats=20tap?= =?UTF-8?q?=20=E2=80=94=20re-anchor=20the=20scroll=20centroid=20on=20point?= =?UTF-8?q?er-count=20changes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The two-or-more-finger scroll path measured its centroid across a VARYING pointer count: real fingers never land (or lift) in the same input frame, so the 2→3 transition moved the centroid far past SCROLL_DIV, emitted a phantom wheel tick to the host, and set `moved` — which disqualified the not-moved tap classification and made the advertised 3-finger stats-cycle tap unreachable on real hardware (two-finger right-click survived only because its anchor never crossed a count change). Re-anchor whenever the finger count changes; genuine two-finger scrolling is measured between same-count frames exactly as before. Co-Authored-By: Claude Fable 5 --- .../src/main/kotlin/io/unom/punktfunk/TouchInput.kt | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/clients/android/app/src/main/kotlin/io/unom/punktfunk/TouchInput.kt b/clients/android/app/src/main/kotlin/io/unom/punktfunk/TouchInput.kt index 4bed495e..81348165 100644 --- a/clients/android/app/src/main/kotlin/io/unom/punktfunk/TouchInput.kt +++ b/clients/android/app/src/main/kotlin/io/unom/punktfunk/TouchInput.kt @@ -127,6 +127,7 @@ internal suspend fun PointerInputScope.streamTouchInput( var moved = false var maxFingers = 1 var scrolling = false + var scrollCount = 0 // pointer count the scroll centroid is anchored at var prevCx = startX var prevCy = startY var upTime = down.uptimeMillis @@ -149,11 +150,18 @@ internal suspend fun PointerInputScope.streamTouchInput( if (pressed.size > maxFingers) maxFingers = pressed.size if (pressed.size >= 2) { - // Two fingers → scroll by the centroid delta; never move the cursor. + // Two+ fingers → scroll by the centroid delta; never move the cursor. val cx = (pressed.sumOf { it.position.x.toDouble() } / pressed.size).toFloat() val cy = (pressed.sumOf { it.position.y.toDouble() } / pressed.size).toFloat() - if (!scrolling) { + // (Re-)anchor whenever the finger COUNT changes, not just on scroll start: the + // centroid of three fingers sits far from the centroid of two, and real fingers + // never land (or lift) in the same input frame — so the 2→3 transition would + // otherwise read as a scroll notch, sending a phantom wheel tick to the host AND + // setting `moved`, which disqualified the tap classification below and made the + // 3-finger stats tap unreachable on real hardware. + if (!scrolling || pressed.size != scrollCount) { scrolling = true + scrollCount = pressed.size prevCx = cx prevCy = cy }