async-hid 0.3.0

A async library for interacting with HID devices
Documentation
use crate::backend::win32::check_error;
use std::ffi::c_void;
use windows::core::PCWSTR;
use windows::Win32::Devices::HumanInterfaceDevice::{HidD_FreePreparsedData, HidD_GetAttributes, HidD_GetPreparsedData, HidD_GetProductString, HidD_GetSerialNumberString, HidP_GetCaps, HIDD_ATTRIBUTES, HIDP_CAPS, HIDP_STATUS_SUCCESS, PHIDP_PREPARSED_DATA};
use windows::Win32::Foundation::{CloseHandle, HANDLE};
use windows::Win32::Storage::FileSystem::{CreateFileW, FILE_FLAG_OVERLAPPED, FILE_SHARE_NONE, FILE_SHARE_READ, FILE_SHARE_WRITE, OPEN_EXISTING};
use crate::{ensure, HidResult};

#[derive(Debug, Eq, PartialEq)]
#[repr(transparent)]
pub struct Device(HANDLE);

unsafe impl Send for Device {}
unsafe impl Sync for Device {}

impl Device {

    pub fn open(path: PCWSTR, read: bool, write: bool) -> HidResult<Device> {
        let handle = unsafe {
            CreateFileW(
                path,
                match (read, write) {
                    (true, false) => FILE_SHARE_READ,
                    (false, true) => FILE_SHARE_WRITE,
                    (true, true) => FILE_SHARE_READ | FILE_SHARE_WRITE,
                    (false, false) => FILE_SHARE_NONE,
                }.0,
                FILE_SHARE_READ | FILE_SHARE_WRITE,
                None,
                OPEN_EXISTING,
                FILE_FLAG_OVERLAPPED,
                None
            )
        }?;
        Ok(Device(handle))
    }

    pub fn handle(&self) -> HANDLE {
        self.0
    }

    pub fn attributes(&self) -> HidResult<HIDD_ATTRIBUTES> {
        let mut attributes = HIDD_ATTRIBUTES::default();
        check_error(unsafe { HidD_GetAttributes(self.0, &mut attributes) })?;
        Ok(attributes)
    }

    pub fn preparsed_data(&self) -> HidResult<PreparsedData> {
        PreparsedData::from_device(self)
    }

    #[track_caller]
    fn read_string(&self, func: unsafe fn(HANDLE, *mut c_void, u32) -> bool) -> Option<String> {
        let mut buffer = [0u16; 512];
        ensure!(unsafe { func(self.0, buffer.as_mut_ptr() as _, (size_of::<u16>() * buffer.len()) as u32) });
            
        let serial_number = buffer
            .split(|c| *c == 0x0)
            .map(String::from_utf16_lossy)
            .next()
            .expect("Failed to interpret string");
        Some(serial_number)
    }

    pub fn serial_number(&self) -> Option<String> {
        //Silently discard errors
        self.read_string(HidD_GetSerialNumberString)
    }

    pub fn name(&self) -> HidResult<String> {
        self.read_string(HidD_GetProductString)
            .ok_or_else(|| windows::core::Error::from_win32().into())
    }

}

impl Drop for Device {
    fn drop(&mut self) {
        unsafe { CloseHandle(self.0).unwrap_or_else(|err| log::warn!("Failed to close device handle: {}", err)) }
    }
}

#[derive(Debug, Eq, PartialEq)]
#[repr(transparent)]
pub struct PreparsedData(PHIDP_PREPARSED_DATA);

impl PreparsedData {

    pub fn from_device(device: &Device) -> HidResult<PreparsedData> {
        let mut preparsed_data = PHIDP_PREPARSED_DATA::default();
        check_error(unsafe { HidD_GetPreparsedData(device.0, &mut preparsed_data) })?;
        Ok(PreparsedData(preparsed_data))
    }

    pub fn caps(&self) -> HidResult<HIDP_CAPS> {
        let mut caps = HIDP_CAPS::default();
        check_error(unsafe { HidP_GetCaps(self.0, &mut caps) } == HIDP_STATUS_SUCCESS)?;
        Ok(caps)
    }

}

impl Drop for PreparsedData {
    fn drop(&mut self) {
        check_error(unsafe { HidD_FreePreparsedData(self.0) })
            .unwrap_or_else(|err| log::warn!("Failed to free preparsed data: {}", err))
    }
}