async_hid/lib.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
#![doc = include_str!("../README.md")]
mod backend;
mod error;
use std::fmt::{Debug, Formatter};
use std::future::Future;
use futures_core::Stream;
use crate::backend::{BackendDevice, BackendDeviceId};
pub use crate::error::{ErrorSource, HidError, HidResult};
/// A struct containing basic information about a device
///
/// This struct can be obtained by calling [DeviceInfo::enumerate] and upgraded into a usable [Device] by calling [DeviceInfo::open].
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct DeviceInfo {
/// OS specific identifier
pub id: DeviceId,
/// The human readable name
pub name: String,
/// The HID product id assigned to this device
pub product_id: u16,
/// The HID vendor id of the device's manufacturer (i.e Logitech = 0x46D)
pub vendor_id: u16,
/// The HID usage id
pub usage_id: u16,
/// The HID usage page
pub usage_page: u16
}
impl DeviceInfo {
/// Enumerates all **accessible** HID devices
///
/// If this library fails to retrieve the [DeviceInfo] of a device it will be automatically excluded.
/// Register a `log` compatible logger at `trace` level for more information about the discarded devices.
pub fn enumerate() -> impl Future<Output = HidResult<impl Stream<Item = DeviceInfo> + Unpin>> {
backend::enumerate()
}
/// Opens the associated device in the requested [AccessMode]
pub async fn open(&self, mode: AccessMode) -> HidResult<Device> {
let dev = backend::open(&self.id.0, mode).await?;
Ok(Device {
inner: dev,
info: self.clone(),
mode
})
}
/// Convenience method for easily finding a specific device
pub fn matches(&self, usage_page: u16, usage_id: u16, vendor_id: u16, product_id: u16) -> bool {
self.usage_page == usage_page && self.usage_id == usage_id && self.vendor_id == vendor_id && self.product_id == product_id
}
}
/// A struct representing an opened device
///
/// Dropping this struct will close the associated device
pub struct Device {
inner: BackendDevice,
info: DeviceInfo,
mode: AccessMode
}
impl Device {
/// Read a input report from this device
pub fn read_input_report<'a>(&'a self, buf: &'a mut [u8]) -> impl Future<Output = HidResult<usize>> + 'a {
debug_assert!(self.mode.readable());
self.inner.read_input_report(buf)
}
/// Write an output report to this device
pub fn write_output_report<'a>(&'a self, buf: &'a [u8]) -> impl Future<Output = HidResult<()>> + 'a {
debug_assert!(self.mode.writeable());
self.inner.write_output_report(buf)
}
/// Retrieves the [DeviceInfo] associated with this device
pub fn info(&self) -> &DeviceInfo {
&self.info
}
}
/// An opaque struct that wraps the OS specific identifier of a device
#[derive(Clone, Eq, PartialEq)]
#[repr(transparent)]
pub struct DeviceId(BackendDeviceId);
impl From<BackendDeviceId> for DeviceId {
fn from(value: BackendDeviceId) -> Self {
Self(value)
}
}
impl Debug for DeviceId {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self.0)
}
}
/// An enum that controls how a device will be opened
///
/// This mainly influences the flags passed to the underlying OS api,
/// but is also used to avoid initializing read specific data structures for write-only devices.
///
/// In general `Read` means shared access and `Write` or `ReadWrite` means exclusive access
#[derive(Debug, Default, Copy, Clone, Eq, PartialEq)]
pub enum AccessMode {
Read,
Write,
#[default]
ReadWrite
}
impl AccessMode {
pub fn readable(self) -> bool {
matches!(self, Self::Read | Self::ReadWrite)
}
pub fn writeable(self) -> bool {
matches!(self, Self::Write | Self::ReadWrite)
}
}