#![warn(clippy::print_stderr, clippy::print_stdout, clippy::dbg_macro)]
#![warn(clippy::todo)]
#![warn(missing_docs)]
pub use hid_types;
use std::path::Path;
use hid_types::encoding::{TagTypeSize, encode_signed, encode_unsigned};
use hid_types::hid::{CollectionType, IoFlags};
use hid_types::id::tag::{GlobalItem, IntoTagType, LocalItem, MainItem};
use hid_types::id::usage::UsagePage;
pub type DescriptorBytes = tinyvec::ArrayVec<[u8; 4]>;
fn short_encode_unsigned<T: IntoTagType>(tag: T, data: &[u8]) -> DescriptorBytes {
let data = encode_unsigned(data);
let tag = TagTypeSize::from_tag(tag, data.len());
[tag.into_bits()]
.into_iter()
.chain(data.iter().copied())
.collect()
}
fn short_encode_signed<T: IntoTagType>(tag: T, data: &[u8]) -> DescriptorBytes {
let data = encode_signed(data);
let tag = TagTypeSize::from_tag(tag, data.len());
[tag.into_bits()]
.into_iter()
.chain(data.iter().copied())
.collect()
}
pub fn usage_page(page: impl Into<UsagePage>) -> DescriptorBytes {
let page: UsagePage = page.into();
let page_number = page.to_integer();
let data = page_number.to_le_bytes();
short_encode_unsigned(GlobalItem::UsagePage, &data)
}
pub fn usage(value: impl Into<u16>) -> DescriptorBytes {
let value = value.into();
short_encode_unsigned(LocalItem::Usage, &value.to_le_bytes())
}
pub fn collection(value: impl Into<CollectionType>) -> DescriptorBytes {
let value = value.into().to_integer();
short_encode_unsigned(MainItem::Collection, &value.to_le_bytes())
}
pub fn end_collection() -> DescriptorBytes {
short_encode_unsigned(MainItem::EndCollection, &[])
}
pub fn logical_minimum(value: i32) -> DescriptorBytes {
short_encode_signed(GlobalItem::LogicalMinimum, &value.to_le_bytes())
}
pub fn logical_maximum(value: i32) -> DescriptorBytes {
short_encode_signed(GlobalItem::LogicalMaximum, &value.to_le_bytes())
}
pub fn input(flags: impl Into<IoFlags>) -> DescriptorBytes {
let flags = flags.into();
let data: u32 = flags.into();
short_encode_unsigned(MainItem::Input, &data.to_le_bytes())
}
pub fn output(flags: impl Into<IoFlags>) -> DescriptorBytes {
let flags = flags.into();
let data: u32 = flags.into();
short_encode_unsigned(MainItem::Output, &data.to_le_bytes())
}
pub fn feature(flags: impl Into<IoFlags>) -> DescriptorBytes {
let flags = flags.into();
let data: u32 = flags.into();
short_encode_unsigned(MainItem::Feature, &data.to_le_bytes())
}
pub fn usage_minimum(value: impl Into<u16>) -> DescriptorBytes {
let value = value.into();
short_encode_unsigned(LocalItem::UsageMinimum, &value.to_le_bytes())
}
pub fn usage_maximum(value: impl Into<u16>) -> DescriptorBytes {
let value = value.into();
short_encode_unsigned(LocalItem::UsageMaximum, &value.to_le_bytes())
}
pub fn report_id(value: u8) -> DescriptorBytes {
short_encode_unsigned(GlobalItem::ReportId, &value.to_le_bytes())
}
pub fn report_size(value: u8) -> DescriptorBytes {
short_encode_unsigned(GlobalItem::ReportSize, &value.to_le_bytes())
}
pub fn report_count(value: u8) -> DescriptorBytes {
short_encode_unsigned(GlobalItem::ReportCount, &value.to_le_bytes())
}
pub fn concatenate_bytes(
items: impl IntoIterator<Item = impl IntoIterator<Item = u8>>,
) -> impl Iterator<Item = u8> {
items.into_iter().flatten()
}
pub fn write_to_file(
items: impl IntoIterator<Item = impl IntoIterator<Item = u8>>,
path: impl AsRef<Path>,
) -> std::io::Result<()> {
let descriptor: Vec<u8> = items.into_iter().flatten().collect();
std::fs::write(path, descriptor)
}
#[cfg(test)]
mod tests {
use hid_types::hid::{IoFlags, KnownCollectionType};
use hid_types::id::usage::KnownUsagePage;
use super::*;
#[test]
fn test_usage_page() {
let bytes = usage_page(KnownUsagePage::GenericDesktop);
assert_eq!(bytes.as_slice(), &[0x05, 0x01]);
let bytes = usage_page(UsagePage::Reserved(0xFF00));
assert_eq!(bytes.as_slice(), &[0x06, 0x00, 0xFF]);
let bytes = usage_page(UsagePage::Reserved(0x00FF));
assert_eq!(bytes.as_slice(), &[0x05, 0xFF]);
}
#[test]
fn concatenate() {
use hid_types::id::usage::generic_desktop::KnownUsage as Usage;
let bytes = [
usage_page(KnownUsagePage::GenericDesktop),
usage(Usage::Joystick),
collection(KnownCollectionType::Application),
usage(Usage::Pointer),
collection(KnownCollectionType::Physical),
usage(Usage::X),
usage(Usage::Y),
logical_minimum(-1),
logical_maximum(255),
report_size(8),
report_count(2),
input(IoFlags::data().variable()),
end_collection(),
usage_page(KnownUsagePage::Button),
usage_minimum(1u16),
usage_maximum(8u16),
logical_minimum(0),
logical_maximum(1),
report_size(1),
report_count(8),
input(IoFlags::data().variable()),
end_collection(),
];
let desc: Vec<u8> = concatenate_bytes(bytes).collect();
assert_eq!(
desc,
&[
0x05, 0x01, 0x09, 0x04, 0xA1, 0x01, 0x09, 0x01, 0xa1, 0x00, 0x09, 0x30, 0x09, 0x31, 0x15, 0xFF, 0x26, 0xFF, 0x00, 0x75, 0x08, 0x95, 0x02, 0x81, 0x02, 0xc0, 0x05, 0x09, 0x19, 0x01, 0x29, 0x08, 0x15, 0x00, 0x25, 0x01, 0x75, 0x01, 0x95, 0x08, 0x81, 0x02, 0xc0, ]
);
}
}