Skip to content

Add Native Input Capture (Keyboard/Mouse/Gamepad/Touch/MIDI) API (No Web Support) #6

@MichealReed

Description

@MichealReed

Summary
Implement a unified native input subsystem for MiniAV to capture real-time user/system input events: keyboard, mouse, gamepad, touch (multi-touch / gestures), and MIDI (note/control/system messages). Web is explicitly out-of-scope for this phase.

Goals

  • Cross-platform (Windows, macOS, Linux) unified C API.
  • Event callback model with low-latency, monotonic timestamps.
  • Device enumeration + hotplug notifications.
  • Normalized key / button / axis / touch / MIDI representations.
  • Support both push (OS callbacks) and poll backends internally.
  • Efficient lock-free ring buffer between backend threads and user callback.
  • Touch abstraction (multi-touch points) where available.
  • MIDI: raw message bytes + parsed helpers (status, channel, data bytes, SysEx support).
  • Optional gamepad vibration (where supported).

Out of Scope (for now)

  • Web, Android, iOS.
  • Gesture recognition (pinch/rotate) beyond raw touch points.
  • High-level MIDI routing/thru.

API Proposal (Draft)

#pragma once
#include "miniav_types.h"
#include <stdint.h>

#ifdef __cplusplus
extern "C" {
#endif

typedef enum MiniAVInputDeviceType {
    MINIAV_INPUT_DEVICE_KEYBOARD = 1,
    MINIAV_INPUT_DEVICE_MOUSE    = 2,
    MINIAV_INPUT_DEVICE_GAMEPAD  = 3,
    MINIAV_INPUT_DEVICE_TOUCH    = 4,
    MINIAV_INPUT_DEVICE_MIDI     = 5,
    MINIAV_INPUT_DEVICE_UNKNOWN  = 0xFF
} MiniAVInputDeviceType;

typedef enum MiniAVInputEventKind {
    MINIAV_INPUT_EVENT_KEY_DOWN = 1,
    MINIAV_INPUT_EVENT_KEY_UP,
    MINIAV_INPUT_EVENT_MOUSE_MOVE,
    MINIAV_INPUT_EVENT_MOUSE_BUTTON_DOWN,
    MINIAV_INPUT_EVENT_MOUSE_BUTTON_UP,
    MINIAV_INPUT_EVENT_MOUSE_WHEEL,
    MINIAV_INPUT_EVENT_GAMEPAD_BUTTON_DOWN,
    MINIAV_INPUT_EVENT_GAMEPAD_BUTTON_UP,
    MINIAV_INPUT_EVENT_GAMEPAD_AXIS,
    MINIAV_INPUT_EVENT_TOUCH_BEGIN,
    MINIAV_INPUT_EVENT_TOUCH_MOVE,
    MINIAV_INPUT_EVENT_TOUCH_END,
    MINIAV_INPUT_EVENT_TOUCH_CANCEL,
    MINIAV_INPUT_EVENT_TOUCH_FRAME,      // frame delimiter batching points
    MINIAV_INPUT_EVENT_MIDI_MESSAGE,
    MINIAV_INPUT_EVENT_DEVICE_ADDED,
    MINIAV_INPUT_EVENT_DEVICE_REMOVED
} MiniAVInputEventKind;

typedef struct MiniAVInputDeviceInfo {
    const char* device_id;
    const char* name;
    MiniAVInputDeviceType type;
    uint32_t vendor_id;
    uint32_t product_id;
} MiniAVInputDeviceInfo;

typedef enum MiniAVTouchPhase {
    MINIAV_TOUCH_PHASE_UNKNOWN = 0,
    MINIAV_TOUCH_PHASE_BEGIN,
    MINIAV_TOUCH_PHASE_MOVE,
    MINIAV_TOUCH_PHASE_END,
    MINIAV_TOUCH_PHASE_CANCEL
} MiniAVTouchPhase;

typedef struct MiniAVTouchPoint {
    uint64_t touch_id;     // stable per contact
    float x;               // normalized 0..1 or pixels (flagged)
    float y;
    float pressure;        // 0..1 if available else -1
    float major_radius;    // -1 if unknown
    float minor_radius;    // -1 if unknown
    MiniAVTouchPhase phase;
} MiniAVTouchPoint;

typedef struct MiniAVMidiMessage {
    uint64_t timestamp_ns;
    uint8_t status;
    uint8_t data1;
    uint8_t data2;
    const uint8_t* sysex;      // if SysEx, points to buffer (owned until event released)
    uint32_t sysex_length;
    uint8_t is_sysex;
    uint8_t channel;           // 0-15 if channel voice
} MiniAVMidiMessage;

typedef enum MiniAVKey {
    MINIAV_KEY_UNKNOWN = 0,
    // (expand as needed)
    MINIAV_KEY_A, MINIAV_KEY_B, MINIAV_KEY_C, MINIAV_KEY_Z = MINIAV_KEY_A + 25,
    MINIAV_KEY_0, MINIAV_KEY_1, MINIAV_KEY_9 = MINIAV_KEY_0 + 9,
    MINIAV_KEY_ENTER,
    MINIAV_KEY_ESCAPE,
    MINIAV_KEY_SPACE,
    MINIAV_KEY_LEFT, MINIAV_KEY_RIGHT, MINIAV_KEY_UP, MINIAV_KEY_DOWN,
    MINIAV_KEY_SHIFT_LEFT, MINIAV_KEY_SHIFT_RIGHT,
    MINIAV_KEY_CTRL_LEFT, MINIAV_KEY_CTRL_RIGHT,
    MINIAV_KEY_ALT_LEFT, MINIAV_KEY_ALT_RIGHT,
    MINIAV_KEY_SUPER_LEFT, MINIAV_KEY_SUPER_RIGHT,
    MINIAV_KEY_F1, MINIAV_KEY_F12 = MINIAV_KEY_F1 + 11
} MiniAVKey;

typedef struct MiniAVInputEvent {
    uint64_t timestamp_ns;
    const char* device_id;
    MiniAVInputDeviceType device_type;
    MiniAVInputEventKind kind;
    union {
        struct { MiniAVKey key; } key;
        struct { int32_t x; int32_t y; int32_t dx; int32_t dy; } mouse_move;
        struct { int32_t x; int32_t y; int32_t dx; int32_t dy; uint8_t button; } mouse_button;
        struct { float wheel_x; float wheel_y; int32_t pixel_delta_x; int32_t pixel_delta_y; } mouse_wheel;
        struct { uint16_t button; uint8_t pressed; } gamepad_button;
        struct { uint16_t axis; float value; } gamepad_axis;
        struct { const MiniAVTouchPoint* points; uint32_t count; } touch; // for individual + frame events
        struct { MiniAVMidiMessage msg; } midi;
        struct { MiniAVInputDeviceInfo info; } device_change;
    } data;
} MiniAVInputEvent;

typedef void (*MiniAVInputEventCallback)(const MiniAVInputEvent* event, void* user);

typedef struct MiniAVInputContext__* MiniAVInputContextHandle;

MiniAVResultCode MiniAV_Input_EnumerateDevices(MiniAVInputDeviceInfo** devices, uint32_t* count);
MiniAVResultCode MiniAV_Input_FreeDeviceList(MiniAVInputDeviceInfo* devices, uint32_t count);

MiniAVResultCode MiniAV_Input_CreateContext(MiniAVInputContextHandle* ctx);
MiniAVResultCode MiniAV_Input_DestroyContext(MiniAVInputContextHandle ctx);

MiniAVResultCode MiniAV_Input_Start(MiniAVInputContextHandle ctx, MiniAVInputEventCallback cb, void* user);
MiniAVResultCode MiniAV_Input_Stop(MiniAVInputContextHandle ctx);

MiniAVResultCode MiniAV_Input_GamepadSetVibration(MiniAVInputContextHandle ctx, const char* device_id, float low_freq, float high_freq, uint32_t duration_ms);

#ifdef __cplusplus
}
#endif

