Skip to content

feat(sokol/android): JNI InputManager gamepad detection source — centerpiece (Phase 1) #248

@apotema

Description

@apotema

Phase 1 of controller-support epic: labelle-toolkit/labelle-engine#609centerpiece (Android TV is sokol-only).

Goal

A sokol+Android gamepad detection source: detect controllers connected/removed and supply a stable reconnection key, via JNI to Android's InputManager — independent of sokol's render/input pipeline.

Why this is the priority

Android TV is sokol-only (verified — no other backend has an Android template). sokol has zero gamepad support today and owns the AInputQueue with no raw-event hook. But hotplug detection does not need the event stream — InputManager is reachable purely via JNI.

Available hook

sapp_android_get_native_activity() is exposed by the sokol fork (app.zig:2470, currently unused) → gives the ANativeActivity → JNIEnv. sokol here is a labelle fork (commit 887b30f), so adding a tiny JNI shim/companion is feasible.

Scope

Build a gamepad_source sub-package (in-tree, per the sub-package convention) with an Android impl:

  1. Via JNI: InputManager.getInputDeviceIds() to enumerate at startup; register an InputDeviceListener (onInputDeviceAdded/Removed) → push GamepadEvents into a ring buffer.
  2. Identity: InputDevice.getDescriptor() → derive a stable guid; getName()name; getSources() → set source_class (gamepad vs dpad_remote — TV remotes must be distinguishable).
  3. Marshal listener callbacks (Looper thread) safely into the ring buffer the engine drains in tick.
  4. Wire pollGamepadEvents for the sokol backend on Android to this source (engine fallback path from Phase 0).
  5. Manifest: ensure <uses-feature android.hardware.gamepad required="false"> in the Android template; keep D-pad navigability; intercept controller-B → KEYCODE_BACK so it doesn't exit the app.

Acceptance criteria

  • On an Android TV device/emulator: connecting/removing a BT pad emits connect/disconnect with stable descriptor-based guid.
  • Pre-paired controller is discovered at startup.
  • TV remote reports source_class = dpad_remote, real pad reports gamepad.

Non-goals (→ Phase 3, full-gameplay state)

  • Button/axis state is NOT in this issue. Reading analog state on sokol+Android requires forwarding AInputEvents (sokol-fork patch) or Paddleboat/AGDK + semantic mapping — tracked as the Phase 3 epic. This issue delivers detection/removal/identity only.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions