Skip to content

Commit

Permalink
Add device monitoring for hidapi backend, based on libusb/hidapi#674
Browse files Browse the repository at this point in the history
  • Loading branch information
Nemo157 committed May 19, 2024
1 parent b283459 commit 82d2456
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 1 deletion.
24 changes: 24 additions & 0 deletions src/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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")]
Expand Down Expand Up @@ -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
Expand Down
71 changes: 70 additions & 1 deletion src/hidapi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -39,6 +39,75 @@ impl HidApiBackend {
Ok(device_vector)
}

pub fn monitor_hid_device_info() -> HidResult<impl Iterator<Item = Event>> {
type Sender = std::sync::mpsc::Sender::<(ffi::HidHotplugEvent, HidResult<DeviceInfo>)>;
type Receiver = std::sync::mpsc::Receiver::<(ffi::HidHotplugEvent, HidResult<DeviceInfo>)>;

struct Iter {
handle: ffi::HidHotplugCallbackHandle,
tx: *mut Sender,
rx: Receiver,
}

impl Iterator for Iter {
type Item = Event;

fn next(&mut self) -> Option<Self::Item> {
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::<Sender>();
let _ = unsafe { &*tx }.send((event, unsafe { conv_hid_device_info(info) }));
0
}

let user_data = tx.cast::<libc::c_void>();
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<HidDevice> {
let device = unsafe { ffi::hid_open(vid, pid, std::ptr::null()) };

Expand Down

0 comments on commit 82d2456

Please sign in to comment.