diff --git a/hid/example/pubspec.yaml b/hid/example/pubspec.yaml index daaea53..b64ba3b 100644 --- a/hid/example/pubspec.yaml +++ b/hid/example/pubspec.yaml @@ -3,7 +3,8 @@ description: Demonstrates how to use the hid plugin. publish_to: "none" # Remove this line if you wish to publish to pub.dev environment: - sdk: ">=2.15.1 <3.0.0" + sdk: '>=3.0.0 <4.0.0' + flutter: ">=3.10.0" dependencies: flutter: @@ -14,7 +15,7 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^1.0.0 + flutter_lints: ^3.0.1 # From a real application you should not have this dependency_overrides: @@ -28,6 +29,8 @@ dependency_overrides: path: ../../hid_macos hid_windows: path: ../../hid_windows + hid_platform_interface: + path: ../../hid_platform_interface flutter: uses-material-design: true diff --git a/hid/lib/hid.dart b/hid/lib/hid.dart index 7bddad6..a779f15 100644 --- a/hid/lib/hid.dart +++ b/hid/lib/hid.dart @@ -3,6 +3,6 @@ import 'package:hid_platform_interface/hid_platform_interface.dart'; export 'package:hid_platform_interface/device.dart'; HidPlatform get _platform => HidPlatform.instance; -Future> getDeviceList() { - return _platform.getDeviceList(); +Future> getDeviceList({int? vendorId, int? productId}) { + return _platform.getDeviceList(vendorId: vendorId, productId: productId); } diff --git a/hid/pubspec.yaml b/hid/pubspec.yaml index c672e55..d4ccdfa 100644 --- a/hid/pubspec.yaml +++ b/hid/pubspec.yaml @@ -1,25 +1,26 @@ name: hid description: A multi-platform plugin which allows an application to interface with USB HID-Class devices on Linux, Windows, Android, and macOS. -version: 0.1.6 +version: 0.1.7 repository: https://github.com/rustui/hid/tree/main/hid environment: - sdk: ">=2.15.0 <3.0.0" - flutter: ">=2.5.0" + sdk: '>=3.0.0 <4.0.0' + flutter: ">=3.10.0" dependencies: flutter: sdk: flutter - hid_platform_interface: ^0.0.8 - hid_windows: ^0.1.0 - hid_macos: ^0.1.1 - hid_linux: ^0.1.0 - hid_android: ^0.1.3 + ffi: ^2.1.0 + hid_platform_interface: ^0.0.9 + hid_windows: ^0.1.1 + hid_macos: ^0.1.2 + hid_linux: ^0.1.1 + hid_android: ^0.1.4 dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^1.0.0 + flutter_lints: ^3.0.1 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt index 787dfa9..55f7306 100644 --- a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt +++ b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidAndroidPlugin.kt @@ -16,6 +16,13 @@ import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel.MethodCallHandler import io.flutter.plugin.common.MethodChannel.Result +import kotlin.experimental.and + +private const val REQUEST_GET_REPORT = 0x01; +private const val REQUEST_SET_REPORT = 0x09; +private const val REPORT_TYPE_INPUT = 0x0100; +private const val REPORT_TYPE_OUTPUT = 0x0200; +private const val REPORT_TYPE_FEATURE = 0x0300; /** HidAndroidPlugin */ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { @@ -26,8 +33,6 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { private val gson = Gson() private var connection: UsbDeviceConnection? = null private var device: UsbDevice? = null - private var interfaceIndex: Int? = null - private var endpointIndex: Int? = null override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { channel = MethodChannel(flutterPluginBinding.binaryMessenger, "hid_android") @@ -40,26 +45,31 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { when (call.method) { "getDeviceList" -> { + val vendorId: Int? = call.argument("vendorId") + val productId: Int? = call.argument("productId") val devices: MutableList = mutableListOf() for (device in usbManager.deviceList.values) { try { - val json = gson.toJson( - HidDevice( - device.vendorId, - device.productId, - device.serialNumber ?: "", - device.productName ?: "", - device.deviceName + if (((vendorId == null) or (device.getVendorId() == (vendorId ?: 0))) and + ((productId == null) or (device.getProductId() == (productId ?: 0)))) { + val json = gson.toJson( + HidDevice( + device.vendorId, + device.productId, + device.serialNumber ?: "", + device.productName ?: "", + device.deviceName + ) ) - ) - devices.add(json) + devices.add(json) + } } catch (e: Exception) { val permissionIntent = PendingIntent.getBroadcast( context, 0, Intent("ACTION_USB_PERMISSION"), - 0 + PendingIntent.FLAG_IMMUTABLE ) usbManager.requestPermission(device, permissionIntent) } @@ -67,40 +77,183 @@ class HidAndroidPlugin : FlutterPlugin, MethodCallHandler { result.success(devices) } "open" -> { - device = usbManager.deviceList[call.argument("deviceName")]!! - connection = usbManager.openDevice(device) - (interfaceIndex, endpointIndex) = getReadIndices(device!!)!! - result.success( - connection!!.claimInterface( - device!!.getInterface(interfaceIndex!!), - true - ) - ) + try { + device = usbManager.deviceList[call.argument("deviceName")]!! + connection = usbManager.openDevice(device) + + result.success( true ) + } catch (e: Exception) { + result.error("error", e.message, e.getStackTrace().joinToString(separator = "\n")) + } } "read" -> { - if (connection != null) { - val length: Int = call.argument("length")!! - val duration: Int = call.argument("duration")!! - Thread { - kotlin.run { - val array = ByteArray(length) - connection!!.bulkTransfer( - device!!.getInterface(i).getEndpoint(j), - array, - length, - duration - ) - result.success(array.map { it.toUByte().toInt() }) + try { + if (connection != null) { + val length: Int = call.argument("length")!! + val duration: Int = call.argument("duration")!! + val pair = getReadIndices(device!!) + if (pair == null) { + result.error("error", "Could not find interface and endpoint for read operation", call.method) + } else { + val interfaceIndex = pair.first; + val endpointIndex = pair.second; + if (!connection!!.claimInterface(device!!.getInterface(interfaceIndex!!), true)) { + result.error("error", "Could not claim interface for read operation", call.method) + } else { + Thread { + kotlin.run { + val array = ByteArray(length) + connection!!.bulkTransfer( + device!!.getInterface(interfaceIndex!!).getEndpoint(endpointIndex!!), + array, + length, + duration + ) + result.success(array.map { it.toUByte().toInt() }) + } + }.start() + } } - }.start() - } else { - result.error("error", "error", "error") + } else { + result.error("error", "device connection is null", call.method) + } + } catch (e: Exception) { + result.error("error", e.message, e.getStackTrace().joinToString(separator = "\n")) + } + } + "write" -> { + try { + if (connection != null) { + val bytes: ByteArray = call.argument("bytes")!! + val pair = getWriteIndices(device!!) + if (pair == null) { + result.error("error", "Could not find interface and endpoint for write operation", call.method) + } else { + val interfaceIndex = pair.first; + val endpointIndex = pair.second; + if (!connection!!.claimInterface(device!!.getInterface(interfaceIndex!!), true)) { + result.error("error", "Could not claim interface for write operation", call.method) + } else { + Thread { + kotlin.run { + try { + connection!!.bulkTransfer( + device!!.getInterface(interfaceIndex!!).getEndpoint(endpointIndex!!), + bytes, + bytes.size, + 1000 + ) + result.success(0) + } catch (e: Exception) { + result.error("error", e.message, e.getStackTrace().joinToString(separator = "\n")) + } + } + }.start() + } + } + } else { + result.error("error", "device connection is null", call.method) + } + } catch (e: Exception) { + result.error("error", e.message, e.getStackTrace().joinToString(separator = "\n")) + } + } + "setFeature" -> { + try { + if (connection != null) { + val bytes: ByteArray = call.argument("bytes")!! + val pair = getHIDIndices(device!!) + if (pair == null) { + result.error("error", "Could not find interface and endpoint for HID operation", call.method) + } else { + val interfaceIndex = pair.first; + val endpointIndex = pair.second; + if (!connection!!.claimInterface(device!!.getInterface(interfaceIndex!!), true)) { + result.error("error", "Could not claim interface for HID operation", call.method) + } else { + Thread { + kotlin.run { + try { + val reportId = bytes.get(0).toInt() and 0xff + + connection!!.controlTransfer( + UsbConstants.USB_DIR_OUT or UsbConstants.USB_TYPE_CLASS or UsbConstants.USB_INTERFACE_SUBCLASS_BOOT, + REQUEST_SET_REPORT, + reportId or REPORT_TYPE_OUTPUT, + interfaceIndex ?: 0, + bytes, + 1, + bytes.size - 1, + 0 + ) + result.success(0) + } catch (e: Exception) { + result.error("error", e.message, e.getStackTrace().joinToString(separator = "\n")) + } + } + }.start() + } + } + } else { + result.error("error", "device connection is null", call.method) + } + } catch (e: Exception) { + result.error("error", e.message, e.getStackTrace().joinToString(separator = "\n")) + } + } + "getFeature" -> { + try { + if (connection != null) { + val bytes: ByteArray = call.argument("bytes")!! + val pair = getHIDIndices(device!!) + if (pair == null) { + result.error("error", "Could not find interface and endpoint for HID operation", call.method) + } else { + val interfaceIndex = pair.first; + val endpointIndex = pair.second; + if (!connection!!.claimInterface(device!!.getInterface(interfaceIndex!!), true)) { + result.error("error", "Could not claim interface for HID operation", call.method) + } else { + Thread { + kotlin.run { + try { + val reportId = bytes.get(0).toInt() and 0xff + + val array = ByteArray(bytes.size - 1) + + connection!!.controlTransfer( + UsbConstants.USB_DIR_IN or UsbConstants.USB_TYPE_CLASS or UsbConstants.USB_INTERFACE_SUBCLASS_BOOT, + REQUEST_GET_REPORT, + reportId or REPORT_TYPE_INPUT, + interfaceIndex ?: 0, + array, + bytes.size - 1, + 0 + ) + result.success(array.map { it.toUByte().toInt() }) + } catch (e: Exception) { + result.error("error", e.message, e.getStackTrace().joinToString(separator = "\n")) + } + } + }.start() + } + } + } else { + result.error("error", "device connection is null", call.method) + } + } catch (e: Exception) { + result.error("error", e.message, e.getStackTrace().joinToString(separator = "\n")) } } "close" -> { - connection?.close() - connection = null - device = null + try { + connection?.close() + connection = null + device = null + result.success(0) + } catch (e: Exception) { + result.error("error", e.message, e.getStackTrace().joinToString(separator = "\n")) + } } else -> result.notImplemented() } @@ -122,4 +275,32 @@ fun getReadIndices(device: UsbDevice): Pair? { } } return null +} + +fun getWriteIndices(device: UsbDevice): Pair? { + for (i in 0 until device.interfaceCount) { + val inter = device.getInterface(i) + for (j in 0 until inter.endpointCount) { + val endpoint = inter.getEndpoint(j) + if (endpoint.type == UsbConstants.USB_ENDPOINT_XFER_INT && endpoint.direction == UsbConstants.USB_DIR_OUT) { + return Pair(i, j) + } + } + } + return null +} + +fun getHIDIndices(device: UsbDevice): Pair? { + for (i in 0 until device.interfaceCount) { + val inter = device.getInterface(i) + if (inter.getInterfaceClass() == UsbConstants.USB_CLASS_HID) { + for (j in 0 until inter.endpointCount) { + val endpoint = inter.getEndpoint(j) + if (endpoint.type == UsbConstants.USB_ENDPOINT_XFER_INT && endpoint.direction == UsbConstants.USB_DIR_IN) { + return Pair(i, j) + } + } + } + } + return null } \ No newline at end of file diff --git a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidDevice.kt b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidDevice.kt index 921e987..820dae7 100644 --- a/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidDevice.kt +++ b/hid_android/android/src/main/kotlin/com/rustui/hid_android/HidDevice.kt @@ -1,10 +1,17 @@ package com.rustui.hid_android +import com.google.gson.annotations.SerializedName + class HidDevice( + @SerializedName("vendorId") val vendorId: Int, + @SerializedName("productId") val productId: Int, + @SerializedName("serialNumber") val serialNumber: String, + @SerializedName("productName") val productName: String, + @SerializedName("deviceName") val deviceName: String ) { } \ No newline at end of file diff --git a/hid_android/lib/hid_android.dart b/hid_android/lib/hid_android.dart index 460b0da..745721f 100644 --- a/hid_android/lib/hid_android.dart +++ b/hid_android/lib/hid_android.dart @@ -12,18 +12,28 @@ class HidAndroid extends HidPlatform { } @override - Future> getDeviceList() async { + Future> getDeviceList({int? vendorId, int? productId}) async { final List list = []; - final List devices = await _channel.invokeMethod('getDeviceList'); + final List devices = await _channel.invokeMethod('getDeviceList', { + 'vendorId': vendorId, + 'productId': productId, + }); for (var deviceObject in devices) { - final rawDevice = deviceObject! as String; - final device = jsonDecode(rawDevice); - list.add(UsbDevice( - vendorId: device['vendorId'], - productId: device['productId'], - serialNumber: device['serialNumber'], - productName: device['productName'], - deviceName: device['deviceName'])); + final rawDevice = deviceObject as String?; + if (rawDevice != null) { + try { + final json = jsonDecode(rawDevice); + final device = UsbDevice( + vendorId: json['vendorId'], + productId: json['productId'], + serialNumber: json['serialNumber'] ?? '', + productName: json['productName'] ?? '', + deviceName: json['deviceName'] ?? ''); + list.add(device); + } catch (e) { + // ignored + } + } } return list; } @@ -32,17 +42,8 @@ class HidAndroid extends HidPlatform { class UsbDevice extends Device { bool isOpen = false; String deviceName; - UsbDevice( - {required int vendorId, - required int productId, - required String serialNumber, - required String productName, - required this.deviceName}) - : super( - vendorId: vendorId, - productId: productId, - serialNumber: serialNumber, - productName: productName); + UsbDevice({required int vendorId, required int productId, required String serialNumber, required String productName, required this.deviceName}) + : super(vendorId: vendorId, productId: productId, serialNumber: serialNumber, productName: productName); @override Future open() async { @@ -57,8 +58,7 @@ class UsbDevice extends Device { Stream> read(int length, int duration) async* { while (isOpen) { final start = DateTime.now(); - final List array = await _channel.invokeMethod( - 'read', {'length': length, 'duration': duration}); + final List array = await _channel.invokeMethod('read', {'length': length, 'duration': duration}); yield array.map((e) => e! as int).toList(); var t = DateTime.now().difference(start).inMilliseconds; t = min(max(0, t), duration); @@ -66,6 +66,27 @@ class UsbDevice extends Device { } } + @override + Future write(Uint8List bytes) async { + await _channel.invokeMethod('write', {'bytes': bytes}); + } + + @override + Future setFeature(Uint8List bytes) async { + await _channel.invokeMethod('setFeature', {'bytes': bytes}); + } + + @override + Future getFeature(Uint8List bytes) async { + final List array = await _channel.invokeMethod('getFeature', {'bytes': bytes}); + final res = array.map((e) => e! as int).toList(); + final count = min(res.length, bytes.length - 1); + var pos = 1; // keep the first byte in place + for (var idx = 0; idx < count; idx++) { + bytes[pos++] = res[idx]; + } + } + @override Future close() async { isOpen = false; diff --git a/hid_android/pubspec.yaml b/hid_android/pubspec.yaml index c5a3064..7720499 100644 --- a/hid_android/pubspec.yaml +++ b/hid_android/pubspec.yaml @@ -1,21 +1,21 @@ name: hid_android description: Android implementation of the hid plugin. -version: 0.1.3 +version: 0.1.4 homepage: https://github.com/rustui/hid/tree/main/hid_android environment: - sdk: ">=2.15.0 <3.0.0" - flutter: ">=2.5.0" + sdk: '>=3.0.0 <4.0.0' + flutter: ">=3.10.0" dependencies: flutter: sdk: flutter - hid_platform_interface: ^0.0.8 + hid_platform_interface: ^0.0.9 dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^1.0.0 + flutter_lints: ^3.0.1 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/hid_linux/lib/generated_bindings.dart b/hid_linux/lib/generated_bindings.dart index 5a51438..d4e1169 100644 --- a/hid_linux/lib/generated_bindings.dart +++ b/hid_linux/lib/generated_bindings.dart @@ -5,17 +5,13 @@ import 'dart:ffi' as ffi; class Api { /// Holds the symbol lookup function. - final ffi.Pointer Function(String symbolName) - _lookup; + final ffi.Pointer Function(String symbolName) _lookup; /// The symbols are looked up in [dynamicLibrary]. Api(ffi.DynamicLibrary dynamicLibrary) : _lookup = dynamicLibrary.lookup; /// The symbols are looked up with [lookup]. - Api.fromLookup( - ffi.Pointer Function(String symbolName) - lookup) - : _lookup = lookup; + Api.fromLookup(ffi.Pointer Function(String symbolName) lookup) : _lookup = lookup; /// @brief Initialize the HIDAPI library. /// @@ -34,8 +30,7 @@ class Api { return _init(); } - late final _initPtr = - _lookup>('hid_init'); + late final _initPtr = _lookup>('hid_init'); late final _init = _initPtr.asFunction(); /// @brief Finalize the HIDAPI library. @@ -52,8 +47,7 @@ class Api { return _exit(); } - late final _exitPtr = - _lookup>('hid_exit'); + late final _exitPtr = _lookup>('hid_exit'); late final _exit = _exitPtr.asFunction(); /// @brief Enumerate the HID Devices. @@ -86,12 +80,8 @@ class Api { ); } - late final _enumeratePtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Uint16, ffi.Uint16)>>('hid_enumerate'); - late final _enumerate = _enumeratePtr - .asFunction Function(int, int)>(); + late final _enumeratePtr = _lookup Function(ffi.Uint16, ffi.Uint16)>>('hid_enumerate'); + late final _enumerate = _enumeratePtr.asFunction Function(int, int)>(); /// @brief Free an enumeration Linked List /// @@ -108,11 +98,8 @@ class Api { ); } - late final _free_enumerationPtr = _lookup< - ffi.NativeFunction)>>( - 'hid_free_enumeration'); - late final _free_enumeration = _free_enumerationPtr - .asFunction)>(); + late final _free_enumerationPtr = _lookup)>>('hid_free_enumeration'); + late final _free_enumeration = _free_enumerationPtr.asFunction)>(); /// @brief Open a HID device using a Vendor ID (VID), Product ID /// (PID) and optionally a serial number. @@ -143,12 +130,8 @@ class Api { ); } - late final _openPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Uint16, ffi.Uint16, ffi.Pointer)>>('hid_open'); - late final _open = _openPtr.asFunction< - ffi.Pointer Function(int, int, ffi.Pointer)>(); + late final _openPtr = _lookup Function(ffi.Uint16, ffi.Uint16, ffi.Pointer)>>('hid_open'); + late final _open = _openPtr.asFunction Function(int, int, ffi.Pointer)>(); /// @brief Open a HID device by its path name. /// @@ -172,12 +155,8 @@ class Api { ); } - late final _open_pathPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer)>>('hid_open_path'); - late final _open_path = _open_pathPtr - .asFunction Function(ffi.Pointer)>(); + late final _open_pathPtr = _lookup Function(ffi.Pointer)>>('hid_open_path'); + late final _open_path = _open_pathPtr.asFunction Function(ffi.Pointer)>(); /// @brief Write an Output report to a HID device. /// @@ -218,12 +197,8 @@ class Api { ); } - late final _writePtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_write'); - late final _write = _writePtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _writePtr = _lookup, ffi.Pointer, size_t)>>('hid_write'); + late final _write = _writePtr.asFunction, ffi.Pointer, int)>(); /// @brief Read an Input report from a HID device with timeout. /// @@ -259,13 +234,9 @@ class Api { ); } - late final _read_timeoutPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t, ffi.Int32)>>('hid_read_timeout'); - late final _read_timeout = _read_timeoutPtr.asFunction< - int Function( - ffi.Pointer, ffi.Pointer, int, int)>(); + late final _read_timeoutPtr = + _lookup, ffi.Pointer, size_t, ffi.Int32)>>('hid_read_timeout'); + late final _read_timeout = _read_timeoutPtr.asFunction, ffi.Pointer, int, int)>(); /// @brief Read an Input report from a HID device. /// @@ -298,12 +269,8 @@ class Api { ); } - late final _readPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_read'); - late final _read = _readPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _readPtr = _lookup, ffi.Pointer, size_t)>>('hid_read'); + late final _read = _readPtr.asFunction, ffi.Pointer, int)>(); /// @brief Set the device handle to be non-blocking. /// @@ -332,12 +299,8 @@ class Api { ); } - late final _set_nonblockingPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function( - ffi.Pointer, ffi.Int32)>>('hid_set_nonblocking'); - late final _set_nonblocking = _set_nonblockingPtr - .asFunction, int)>(); + late final _set_nonblockingPtr = _lookup, ffi.Int32)>>('hid_set_nonblocking'); + late final _set_nonblocking = _set_nonblockingPtr.asFunction, int)>(); /// @brief Send a Feature report to the device. /// @@ -378,12 +341,9 @@ class Api { ); } - late final _send_feature_reportPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_send_feature_report'); - late final _send_feature_report = _send_feature_reportPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _send_feature_reportPtr = + _lookup, ffi.Pointer, size_t)>>('hid_send_feature_report'); + late final _send_feature_report = _send_feature_reportPtr.asFunction, ffi.Pointer, int)>(); /// @brief Get a feature report from a HID device. /// @@ -421,12 +381,9 @@ class Api { ); } - late final _get_feature_reportPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_get_feature_report'); - late final _get_feature_report = _get_feature_reportPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _get_feature_reportPtr = + _lookup, ffi.Pointer, size_t)>>('hid_get_feature_report'); + late final _get_feature_report = _get_feature_reportPtr.asFunction, ffi.Pointer, int)>(); /// @brief Get a input report from a HID device. /// @@ -462,12 +419,9 @@ class Api { ); } - late final _get_input_reportPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_get_input_report'); - late final _get_input_report = _get_input_reportPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _get_input_reportPtr = + _lookup, ffi.Pointer, size_t)>>('hid_get_input_report'); + late final _get_input_report = _get_input_reportPtr.asFunction, ffi.Pointer, int)>(); /// @brief Close a HID device. /// @@ -483,11 +437,8 @@ class Api { ); } - late final _closePtr = - _lookup)>>( - 'hid_close'); - late final _close = - _closePtr.asFunction)>(); + late final _closePtr = _lookup)>>('hid_close'); + late final _close = _closePtr.asFunction)>(); /// @brief Get The Manufacturer String from a HID device. /// @@ -510,12 +461,9 @@ class Api { ); } - late final _get_manufacturer_stringPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_get_manufacturer_string'); - late final _get_manufacturer_string = _get_manufacturer_stringPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _get_manufacturer_stringPtr = + _lookup, ffi.Pointer, size_t)>>('hid_get_manufacturer_string'); + late final _get_manufacturer_string = _get_manufacturer_stringPtr.asFunction, ffi.Pointer, int)>(); /// @brief Get The Product String from a HID device. /// @@ -538,12 +486,9 @@ class Api { ); } - late final _get_product_stringPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_get_product_string'); - late final _get_product_string = _get_product_stringPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _get_product_stringPtr = + _lookup, ffi.Pointer, size_t)>>('hid_get_product_string'); + late final _get_product_string = _get_product_stringPtr.asFunction, ffi.Pointer, int)>(); /// @brief Get The Serial Number String from a HID device. /// @@ -566,13 +511,9 @@ class Api { ); } - late final _get_serial_number_stringPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_get_serial_number_string'); - late final _get_serial_number_string = - _get_serial_number_stringPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _get_serial_number_stringPtr = + _lookup, ffi.Pointer, size_t)>>('hid_get_serial_number_string'); + late final _get_serial_number_string = _get_serial_number_stringPtr.asFunction, ffi.Pointer, int)>(); /// @brief Get a string from a HID device, based on its string index. /// @@ -598,12 +539,9 @@ class Api { ); } - late final _get_indexed_stringPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Int32, - ffi.Pointer, size_t)>>('hid_get_indexed_string'); - late final _get_indexed_string = _get_indexed_stringPtr.asFunction< - int Function(ffi.Pointer, int, ffi.Pointer, int)>(); + late final _get_indexed_stringPtr = + _lookup, ffi.Int32, ffi.Pointer, size_t)>>('hid_get_indexed_string'); + late final _get_indexed_string = _get_indexed_stringPtr.asFunction, int, ffi.Pointer, int)>(); /// @brief Get a string describing the last error which occurred. /// @@ -631,11 +569,8 @@ class Api { ); } - late final _errorPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer)>>('hid_error'); - late final _error = _errorPtr - .asFunction Function(ffi.Pointer)>(); + late final _errorPtr = _lookup Function(ffi.Pointer)>>('hid_error'); + late final _error = _errorPtr.asFunction Function(ffi.Pointer)>(); /// @brief Get a runtime version of the library. /// @@ -647,11 +582,8 @@ class Api { return _version(); } - late final _versionPtr = - _lookup Function()>>( - 'hid_version'); - late final _version = - _versionPtr.asFunction Function()>(); + late final _versionPtr = _lookup Function()>>('hid_version'); + late final _version = _versionPtr.asFunction Function()>(); /// @brief Get a runtime version string of the library. /// @@ -663,14 +595,11 @@ class Api { return _version_str(); } - late final _version_strPtr = - _lookup Function()>>( - 'hid_version_str'); - late final _version_str = - _version_strPtr.asFunction Function()>(); + late final _version_strPtr = _lookup Function()>>('hid_version_str'); + late final _version_str = _version_strPtr.asFunction Function()>(); } -class hid_api_version extends ffi.Struct { +base class hid_api_version extends ffi.Struct { @ffi.Int32() external int major; @@ -681,10 +610,10 @@ class hid_api_version extends ffi.Struct { external int patch; } -class hid_device_ extends ffi.Opaque {} +base class hid_device_ extends ffi.Opaque {} /// hidapi info structure -class hid_device_info extends ffi.Struct { +base class hid_device_info extends ffi.Struct { /// Platform-specific device path external ffi.Pointer path; diff --git a/hid_linux/lib/hid_linux.dart b/hid_linux/lib/hid_linux.dart index 179194b..202ed8c 100644 --- a/hid_linux/lib/hid_linux.dart +++ b/hid_linux/lib/hid_linux.dart @@ -2,6 +2,7 @@ import 'dart:ffi'; import 'dart:typed_data'; import 'package:ffi/ffi.dart'; +import 'package:flutter/services.dart'; import 'package:hid_platform_interface/hid_platform_interface.dart'; import 'generated_bindings.dart'; @@ -14,9 +15,9 @@ class HidPluginLinux extends HidPlatform { } @override - Future> getDeviceList() async { + Future> getDeviceList({int? vendorId, int? productId}) async { List devices = []; - final pointer = _api.enumerate(0, 0); + final pointer = _api.enumerate(vendorId ?? 0, productId ?? 0); var current = pointer; while (current.address != nullptr.address) { final ref = current.ref; @@ -46,16 +47,11 @@ class UsbDevice extends Device { required String productName, required int usagePage, required int usage, - }) : super( - vendorId: vendorId, - productId: productId, - serialNumber: serialNumber, - productName: productName, - usagePage: usagePage, - usage: usage); + }) : super(vendorId: vendorId, productId: productId, serialNumber: serialNumber, productName: productName, usagePage: usagePage, usage: usage); @override Future open() async { + if (_raw != null) throw PlatformException(code: "rawDeviceAlreadyOpen", message: "Pointer to the USB device is not null, device already open"); final pointer = _api.open(vendorId, productId, serialNumber.toPointer()); if (pointer.address == nullptr.address) return false; final result = _api.set_nonblocking(pointer, 1); @@ -71,13 +67,14 @@ class UsbDevice extends Device { final raw = _raw; if (raw != null) { _api.close(raw); + _raw = null; } } @override Stream read(int length, int duration) async* { final raw = _raw; - if (raw == null) throw Exception(); + if (raw == null) throw PlatformException(code: "rawDeviceNotOpen", message: "Pointer to the USB device is null, did you forget to call open?"); final buf = calloc(length); var count = 0; while (isOpen) { @@ -95,14 +92,13 @@ class UsbDevice extends Device { @override Future write(Uint8List bytes) async { final raw = _raw; - if (raw == null) throw Exception(); + if (raw == null) throw PlatformException(code: "rawDeviceNotOpen", message: "Pointer to the USB device is null, did you forget to call open?"); final buf = calloc(bytes.lengthInBytes); final Uint8List _buf = buf.asTypedList(bytes.lengthInBytes); _buf.setRange(0, bytes.lengthInBytes, bytes); var offset = 0; while (isOpen && bytes.lengthInBytes - offset > 0) { - final count = - _api.write(raw, buf.elementAt(offset), bytes.lengthInBytes - offset); + final count = _api.write(raw, buf.elementAt(offset), bytes.lengthInBytes - offset); if (count == -1) { break; } else { @@ -111,6 +107,46 @@ class UsbDevice extends Device { } calloc.free(buf); } + + @override + Future setFeature(Uint8List bytes) async { + final raw = _raw; + if (raw == null) throw PlatformException(code: "rawDeviceNotOpen", message: "Pointer to the USB device is null, did you forget to call open?"); + final buf = calloc(bytes.lengthInBytes); + final Uint8List _buf = buf.asTypedList(bytes.lengthInBytes); + _buf.setRange(0, bytes.lengthInBytes, bytes); + var offset = 0; + while (isOpen && bytes.lengthInBytes - offset > 0) { + final count = _api.send_feature_report(raw, buf.elementAt(offset), bytes.lengthInBytes - offset); + if (count == -1) { + break; + } else { + offset += count; + } + } + calloc.free(buf); + } + + @override + Future getFeature(Uint8List bytes) async { + final raw = _raw; + if (raw == null) throw PlatformException(code: "rawDeviceNotOpen", message: "Pointer to the USB device is null, did you forget to call open?"); + final buf = calloc(bytes.lengthInBytes); + var count = 0; + var offset = 0; + while (isOpen && bytes.lengthInBytes - offset > 0) { + count = _api.get_feature_report(raw, buf, bytes.lengthInBytes); + if (count == -1) { + break; + } else if (count > 0) { + final res = buf.asTypedList(count); + for (var idx = 0; idx < count; idx++) { + bytes[offset++] = res[idx]; + } + } + } + calloc.free(buf); + } } extension PointerToString on Pointer { diff --git a/hid_linux/pubspec.yaml b/hid_linux/pubspec.yaml index a879f7e..d3ddb3f 100644 --- a/hid_linux/pubspec.yaml +++ b/hid_linux/pubspec.yaml @@ -1,22 +1,22 @@ name: hid_linux description: Linux implementation of the hid plugin. -version: 0.1.0 +version: 0.1.1 repository: https://github.com/rustui/hid/tree/main/hid_linux environment: - sdk: ">=2.15.0 <3.0.0" - flutter: ">=2.5.0" + sdk: '>=3.0.0 <4.0.0' + flutter: ">=3.10.0" dependencies: flutter: sdk: flutter - ffi: ^1.1.2 - hid_platform_interface: ^0.0.8 + hid_platform_interface: ^0.0.9 + ffi: ^2.1.0 dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^1.0.0 + flutter_lints: ^3.0.1 flutter: plugin: diff --git a/hid_macos/lib/generated_bindings.dart b/hid_macos/lib/generated_bindings.dart index 5a51438..d4e1169 100644 --- a/hid_macos/lib/generated_bindings.dart +++ b/hid_macos/lib/generated_bindings.dart @@ -5,17 +5,13 @@ import 'dart:ffi' as ffi; class Api { /// Holds the symbol lookup function. - final ffi.Pointer Function(String symbolName) - _lookup; + final ffi.Pointer Function(String symbolName) _lookup; /// The symbols are looked up in [dynamicLibrary]. Api(ffi.DynamicLibrary dynamicLibrary) : _lookup = dynamicLibrary.lookup; /// The symbols are looked up with [lookup]. - Api.fromLookup( - ffi.Pointer Function(String symbolName) - lookup) - : _lookup = lookup; + Api.fromLookup(ffi.Pointer Function(String symbolName) lookup) : _lookup = lookup; /// @brief Initialize the HIDAPI library. /// @@ -34,8 +30,7 @@ class Api { return _init(); } - late final _initPtr = - _lookup>('hid_init'); + late final _initPtr = _lookup>('hid_init'); late final _init = _initPtr.asFunction(); /// @brief Finalize the HIDAPI library. @@ -52,8 +47,7 @@ class Api { return _exit(); } - late final _exitPtr = - _lookup>('hid_exit'); + late final _exitPtr = _lookup>('hid_exit'); late final _exit = _exitPtr.asFunction(); /// @brief Enumerate the HID Devices. @@ -86,12 +80,8 @@ class Api { ); } - late final _enumeratePtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Uint16, ffi.Uint16)>>('hid_enumerate'); - late final _enumerate = _enumeratePtr - .asFunction Function(int, int)>(); + late final _enumeratePtr = _lookup Function(ffi.Uint16, ffi.Uint16)>>('hid_enumerate'); + late final _enumerate = _enumeratePtr.asFunction Function(int, int)>(); /// @brief Free an enumeration Linked List /// @@ -108,11 +98,8 @@ class Api { ); } - late final _free_enumerationPtr = _lookup< - ffi.NativeFunction)>>( - 'hid_free_enumeration'); - late final _free_enumeration = _free_enumerationPtr - .asFunction)>(); + late final _free_enumerationPtr = _lookup)>>('hid_free_enumeration'); + late final _free_enumeration = _free_enumerationPtr.asFunction)>(); /// @brief Open a HID device using a Vendor ID (VID), Product ID /// (PID) and optionally a serial number. @@ -143,12 +130,8 @@ class Api { ); } - late final _openPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Uint16, ffi.Uint16, ffi.Pointer)>>('hid_open'); - late final _open = _openPtr.asFunction< - ffi.Pointer Function(int, int, ffi.Pointer)>(); + late final _openPtr = _lookup Function(ffi.Uint16, ffi.Uint16, ffi.Pointer)>>('hid_open'); + late final _open = _openPtr.asFunction Function(int, int, ffi.Pointer)>(); /// @brief Open a HID device by its path name. /// @@ -172,12 +155,8 @@ class Api { ); } - late final _open_pathPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer)>>('hid_open_path'); - late final _open_path = _open_pathPtr - .asFunction Function(ffi.Pointer)>(); + late final _open_pathPtr = _lookup Function(ffi.Pointer)>>('hid_open_path'); + late final _open_path = _open_pathPtr.asFunction Function(ffi.Pointer)>(); /// @brief Write an Output report to a HID device. /// @@ -218,12 +197,8 @@ class Api { ); } - late final _writePtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_write'); - late final _write = _writePtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _writePtr = _lookup, ffi.Pointer, size_t)>>('hid_write'); + late final _write = _writePtr.asFunction, ffi.Pointer, int)>(); /// @brief Read an Input report from a HID device with timeout. /// @@ -259,13 +234,9 @@ class Api { ); } - late final _read_timeoutPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t, ffi.Int32)>>('hid_read_timeout'); - late final _read_timeout = _read_timeoutPtr.asFunction< - int Function( - ffi.Pointer, ffi.Pointer, int, int)>(); + late final _read_timeoutPtr = + _lookup, ffi.Pointer, size_t, ffi.Int32)>>('hid_read_timeout'); + late final _read_timeout = _read_timeoutPtr.asFunction, ffi.Pointer, int, int)>(); /// @brief Read an Input report from a HID device. /// @@ -298,12 +269,8 @@ class Api { ); } - late final _readPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_read'); - late final _read = _readPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _readPtr = _lookup, ffi.Pointer, size_t)>>('hid_read'); + late final _read = _readPtr.asFunction, ffi.Pointer, int)>(); /// @brief Set the device handle to be non-blocking. /// @@ -332,12 +299,8 @@ class Api { ); } - late final _set_nonblockingPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function( - ffi.Pointer, ffi.Int32)>>('hid_set_nonblocking'); - late final _set_nonblocking = _set_nonblockingPtr - .asFunction, int)>(); + late final _set_nonblockingPtr = _lookup, ffi.Int32)>>('hid_set_nonblocking'); + late final _set_nonblocking = _set_nonblockingPtr.asFunction, int)>(); /// @brief Send a Feature report to the device. /// @@ -378,12 +341,9 @@ class Api { ); } - late final _send_feature_reportPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_send_feature_report'); - late final _send_feature_report = _send_feature_reportPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _send_feature_reportPtr = + _lookup, ffi.Pointer, size_t)>>('hid_send_feature_report'); + late final _send_feature_report = _send_feature_reportPtr.asFunction, ffi.Pointer, int)>(); /// @brief Get a feature report from a HID device. /// @@ -421,12 +381,9 @@ class Api { ); } - late final _get_feature_reportPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_get_feature_report'); - late final _get_feature_report = _get_feature_reportPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _get_feature_reportPtr = + _lookup, ffi.Pointer, size_t)>>('hid_get_feature_report'); + late final _get_feature_report = _get_feature_reportPtr.asFunction, ffi.Pointer, int)>(); /// @brief Get a input report from a HID device. /// @@ -462,12 +419,9 @@ class Api { ); } - late final _get_input_reportPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_get_input_report'); - late final _get_input_report = _get_input_reportPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _get_input_reportPtr = + _lookup, ffi.Pointer, size_t)>>('hid_get_input_report'); + late final _get_input_report = _get_input_reportPtr.asFunction, ffi.Pointer, int)>(); /// @brief Close a HID device. /// @@ -483,11 +437,8 @@ class Api { ); } - late final _closePtr = - _lookup)>>( - 'hid_close'); - late final _close = - _closePtr.asFunction)>(); + late final _closePtr = _lookup)>>('hid_close'); + late final _close = _closePtr.asFunction)>(); /// @brief Get The Manufacturer String from a HID device. /// @@ -510,12 +461,9 @@ class Api { ); } - late final _get_manufacturer_stringPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_get_manufacturer_string'); - late final _get_manufacturer_string = _get_manufacturer_stringPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _get_manufacturer_stringPtr = + _lookup, ffi.Pointer, size_t)>>('hid_get_manufacturer_string'); + late final _get_manufacturer_string = _get_manufacturer_stringPtr.asFunction, ffi.Pointer, int)>(); /// @brief Get The Product String from a HID device. /// @@ -538,12 +486,9 @@ class Api { ); } - late final _get_product_stringPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_get_product_string'); - late final _get_product_string = _get_product_stringPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _get_product_stringPtr = + _lookup, ffi.Pointer, size_t)>>('hid_get_product_string'); + late final _get_product_string = _get_product_stringPtr.asFunction, ffi.Pointer, int)>(); /// @brief Get The Serial Number String from a HID device. /// @@ -566,13 +511,9 @@ class Api { ); } - late final _get_serial_number_stringPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_get_serial_number_string'); - late final _get_serial_number_string = - _get_serial_number_stringPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _get_serial_number_stringPtr = + _lookup, ffi.Pointer, size_t)>>('hid_get_serial_number_string'); + late final _get_serial_number_string = _get_serial_number_stringPtr.asFunction, ffi.Pointer, int)>(); /// @brief Get a string from a HID device, based on its string index. /// @@ -598,12 +539,9 @@ class Api { ); } - late final _get_indexed_stringPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Int32, - ffi.Pointer, size_t)>>('hid_get_indexed_string'); - late final _get_indexed_string = _get_indexed_stringPtr.asFunction< - int Function(ffi.Pointer, int, ffi.Pointer, int)>(); + late final _get_indexed_stringPtr = + _lookup, ffi.Int32, ffi.Pointer, size_t)>>('hid_get_indexed_string'); + late final _get_indexed_string = _get_indexed_stringPtr.asFunction, int, ffi.Pointer, int)>(); /// @brief Get a string describing the last error which occurred. /// @@ -631,11 +569,8 @@ class Api { ); } - late final _errorPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer)>>('hid_error'); - late final _error = _errorPtr - .asFunction Function(ffi.Pointer)>(); + late final _errorPtr = _lookup Function(ffi.Pointer)>>('hid_error'); + late final _error = _errorPtr.asFunction Function(ffi.Pointer)>(); /// @brief Get a runtime version of the library. /// @@ -647,11 +582,8 @@ class Api { return _version(); } - late final _versionPtr = - _lookup Function()>>( - 'hid_version'); - late final _version = - _versionPtr.asFunction Function()>(); + late final _versionPtr = _lookup Function()>>('hid_version'); + late final _version = _versionPtr.asFunction Function()>(); /// @brief Get a runtime version string of the library. /// @@ -663,14 +595,11 @@ class Api { return _version_str(); } - late final _version_strPtr = - _lookup Function()>>( - 'hid_version_str'); - late final _version_str = - _version_strPtr.asFunction Function()>(); + late final _version_strPtr = _lookup Function()>>('hid_version_str'); + late final _version_str = _version_strPtr.asFunction Function()>(); } -class hid_api_version extends ffi.Struct { +base class hid_api_version extends ffi.Struct { @ffi.Int32() external int major; @@ -681,10 +610,10 @@ class hid_api_version extends ffi.Struct { external int patch; } -class hid_device_ extends ffi.Opaque {} +base class hid_device_ extends ffi.Opaque {} /// hidapi info structure -class hid_device_info extends ffi.Struct { +base class hid_device_info extends ffi.Struct { /// Platform-specific device path external ffi.Pointer path; diff --git a/hid_macos/lib/hid_macos.dart b/hid_macos/lib/hid_macos.dart index 3b2b172..e7cd0c7 100644 --- a/hid_macos/lib/hid_macos.dart +++ b/hid_macos/lib/hid_macos.dart @@ -1,6 +1,7 @@ import 'dart:ffi'; import 'dart:typed_data'; +import 'package:flutter/services.dart'; import 'package:hid_platform_interface/hid_platform_interface.dart'; import 'package:ffi/ffi.dart'; import 'generated_bindings.dart'; @@ -13,9 +14,9 @@ class HidPluginMacOS extends HidPlatform { } @override - Future> getDeviceList() async { + Future> getDeviceList({int? vendorId, int? productId}) async { List devices = []; - final pointer = _api.enumerate(0, 0); + final pointer = _api.enumerate(vendorId ?? 0, productId ?? 0); var current = pointer; while (current.address != nullptr.address) { final ref = current.ref; @@ -45,16 +46,11 @@ class UsbDevice extends Device { required String productName, required int usagePage, required int usage, - }) : super( - vendorId: vendorId, - productId: productId, - serialNumber: serialNumber, - productName: productName, - usagePage: usagePage, - usage: usage); + }) : super(vendorId: vendorId, productId: productId, serialNumber: serialNumber, productName: productName, usagePage: usagePage, usage: usage); @override Future open() async { + if (_raw != null) throw PlatformException(code: "rawDeviceAlreadyOpen", message: "Pointer to the USB device is not null, device already open"); final pointer = _api.open(vendorId, productId, serialNumber.toPointer()); if (pointer.address == nullptr.address) return false; final result = _api.set_nonblocking(pointer, 1); @@ -70,13 +66,14 @@ class UsbDevice extends Device { final raw = _raw; if (raw != null) { _api.close(raw); + _raw = null; } } @override Stream read(int length, int duration) async* { final raw = _raw; - if (raw == null) throw Exception(); + if (raw == null) throw PlatformException(code: "rawDeviceNotOpen", message: "Pointer to the USB device is null, did you forget to call open?"); final buf = calloc(length); var count = 0; while (isOpen) { @@ -94,14 +91,13 @@ class UsbDevice extends Device { @override Future write(Uint8List bytes) async { final raw = _raw; - if (raw == null) throw Exception(); + if (raw == null) throw PlatformException(code: "rawDeviceNotOpen", message: "Pointer to the USB device is null, did you forget to call open?"); final buf = calloc(bytes.lengthInBytes); final Uint8List _buf = buf.asTypedList(bytes.lengthInBytes); _buf.setRange(0, bytes.lengthInBytes, bytes); var offset = 0; while (isOpen && bytes.lengthInBytes - offset > 0) { - final count = - _api.write(raw, buf.elementAt(offset), bytes.lengthInBytes - offset); + final count = _api.write(raw, buf.elementAt(offset), bytes.lengthInBytes - offset); if (count == -1) { break; } else { @@ -110,6 +106,46 @@ class UsbDevice extends Device { } calloc.free(buf); } + + @override + Future setFeature(Uint8List bytes) async { + final raw = _raw; + if (raw == null) throw PlatformException(code: "rawDeviceNotOpen", message: "Pointer to the USB device is null, did you forget to call open?"); + final buf = calloc(bytes.lengthInBytes); + final Uint8List _buf = buf.asTypedList(bytes.lengthInBytes); + _buf.setRange(0, bytes.lengthInBytes, bytes); + var offset = 0; + while (isOpen && bytes.lengthInBytes - offset > 0) { + final count = _api.send_feature_report(raw, buf.elementAt(offset), bytes.lengthInBytes - offset); + if (count == -1) { + break; + } else { + offset += count; + } + } + calloc.free(buf); + } + + @override + Future getFeature(Uint8List bytes) async { + final raw = _raw; + if (raw == null) throw PlatformException(code: "rawDeviceNotOpen", message: "Pointer to the USB device is null, did you forget to call open?"); + final buf = calloc(bytes.lengthInBytes); + var count = 0; + var offset = 0; + while (isOpen && bytes.lengthInBytes - offset > 0) { + count = _api.get_feature_report(raw, buf, bytes.lengthInBytes); + if (count == -1) { + break; + } else if (count > 0) { + final res = buf.asTypedList(count); + for (var idx = 0; idx < count; idx++) { + bytes[offset++] = res[idx]; + } + } + } + calloc.free(buf); + } } extension PointerToString on Pointer { diff --git a/hid_macos/pubspec.yaml b/hid_macos/pubspec.yaml index 8148cf0..b06a5ef 100644 --- a/hid_macos/pubspec.yaml +++ b/hid_macos/pubspec.yaml @@ -1,22 +1,22 @@ name: hid_macos description: macOS implementation of the hid plugin. -version: 0.1.1 +version: 0.1.2 repository: https://github.com/rustui/hid/tree/main/hid_macos environment: - sdk: ">=2.15.0 <3.0.0" - flutter: ">=2.5.0" + sdk: '>=3.0.0 <4.0.0' + flutter: ">=3.10.0" dependencies: flutter: sdk: flutter - ffi: ^1.1.2 - hid_platform_interface: ^0.0.8 + ffi: ^2.1.0 + hid_platform_interface: ^0.0.9 dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^1.0.0 + flutter_lints: ^3.0.1 flutter: plugin: diff --git a/hid_platform_interface/lib/device.dart b/hid_platform_interface/lib/device.dart index d941f88..a533990 100644 --- a/hid_platform_interface/lib/device.dart +++ b/hid_platform_interface/lib/device.dart @@ -30,4 +30,12 @@ abstract class Device { Future write(Uint8List bytes) { throw UnimplementedError(); } + + Future getFeature(Uint8List bytes) { + throw UnimplementedError(); + } + + Future setFeature(Uint8List bytes) { + throw UnimplementedError(); + } } diff --git a/hid_platform_interface/lib/hid_platform_interface.dart b/hid_platform_interface/lib/hid_platform_interface.dart index 0029b98..a5d9c84 100644 --- a/hid_platform_interface/lib/hid_platform_interface.dart +++ b/hid_platform_interface/lib/hid_platform_interface.dart @@ -16,7 +16,7 @@ abstract class HidPlatform extends PlatformInterface { _instance = instance; } - Future> getDeviceList() { + Future> getDeviceList({int? vendorId, int? productId}) { throw UnimplementedError(); } } diff --git a/hid_platform_interface/pubspec.yaml b/hid_platform_interface/pubspec.yaml index f31d630..ed9e19c 100644 --- a/hid_platform_interface/pubspec.yaml +++ b/hid_platform_interface/pubspec.yaml @@ -1,11 +1,11 @@ name: hid_platform_interface description: A common platform interface for the hid plugin. -version: 0.0.8 +version: 0.0.9 repository: https://github.com/rustui/hid/tree/main/hid_platform_interface environment: - sdk: ">=2.15.0 <3.0.0" - flutter: ">=2.5.0" + sdk: '>=3.0.0 <4.0.0' + flutter: ">=3.10.0" dependencies: flutter: @@ -15,7 +15,7 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^1.0.0 + flutter_lints: ^3.0.1 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/hid_windows/lib/generated_bindings.dart b/hid_windows/lib/generated_bindings.dart index e5d29b2..c0e1cf6 100644 --- a/hid_windows/lib/generated_bindings.dart +++ b/hid_windows/lib/generated_bindings.dart @@ -5,17 +5,13 @@ import 'dart:ffi' as ffi; class Api { /// Holds the symbol lookup function. - final ffi.Pointer Function(String symbolName) - _lookup; + final ffi.Pointer Function(String symbolName) _lookup; /// The symbols are looked up in [dynamicLibrary]. Api(ffi.DynamicLibrary dynamicLibrary) : _lookup = dynamicLibrary.lookup; /// The symbols are looked up with [lookup]. - Api.fromLookup( - ffi.Pointer Function(String symbolName) - lookup) - : _lookup = lookup; + Api.fromLookup(ffi.Pointer Function(String symbolName) lookup) : _lookup = lookup; /// @brief Initialize the HIDAPI library. /// @@ -34,8 +30,7 @@ class Api { return _init(); } - late final _initPtr = - _lookup>('hid_init'); + late final _initPtr = _lookup>('hid_init'); late final _init = _initPtr.asFunction(); /// @brief Finalize the HIDAPI library. @@ -52,8 +47,7 @@ class Api { return _exit(); } - late final _exitPtr = - _lookup>('hid_exit'); + late final _exitPtr = _lookup>('hid_exit'); late final _exit = _exitPtr.asFunction(); /// @brief Enumerate the HID Devices. @@ -86,12 +80,8 @@ class Api { ); } - late final _enumeratePtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Uint16, ffi.Uint16)>>('hid_enumerate'); - late final _enumerate = _enumeratePtr - .asFunction Function(int, int)>(); + late final _enumeratePtr = _lookup Function(ffi.Uint16, ffi.Uint16)>>('hid_enumerate'); + late final _enumerate = _enumeratePtr.asFunction Function(int, int)>(); /// @brief Free an enumeration Linked List /// @@ -108,11 +98,8 @@ class Api { ); } - late final _free_enumerationPtr = _lookup< - ffi.NativeFunction)>>( - 'hid_free_enumeration'); - late final _free_enumeration = _free_enumerationPtr - .asFunction)>(); + late final _free_enumerationPtr = _lookup)>>('hid_free_enumeration'); + late final _free_enumeration = _free_enumerationPtr.asFunction)>(); /// @brief Open a HID device using a Vendor ID (VID), Product ID /// (PID) and optionally a serial number. @@ -143,12 +130,8 @@ class Api { ); } - late final _openPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Uint16, ffi.Uint16, ffi.Pointer)>>('hid_open'); - late final _open = _openPtr.asFunction< - ffi.Pointer Function(int, int, ffi.Pointer)>(); + late final _openPtr = _lookup Function(ffi.Uint16, ffi.Uint16, ffi.Pointer)>>('hid_open'); + late final _open = _openPtr.asFunction Function(int, int, ffi.Pointer)>(); /// @brief Open a HID device by its path name. /// @@ -172,12 +155,8 @@ class Api { ); } - late final _open_pathPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Pointer)>>('hid_open_path'); - late final _open_path = _open_pathPtr - .asFunction Function(ffi.Pointer)>(); + late final _open_pathPtr = _lookup Function(ffi.Pointer)>>('hid_open_path'); + late final _open_path = _open_pathPtr.asFunction Function(ffi.Pointer)>(); /// @brief Write an Output report to a HID device. /// @@ -218,12 +197,8 @@ class Api { ); } - late final _writePtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_write'); - late final _write = _writePtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _writePtr = _lookup, ffi.Pointer, size_t)>>('hid_write'); + late final _write = _writePtr.asFunction, ffi.Pointer, int)>(); /// @brief Read an Input report from a HID device with timeout. /// @@ -259,13 +234,9 @@ class Api { ); } - late final _read_timeoutPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t, ffi.Int32)>>('hid_read_timeout'); - late final _read_timeout = _read_timeoutPtr.asFunction< - int Function( - ffi.Pointer, ffi.Pointer, int, int)>(); + late final _read_timeoutPtr = + _lookup, ffi.Pointer, size_t, ffi.Int32)>>('hid_read_timeout'); + late final _read_timeout = _read_timeoutPtr.asFunction, ffi.Pointer, int, int)>(); /// @brief Read an Input report from a HID device. /// @@ -298,12 +269,8 @@ class Api { ); } - late final _readPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_read'); - late final _read = _readPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _readPtr = _lookup, ffi.Pointer, size_t)>>('hid_read'); + late final _read = _readPtr.asFunction, ffi.Pointer, int)>(); /// @brief Set the device handle to be non-blocking. /// @@ -332,12 +299,8 @@ class Api { ); } - late final _set_nonblockingPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function( - ffi.Pointer, ffi.Int32)>>('hid_set_nonblocking'); - late final _set_nonblocking = _set_nonblockingPtr - .asFunction, int)>(); + late final _set_nonblockingPtr = _lookup, ffi.Int32)>>('hid_set_nonblocking'); + late final _set_nonblocking = _set_nonblockingPtr.asFunction, int)>(); /// @brief Send a Feature report to the device. /// @@ -378,12 +341,9 @@ class Api { ); } - late final _send_feature_reportPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_send_feature_report'); - late final _send_feature_report = _send_feature_reportPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _send_feature_reportPtr = + _lookup, ffi.Pointer, size_t)>>('hid_send_feature_report'); + late final _send_feature_report = _send_feature_reportPtr.asFunction, ffi.Pointer, int)>(); /// @brief Get a feature report from a HID device. /// @@ -421,12 +381,9 @@ class Api { ); } - late final _get_feature_reportPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_get_feature_report'); - late final _get_feature_report = _get_feature_reportPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _get_feature_reportPtr = + _lookup, ffi.Pointer, size_t)>>('hid_get_feature_report'); + late final _get_feature_report = _get_feature_reportPtr.asFunction, ffi.Pointer, int)>(); /// @brief Get a input report from a HID device. /// @@ -462,12 +419,9 @@ class Api { ); } - late final _get_input_reportPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_get_input_report'); - late final _get_input_report = _get_input_reportPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _get_input_reportPtr = + _lookup, ffi.Pointer, size_t)>>('hid_get_input_report'); + late final _get_input_report = _get_input_reportPtr.asFunction, ffi.Pointer, int)>(); /// @brief Close a HID device. /// @@ -483,11 +437,8 @@ class Api { ); } - late final _closePtr = - _lookup)>>( - 'hid_close'); - late final _close = - _closePtr.asFunction)>(); + late final _closePtr = _lookup)>>('hid_close'); + late final _close = _closePtr.asFunction)>(); /// @brief Get The Manufacturer String from a HID device. /// @@ -510,12 +461,9 @@ class Api { ); } - late final _get_manufacturer_stringPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_get_manufacturer_string'); - late final _get_manufacturer_string = _get_manufacturer_stringPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _get_manufacturer_stringPtr = + _lookup, ffi.Pointer, size_t)>>('hid_get_manufacturer_string'); + late final _get_manufacturer_string = _get_manufacturer_stringPtr.asFunction, ffi.Pointer, int)>(); /// @brief Get The Product String from a HID device. /// @@ -538,12 +486,9 @@ class Api { ); } - late final _get_product_stringPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_get_product_string'); - late final _get_product_string = _get_product_stringPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _get_product_stringPtr = + _lookup, ffi.Pointer, size_t)>>('hid_get_product_string'); + late final _get_product_string = _get_product_stringPtr.asFunction, ffi.Pointer, int)>(); /// @brief Get The Serial Number String from a HID device. /// @@ -566,13 +511,9 @@ class Api { ); } - late final _get_serial_number_stringPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Pointer, - size_t)>>('hid_get_serial_number_string'); - late final _get_serial_number_string = - _get_serial_number_stringPtr.asFunction< - int Function(ffi.Pointer, ffi.Pointer, int)>(); + late final _get_serial_number_stringPtr = + _lookup, ffi.Pointer, size_t)>>('hid_get_serial_number_string'); + late final _get_serial_number_string = _get_serial_number_stringPtr.asFunction, ffi.Pointer, int)>(); /// @brief Get a string from a HID device, based on its string index. /// @@ -598,12 +539,9 @@ class Api { ); } - late final _get_indexed_stringPtr = _lookup< - ffi.NativeFunction< - ffi.Int32 Function(ffi.Pointer, ffi.Int32, - ffi.Pointer, size_t)>>('hid_get_indexed_string'); - late final _get_indexed_string = _get_indexed_stringPtr.asFunction< - int Function(ffi.Pointer, int, ffi.Pointer, int)>(); + late final _get_indexed_stringPtr = + _lookup, ffi.Int32, ffi.Pointer, size_t)>>('hid_get_indexed_string'); + late final _get_indexed_string = _get_indexed_stringPtr.asFunction, int, ffi.Pointer, int)>(); /// @brief Get a string describing the last error which occurred. /// @@ -631,11 +569,8 @@ class Api { ); } - late final _errorPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function(ffi.Pointer)>>('hid_error'); - late final _error = _errorPtr - .asFunction Function(ffi.Pointer)>(); + late final _errorPtr = _lookup Function(ffi.Pointer)>>('hid_error'); + late final _error = _errorPtr.asFunction Function(ffi.Pointer)>(); /// @brief Get a runtime version of the library. /// @@ -647,11 +582,8 @@ class Api { return _version(); } - late final _versionPtr = - _lookup Function()>>( - 'hid_version'); - late final _version = - _versionPtr.asFunction Function()>(); + late final _versionPtr = _lookup Function()>>('hid_version'); + late final _version = _versionPtr.asFunction Function()>(); /// @brief Get a runtime version string of the library. /// @@ -663,14 +595,11 @@ class Api { return _version_str(); } - late final _version_strPtr = - _lookup Function()>>( - 'hid_version_str'); - late final _version_str = - _version_strPtr.asFunction Function()>(); + late final _version_strPtr = _lookup Function()>>('hid_version_str'); + late final _version_str = _version_strPtr.asFunction Function()>(); } -class hid_api_version extends ffi.Struct { +base class hid_api_version extends ffi.Struct { @ffi.Int32() external int major; @@ -681,10 +610,10 @@ class hid_api_version extends ffi.Struct { external int patch; } -class hid_device_ extends ffi.Opaque {} +base class hid_device_ extends ffi.Opaque {} /// hidapi info structure -class hid_device_info extends ffi.Struct { +base class hid_device_info extends ffi.Struct { /// Platform-specific device path external ffi.Pointer path; diff --git a/hid_windows/lib/hid_windows.dart b/hid_windows/lib/hid_windows.dart index 8a9497d..848f042 100644 --- a/hid_windows/lib/hid_windows.dart +++ b/hid_windows/lib/hid_windows.dart @@ -1,6 +1,7 @@ import 'dart:ffi'; import 'dart:typed_data'; +import 'package:flutter/services.dart'; import 'package:hid_platform_interface/hid_platform_interface.dart'; import 'package:ffi/ffi.dart'; import 'generated_bindings.dart'; @@ -13,9 +14,9 @@ class HidPluginWindows extends HidPlatform { } @override - Future> getDeviceList() async { + Future> getDeviceList({int? vendorId, int? productId}) async { List devices = []; - final pointer = _api.enumerate(0, 0); + final pointer = _api.enumerate(vendorId ?? 0, productId ?? 0); var current = pointer; while (current.address != nullptr.address) { final ref = current.ref; @@ -45,16 +46,11 @@ class UsbDevice extends Device { required String productName, required int usagePage, required int usage, - }) : super( - vendorId: vendorId, - productId: productId, - serialNumber: serialNumber, - productName: productName, - usagePage: usagePage, - usage: usage); + }) : super(vendorId: vendorId, productId: productId, serialNumber: serialNumber, productName: productName, usagePage: usagePage, usage: usage); @override Future open() async { + if (_raw != null) throw PlatformException(code: "rawDeviceAlreadyOpen", message: "Pointer to the USB device is not null, device already open"); final pointer = _api.open(vendorId, productId, serialNumber.toPointer()); if (pointer.address == nullptr.address) return false; final result = _api.set_nonblocking(pointer, 1); @@ -70,13 +66,14 @@ class UsbDevice extends Device { final raw = _raw; if (raw != null) { _api.close(raw); + _raw = null; } } @override Stream read(int length, int duration) async* { final raw = _raw; - if (raw == null) throw Exception(); + if (raw == null) throw PlatformException(code: "rawDeviceNotOpen", message: "Pointer to the USB device is null, did you forget to call open?"); final buf = calloc(length); var count = 0; while (isOpen) { @@ -94,14 +91,13 @@ class UsbDevice extends Device { @override Future write(Uint8List bytes) async { final raw = _raw; - if (raw == null) throw Exception(); + if (raw == null) throw PlatformException(code: "rawDeviceNotOpen", message: "Pointer to the USB device is null, did you forget to call open?"); final buf = calloc(bytes.lengthInBytes); final Uint8List _buf = buf.asTypedList(bytes.lengthInBytes); _buf.setRange(0, bytes.lengthInBytes, bytes); var offset = 0; while (isOpen && bytes.lengthInBytes - offset > 0) { - final count = - _api.write(raw, buf.elementAt(offset), bytes.lengthInBytes - offset); + final count = _api.write(raw, buf.elementAt(offset), bytes.lengthInBytes - offset); if (count == -1) { break; } else { @@ -110,6 +106,46 @@ class UsbDevice extends Device { } calloc.free(buf); } + + @override + Future setFeature(Uint8List bytes) async { + final raw = _raw; + if (raw == null) throw PlatformException(code: "rawDeviceNotOpen", message: "Pointer to the USB device is null, did you forget to call open?"); + final buf = calloc(bytes.lengthInBytes); + final Uint8List _buf = buf.asTypedList(bytes.lengthInBytes); + _buf.setRange(0, bytes.lengthInBytes, bytes); + var offset = 0; + while (isOpen && bytes.lengthInBytes - offset > 0) { + final count = _api.send_feature_report(raw, buf.elementAt(offset), bytes.lengthInBytes - offset); + if (count == -1) { + break; + } else { + offset += count; + } + } + calloc.free(buf); + } + + @override + Future getFeature(Uint8List bytes) async { + final raw = _raw; + if (raw == null) throw PlatformException(code: "rawDeviceNotOpen", message: "Pointer to the USB device is null, did you forget to call open?"); + final buf = calloc(bytes.lengthInBytes); + var count = 0; + var offset = 0; + while (isOpen && bytes.lengthInBytes - offset > 0) { + count = _api.get_feature_report(raw, buf, bytes.lengthInBytes); + if (count == -1) { + break; + } else if (count > 0) { + final res = buf.asTypedList(count); + for (var idx = 0; idx < count; idx++) { + bytes[offset++] = res[idx]; + } + } + } + calloc.free(buf); + } } extension PointerToString on Pointer { diff --git a/hid_windows/pubspec.yaml b/hid_windows/pubspec.yaml index 4e177ed..cc1f4f4 100644 --- a/hid_windows/pubspec.yaml +++ b/hid_windows/pubspec.yaml @@ -1,22 +1,22 @@ name: hid_windows description: Windows implementation of the hid plugin. -version: 0.1.0 +version: 0.1.1 repository: https://github.com/rustui/hid/tree/main/hid_windows environment: - sdk: ">=2.15.0 <3.0.0" - flutter: ">=2.5.0" + sdk: '>=3.0.0 <4.0.0' + flutter: ">=3.10.0" dependencies: - ffi: ^1.1.2 + ffi: ^2.1.0 flutter: sdk: flutter - hid_platform_interface: ^0.0.8 + hid_platform_interface: ^0.0.9 dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^1.0.0 + flutter_lints: ^3.0.1 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec