From 60af4de3ba990971efa7577634b13b100cad8026 Mon Sep 17 00:00:00 2001 From: enricobuehler Date: Mon, 13 Jul 2026 22:05:13 +0200 Subject: [PATCH] docs(gamepad/android): document the two-motor vibratorIds ordering assumption (G20) The two-motor split assumes ids[0] = light/right and ids[1] = heavy/left, an ordering `VibratorManager.getVibratorIds()` does not guarantee. Record the assumption and its tactile-only failure mode (a heavy-first pad inverts the feel but nothing silences or crashes) at the call site. No behavior change: a per-pad fix needs on-glass verification, and a blanket count-based fallback is unsafe (extra ids may be DualSense trigger actuators that must stay silent). Co-Authored-By: Claude Opus 4.8 --- .../main/kotlin/io/unom/punktfunk/kit/GamepadFeedback.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/clients/android/kit/src/main/kotlin/io/unom/punktfunk/kit/GamepadFeedback.kt b/clients/android/kit/src/main/kotlin/io/unom/punktfunk/kit/GamepadFeedback.kt index b82daf75..76583034 100644 --- a/clients/android/kit/src/main/kotlin/io/unom/punktfunk/kit/GamepadFeedback.kt +++ b/clients/android/kit/src/main/kotlin/io/unom/punktfunk/kit/GamepadFeedback.kt @@ -214,7 +214,13 @@ class GamepadFeedback(private val handle: Long, private val router: GamepadRoute } val combo = CombinedVibration.startParallel() if (bind.amplitudeControlled && bind.ids.size >= 2) { - // ids[0] = light/right, ids[1] = heavy/left (XInput/Moonlight convention). + // Two-motor split — ASSUMPTION: ids[0] = light/right, ids[1] = heavy/left + // (XInput/Moonlight convention). Android does not guarantee the order of + // VibratorManager.getVibratorIds(), so a pad that enumerates heavy-first would + // invert the feel: the stronger amplitude drives the physically-lighter motor. + // Failure mode is tactile only — both motors still fire, nothing silences or + // crashes — so this stays the default pending per-pad on-glass verification (G20). + // ids beyond the first two (rare) are left alone here. if (hi != 0) combo.addVibrator(bind.ids[0], oneShot(hi, durationMs)) if (lo != 0) combo.addVibrator(bind.ids[1], oneShot(lo, durationMs)) } else {