Skip to content

Commit 82d2456

Browse files
committed
Add device monitoring for hidapi backend, based on libusb/hidapi#674
1 parent b283459 commit 82d2456

File tree

2 files changed

+94
-1
lines changed

2 files changed

+94
-1
lines changed

src/ffi.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,17 @@ pub struct HidDeviceInfo {
2727
pub bus_type: HidBusType,
2828
}
2929

30+
pub type HidHotplugCallbackHandle = c_int;
31+
32+
pub type HidHotplugEvent = c_int;
33+
pub const HID_API_HOTPLUG_EVENT_DEVICE_ARRIVED: HidHotplugEvent = 1 << 0;
34+
pub const HID_API_HOTPLUG_EVENT_DEVICE_LEFT: HidHotplugEvent = 1 << 1;
35+
36+
pub type HidHotplugFlag = c_int;
37+
pub const HID_API_HOTPLUG_ENUMERATE: HidHotplugFlag = 1 << 0;
38+
39+
type HidHotplugCallbackFn = extern "C" fn(HidHotplugCallbackHandle, *mut HidDeviceInfo, HidHotplugEvent, *mut c_void) -> c_int;
40+
3041
#[allow(dead_code)]
3142
extern "C" {
3243
#[cfg_attr(target_os = "openbsd", link_name = "hidapi_hid_init")]
@@ -92,6 +103,19 @@ extern "C" {
92103
buf_size: size_t,
93104
) -> c_int;
94105
pub fn hid_error(device: *mut HidDevice) -> *const wchar_t;
106+
107+
pub fn hid_hotplug_register_callback(
108+
vendor_id: c_ushort,
109+
product_id: c_ushort,
110+
events: HidHotplugEvent,
111+
flags: HidHotplugFlag,
112+
callback: HidHotplugCallbackFn,
113+
user_data: *mut c_void,
114+
callback_handle: *mut HidHotplugCallbackHandle,
115+
) -> c_int;
116+
pub fn hid_hotplug_deregister_callback(
117+
callback_handle: HidHotplugCallbackHandle,
118+
) -> c_int;
95119
}
96120

97121
// For documentation look at the corresponding C header file hidapi_darwin.h

src/hidapi.rs

Lines changed: 70 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::{
77

88
use libc::{c_int, size_t, wchar_t};
99

10-
use crate::{ffi, DeviceInfo, HidDeviceBackendBase, HidError, HidResult, WcharString};
10+
use crate::{ffi, DeviceInfo, Event, HidDeviceBackendBase, HidError, HidResult, WcharString};
1111

1212
#[cfg(target_os = "macos")]
1313
mod macos;
@@ -39,6 +39,75 @@ impl HidApiBackend {
3939
Ok(device_vector)
4040
}
4141

42+
pub fn monitor_hid_device_info() -> HidResult<impl Iterator<Item = Event>> {
43+
type Sender = std::sync::mpsc::Sender::<(ffi::HidHotplugEvent, HidResult<DeviceInfo>)>;
44+
type Receiver = std::sync::mpsc::Receiver::<(ffi::HidHotplugEvent, HidResult<DeviceInfo>)>;
45+
46+
struct Iter {
47+
handle: ffi::HidHotplugCallbackHandle,
48+
tx: *mut Sender,
49+
rx: Receiver,
50+
}
51+
52+
impl Iterator for Iter {
53+
type Item = Event;
54+
55+
fn next(&mut self) -> Option<Self::Item> {
56+
loop {
57+
break Some(match self.rx.recv().expect("the sender shouldn't be dropped") {
58+
(ffi::HID_API_HOTPLUG_EVENT_DEVICE_ARRIVED, Ok(info)) => Event::Add(info),
59+
_ => continue,
60+
})
61+
}
62+
}
63+
}
64+
65+
impl Drop for Iter {
66+
fn drop(&mut self) {
67+
unsafe { ffi::hid_hotplug_deregister_callback(self.handle) };
68+
unsafe { drop(Box::from_raw(self.tx)) };
69+
}
70+
}
71+
72+
let mut handle: ffi::HidHotplugCallbackHandle = 0;
73+
74+
let (tx, mut rx) = std::sync::mpsc::channel();
75+
let tx = Box::into_raw(Box::new(tx));
76+
77+
let vendor_id = 0;
78+
let product_id = 0;
79+
let events = ffi::HID_API_HOTPLUG_EVENT_DEVICE_ARRIVED;
80+
let flags = 0;
81+
82+
extern "C" fn callback(_handle: ffi::HidHotplugCallbackHandle, info: *mut ffi::HidDeviceInfo, event: ffi::HidHotplugEvent, user_data: *mut libc::c_void) -> libc::c_int {
83+
let tx = user_data.cast::<Sender>();
84+
let _ = unsafe { &*tx }.send((event, unsafe { conv_hid_device_info(info) }));
85+
0
86+
}
87+
88+
let user_data = tx.cast::<libc::c_void>();
89+
let handle_ptr = std::ptr::addr_of_mut!(handle);
90+
91+
let result = unsafe { ffi::hid_hotplug_register_callback(
92+
vendor_id,
93+
product_id,
94+
events,
95+
flags,
96+
callback,
97+
user_data,
98+
handle_ptr,
99+
) };
100+
101+
if result == 0 {
102+
Ok(Iter { handle, tx, rx })
103+
} else {
104+
match Self::check_error() {
105+
Ok(err) => Err(err),
106+
Err(e) => Err(e),
107+
}
108+
}
109+
}
110+
42111
pub fn open(vid: u16, pid: u16) -> HidResult<HidDevice> {
43112
let device = unsafe { ffi::hid_open(vid, pid, std::ptr::null()) };
44113

0 commit comments

Comments
 (0)