Phase 0 of controller-support epic: labelle-toolkit/labelle-engine#609
Goal
Add the cross-backend gamepad event source to InputInterface (labelle-core/src/input.zig). No engine behavior change yet — this is the contract Phase 1 backends implement against.
Scope
- Define a
GamepadEvent value type (copied data, no borrowed slices — it crosses a ring buffer):
pub const GamepadEvent = struct {
kind: enum { connected, disconnected },
slot: u32,
name: BoundedArray(u8, 63) = .{}, // empty if backend can't supply
guid: ?[16]u8 = null, // stable reconnection key where available
source_class: enum { gamepad, dpad_remote, unknown } = .unknown, // TV remote vs pad
type_hint: enum { unknown, xbox, playstation, nintendo, generic } = .unknown,
};
- Add optional decls, wrapped with
@hasDecl(Impl, ...) like the existing gamepad methods:
pub fn pollGamepadEvents(out: []GamepadEvent) usize — drain pending hotplug events; returns count.
pub fn describeGamepads(out) usize (diagnostic) — enumerate currently-visible devices incl. an unavailable_reason (e.g. permission denied on Linux) for logging.
- Default behavior when a backend declares neither: return 0 (no events) — preserves today's "nothing happens" for null/sokol until Phase 1 wires platform sources.
Acceptance criteria
Non-goals
- No backend implementations (Phase 1). No engine wiring (#610).
Phase 0 of controller-support epic: labelle-toolkit/labelle-engine#609
Goal
Add the cross-backend gamepad event source to
InputInterface(labelle-core/src/input.zig). No engine behavior change yet — this is the contract Phase 1 backends implement against.Scope
GamepadEventvalue type (copied data, no borrowed slices — it crosses a ring buffer):@hasDecl(Impl, ...)like the existing gamepad methods:pub fn pollGamepadEvents(out: []GamepadEvent) usize— drain pending hotplug events; returns count.pub fn describeGamepads(out) usize(diagnostic) — enumerate currently-visible devices incl. anunavailable_reason(e.g. permission denied on Linux) for logging.Acceptance criteria
GamepadEvent+describeGamepadsresult type defined and documented.InputInterfaceexposespollGamepadEvents/describeGamepadswith@hasDeclfallbacks returning 0.isGamepadAvailable, etc.) untouched.Non-goals