From 82d2456287421ffe68b29b2050ed00e0b6a305e8 Mon Sep 17 00:00:00 2001 From: Wim Looman Date: Sun, 19 May 2024 11:54:21 +0200 Subject: [PATCH] Add device monitoring for hidapi backend, based on https://github.com/libusb/hidapi/pull/674 --- src/ffi.rs | 24 +++++++++++++++++ src/hidapi.rs | 71 ++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/src/ffi.rs b/src/ffi.rs index 7840ddf..fd51379 100644 --- a/src/ffi.rs +++ b/src/ffi.rs @@ -27,6 +27,17 @@ pub struct HidDeviceInfo { pub bus_type: HidBusType, } +pub type HidHotplugCallbackHandle = c_int; + +pub type HidHotplugEvent = c_int; +pub const HID_API_HOTPLUG_EVENT_DEVICE_ARRIVED: HidHotplugEvent = 1 << 0; +pub const HID_API_HOTPLUG_EVENT_DEVICE_LEFT: HidHotplugEvent = 1 << 1; + +pub type HidHotplugFlag = c_int; +pub const HID_API_HOTPLUG_ENUMERATE: HidHotplugFlag = 1 << 0; + +type HidHotplugCallbackFn = extern "C" fn(HidHotplugCallbackHandle, *mut HidDeviceInfo, HidHotplugEvent, *mut c_void) -> c_int; + #[allow(dead_code)] extern "C" { #[cfg_attr(target_os = "openbsd", link_name = "hidapi_hid_init")] @@ -92,6 +103,19 @@ extern "C" { buf_size: size_t, ) -> c_int; pub fn hid_error(device: *mut HidDevice) -> *const wchar_t; + + pub fn hid_hotplug_register_callback( + vendor_id: c_ushort, + product_id: c_ushort, + events: HidHotplugEvent, + flags: HidHotplugFlag, + callback: HidHotplugCallbackFn, + user_data: *mut c_void, + callback_handle: *mut HidHotplugCallbackHandle, + ) -> c_int; + pub fn hid_hotplug_deregister_callback( + callback_handle: HidHotplugCallbackHandle, + ) -> c_int; } // For documentation look at the corresponding C header file hidapi_darwin.h diff --git a/src/hidapi.rs b/src/hidapi.rs index 701574e..5b93d6c 100644 --- a/src/hidapi.rs +++ b/src/hidapi.rs @@ -7,7 +7,7 @@ use std::{ use libc::{c_int, size_t, wchar_t}; -use crate::{ffi, DeviceInfo, HidDeviceBackendBase, HidError, HidResult, WcharString}; +use crate::{ffi, DeviceInfo, Event, HidDeviceBackendBase, HidError, HidResult, WcharString}; #[cfg(target_os = "macos")] mod macos; @@ -39,6 +39,75 @@ impl HidApiBackend { Ok(device_vector) } + pub fn monitor_hid_device_info() -> HidResult> { + type Sender = std::sync::mpsc::Sender::<(ffi::HidHotplugEvent, HidResult)>; + type Receiver = std::sync::mpsc::Receiver::<(ffi::HidHotplugEvent, HidResult)>; + + struct Iter { + handle: ffi::HidHotplugCallbackHandle, + tx: *mut Sender, + rx: Receiver, + } + + impl Iterator for Iter { + type Item = Event; + + fn next(&mut self) -> Option { + loop { + break Some(match self.rx.recv().expect("the sender shouldn't be dropped") { + (ffi::HID_API_HOTPLUG_EVENT_DEVICE_ARRIVED, Ok(info)) => Event::Add(info), + _ => continue, + }) + } + } + } + + impl Drop for Iter { + fn drop(&mut self) { + unsafe { ffi::hid_hotplug_deregister_callback(self.handle) }; + unsafe { drop(Box::from_raw(self.tx)) }; + } + } + + let mut handle: ffi::HidHotplugCallbackHandle = 0; + + let (tx, mut rx) = std::sync::mpsc::channel(); + let tx = Box::into_raw(Box::new(tx)); + + let vendor_id = 0; + let product_id = 0; + let events = ffi::HID_API_HOTPLUG_EVENT_DEVICE_ARRIVED; + let flags = 0; + + extern "C" fn callback(_handle: ffi::HidHotplugCallbackHandle, info: *mut ffi::HidDeviceInfo, event: ffi::HidHotplugEvent, user_data: *mut libc::c_void) -> libc::c_int { + let tx = user_data.cast::(); + let _ = unsafe { &*tx }.send((event, unsafe { conv_hid_device_info(info) })); + 0 + } + + let user_data = tx.cast::(); + let handle_ptr = std::ptr::addr_of_mut!(handle); + + let result = unsafe { ffi::hid_hotplug_register_callback( + vendor_id, + product_id, + events, + flags, + callback, + user_data, + handle_ptr, + ) }; + + if result == 0 { + Ok(Iter { handle, tx, rx }) + } else { + match Self::check_error() { + Ok(err) => Err(err), + Err(e) => Err(e), + } + } + } + pub fn open(vid: u16, pid: u16) -> HidResult { let device = unsafe { ffi::hid_open(vid, pid, std::ptr::null()) };