startCapture method

  1. @override
Future<void> startCapture({
  1. void onKeyboard(
    1. MiniAVKeyboardEvent event,
    2. Object? userData
    )?,
  2. void onMouse(
    1. MiniAVMouseEvent event,
    2. Object? userData
    )?,
  3. void onGamepad(
    1. MiniAVGamepadEvent event,
    2. Object? userData
    )?,
  4. void onMotion(
    1. MiniAVMotionEvent event,
    2. Object? userData
    )?,
  5. Object? userData,
})
override

Start input capture. Provide callbacks for each input type you want to receive.

Implementation

@override
Future<void> startCapture({
  void Function(MiniAVKeyboardEvent event, Object? userData)? onKeyboard,
  void Function(MiniAVMouseEvent event, Object? userData)? onMouse,
  void Function(MiniAVGamepadEvent event, Object? userData)? onGamepad,
  void Function(MiniAVMotionEvent event, Object? userData)? onMotion,
  Object? userData,
}) async {
  // Motion is NOT delivered by this backend. The native bindings now mirror
  // the C struct exactly (motion_rate_hz / motion_mode / motion_callback),
  // but they are written zero/null and MINIAV_INPUT_TYPE_MOTION is never
  // requested, so no C backend ever starts a sensor and
  // miniav_input_deliver_motion's null-check keeps the pointer un-called.
  // Wiring it up is: set the MOTION bit, install a NativeCallable in
  // motion_callback, and bind MiniAV_Input_GetLatestMotion for the pull API.
  // Until then `onMotion` is accepted (the shared interface signature) and
  // provably never invoked here; the web backend implements motion fully.
  _ensureNotDestroyed();

  if (_pendingConfig == null) {
    throw StateError('configure() must be called before startCapture()');
  }

  await stopCapture(); // Clean up any previous capture

  // Create NativeCallable listeners for each provided callback
  void ffiKeyboardCallback(
    ffi.Pointer<bindings.MiniAVKeyboardEvent> eventPtr,
    ffi.Pointer<ffi.Void> cbUserData,
  ) {
    if (_isDestroyed || onKeyboard == null) return;
    try {
      onKeyboard(keyboardEventFromNative(eventPtr.ref), userData);
    } catch (e, s) {
      print('Error in keyboard callback: $e\n$s');
    }
  }

  void ffiMouseCallback(
    ffi.Pointer<bindings.MiniAVMouseEvent> eventPtr,
    ffi.Pointer<ffi.Void> cbUserData,
  ) {
    if (_isDestroyed || onMouse == null) return;
    try {
      onMouse(mouseEventFromNative(eventPtr.ref), userData);
    } catch (e, s) {
      print('Error in mouse callback: $e\n$s');
    }
  }

  void ffiGamepadCallback(
    ffi.Pointer<bindings.MiniAVGamepadEvent> eventPtr,
    ffi.Pointer<ffi.Void> cbUserData,
  ) {
    if (_isDestroyed || onGamepad == null) return;
    try {
      onGamepad(gamepadEventFromNative(eventPtr.ref), userData);
    } catch (e, s) {
      print('Error in gamepad callback: $e\n$s');
    }
  }

  _keyboardCallbackHandle =
      ffi.NativeCallable<bindings.MiniAVKeyboardCallbackFunction>.listener(
        ffiKeyboardCallback,
      );
  _mouseCallbackHandle =
      ffi.NativeCallable<bindings.MiniAVMouseCallbackFunction>.listener(
        ffiMouseCallback,
      );
  _gamepadCallbackHandle =
      ffi.NativeCallable<bindings.MiniAVGamepadCallbackFunction>.listener(
        ffiGamepadCallback,
      );

  // Build the native config struct with callbacks
  final configPtr = calloc<bindings.MiniAVInputConfig>();
  try {
    copyInputConfigToNative(
      _pendingConfig!,
      configPtr.ref,
      keyboardCb: _keyboardCallbackHandle!.nativeFunction,
      mouseCb: _mouseCallbackHandle!.nativeFunction,
      gamepadCb: _gamepadCallbackHandle!.nativeFunction,
    );

    final configRes = bindings.MiniAV_Input_Configure(
      _contextHandle!,
      configPtr,
    );
    if (configRes != bindings.MiniAVResultCode.MINIAV_SUCCESS) {
      await _cleanupCallbacks();
      throw Exception('Failed to configure input context: ${configRes.name}');
    }
  } finally {
    calloc.free(configPtr);
  }

  final startRes = bindings.MiniAV_Input_StartCapture(_contextHandle!);
  if (startRes != bindings.MiniAVResultCode.MINIAV_SUCCESS) {
    await _cleanupCallbacks();
    throw Exception('Failed to start input capture: ${startRes.name}');
  }
}