use std::error::Error;
use async_trait::async_trait;
use hidreport::{Field, Report, ReportDescriptor, Usage, UsageId, UsagePage};
use super::{ChannelError, LONG_REPORT_ID, SHORT_REPORT_ID};
const MAX_REPORT_DESCRIPTOR_LENGTH: usize = 4096;
const HIDPP_USAGE_PAGE: u16 = 0xff00;
const SHORT_REPORT_USAGE: u16 = 0x0001;
const LONG_REPORT_USAGE: u16 = 0x0002;
#[async_trait]
pub trait RawHidChannel: Sync + Send + 'static {
fn vendor_id(&self) -> u16;
fn product_id(&self) -> u16;
async fn write_report(&self, src: &[u8]) -> Result<usize, Box<dyn Error + Sync + Send>>;
async fn read_report(&self, buf: &mut [u8]) -> Result<usize, Box<dyn Error + Sync + Send>>;
fn is_connected(&self) -> bool {
true
}
fn supports_short_long_hidpp(&self) -> Option<(bool, bool)>;
async fn get_report_descriptor(
&self,
buf: &mut [u8],
) -> Result<usize, Box<dyn Error + Sync + Send>>;
}
pub(super) async fn supports_short_long_hidpp(
chan: &impl RawHidChannel,
) -> Result<(bool, bool), ChannelError> {
if let Some((supports_short, supports_long)) = chan.supports_short_long_hidpp() {
return Ok((supports_short, supports_long));
}
let mut raw_descriptor = vec![0u8; MAX_REPORT_DESCRIPTOR_LENGTH];
let descriptor_size = chan.get_report_descriptor(&mut raw_descriptor).await?;
let descriptor = ReportDescriptor::try_from(&raw_descriptor[..descriptor_size])
.map_err(ChannelError::ReportDescriptor)?;
Ok((
declares_report(&descriptor, SHORT_REPORT_ID, SHORT_REPORT_USAGE),
declares_report(&descriptor, LONG_REPORT_ID, LONG_REPORT_USAGE),
))
}
fn declares_report(descriptor: &ReportDescriptor, report_id: u8, usage: u16) -> bool {
descriptor
.find_input_report(&[report_id])
.and_then(|report| report.fields().first())
.and_then(|field| match field {
Field::Array(arr) => Some(arr.usage_range()),
_ => None,
})
.is_some_and(|range| {
range
.lookup_usage(&Usage::from_page_and_id(
UsagePage::from(HIDPP_USAGE_PAGE),
UsageId::from(usage),
))
.is_some()
})
}