Backend Strategy

  • Windows: Raw Input (keyboard/mouse), WM_POINTER / WM_TOUCH, XInput + GameInput (gamepad), WinRT MIDI (preferred) with fallback to WinMM.
  • macOS: CGEventTap (KB/mouse), NSTouch (trackpad), GameController.framework (gamepad), CoreMIDI (MIDI).
  • Linux: evdev + libudev (keyboard/mouse/gamepad/touch), multitouch via ABS_MT* events, ALSA sequencer (MIDI). Fallback X11 for basic KB/mouse if no /dev/input access.
  • Touch: Only if device exposes MT events or platform API supports it.
  • Ring buffer size configurable (e.g. env MINIAV_INPUT_RING=4096).

Threading Model

  • Each backend has one dispatcher thread (message loop / epoll / run loop).
  • Events written to lock-free SPSC ring; consumer thread optionally merges TOUCH_* into FRAME delimiting events before invoking user callback on calling thread or dedicated dispatch thread (configurable).

Device ID Format (Proposal)

  • Windows: "win:<handle_hex>"
  • macOS: "mac:" or GCController vendor/product
  • Linux: "linux:evdev::"
  • MIDI: Append ":midi:<endpoint_unique>"

Memory Rules

  • Strings stable for context lifetime.
  • Touch point arrays owned until callback returns (user must copy).
  • SysEx data freed after callback returns.

Acceptance Criteria

  • Enumerate returns devices with correct categorization.
  • Receive key down/up, mouse move, button, wheel, gamepad button/axis, touch begin/move/end, MIDI note-on/off/control-change, device add/remove.
  • Timestamps monotonic and strictly non-decreasing per device.
  • Stress test (rapid input 5k events/sec) without drops (unless ring overflow -> documented error metric).
  • Clean shutdown without leaks (validated by ASAN on Linux, Instruments on macOS, heap leak detector on Windows).

Risk / Notes

  • Permissions: Linux access to /dev/input requires group (input) or elevated; document fallback.
  • High DPI / coordinate normalization differences; decide pixel vs normalized (current: store raw pixels; normalization optional later).
  • MIDI SysEx large messages may need pooled buffers (future optimization).

Initial Task Breakdown

  1. Define public header + enums (above).
  2. Implement core input context + ring buffer (platform-agnostic).
  3. Windows backend: Raw Input + XInput + minimal MIDI (WinMM).
  4. Linux backend: evdev + udev hotplug + ALSA MIDI.
  5. macOS backend: CGEventTap + GameController + CoreMIDI.
  6. Touch handling: implement per platform where available.
  7. Gamepad vibration API (Windows/XInput + macOS if supported).
  8. Add logging + error translation layer.
  9. Add basic tests (simulated injection where possible / mock).
  10. Documentation + examples (print events).

Future Enhancements

  • Batch event callback variant.
  • State snapshot API (poll last known state).
  • Gesture synthesis (pinch/zoom).
  • Mapping layer for customizable key/gamepad remapping.

Metadata

Metadata

Assignees

No one assigned

    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