#![warn(clippy::print_stderr, clippy::print_stdout, clippy::dbg_macro)]
#![warn(clippy::todo)]
#![warn(missing_docs)]
pub use hid_types;
pub use encode::EncodeItem;
use hid_types::id::{CollectionType, InputFlags, IoFlags, Unit, UnitExponent, UsagePage};
use hid_types::item::usage::{ExtendedUsage, Usage};
use hid_types::item::{Global, Item, Local, Main};
use tinyvec::TinyVec;
use std::path::Path;
mod encode;
pub type DescriptorBytes = TinyVec<[u8; 4]>;
pub fn encode_items(items: &[Item]) -> Vec<u8> {
items.iter().flat_map(|item| item.encode()).collect()
}
pub fn usage_page(usage_page: impl Into<UsagePage>) -> DescriptorBytes {
let usage_page: UsagePage = usage_page.into();
Global::UsagePage(usage_page).encode()
}
pub fn collection(value: impl Into<CollectionType>) -> DescriptorBytes {
Main::Collection(value.into()).encode()
}
pub fn end_collection() -> DescriptorBytes {
Main::EndCollection.encode()
}
pub fn logical_minimum(value: i32) -> DescriptorBytes {
Global::LogicalMinimum(value).encode()
}
pub fn logical_maximum(value: i32) -> DescriptorBytes {
Global::LogicalMaximum(value).encode()
}
pub fn physical_minimum(value: i32) -> DescriptorBytes {
Global::PhysicalMinimum(value).encode()
}
pub fn physical_maximum(value: i32) -> DescriptorBytes {
Global::PhysicalMaximum(value).encode()
}
pub fn unit(unit: impl Into<Unit>) -> DescriptorBytes {
let unit: Unit = unit.into();
Global::Unit(unit).encode()
}
pub fn unit_exponent(unit_exponent: impl Into<UnitExponent>) -> DescriptorBytes {
let unit: UnitExponent = unit_exponent.into();
Global::UnitExponent(unit).encode()
}
pub fn report_id(value: u8) -> DescriptorBytes {
Global::ReportId(value).encode()
}
pub fn report_size(value: u32) -> DescriptorBytes {
Global::ReportSize(value).encode()
}
pub fn report_count(value: u32) -> DescriptorBytes {
Global::ReportCount(value).encode()
}
pub fn push() -> DescriptorBytes {
Global::Push.encode()
}
pub fn pop() -> DescriptorBytes {
Global::Pop.encode()
}
pub fn input(flags: impl Into<IoFlags>) -> DescriptorBytes {
let flags = flags.into();
Main::Input(InputFlags::from_unchecked(flags)).encode()
}
pub fn output(flags: impl Into<IoFlags>) -> DescriptorBytes {
let flags = flags.into();
Main::Output(flags.into()).encode()
}
pub fn feature(flags: impl Into<IoFlags>) -> DescriptorBytes {
let flags = flags.into();
Main::Feature(flags.into()).encode()
}
pub fn usage(value: impl Into<u16>) -> DescriptorBytes {
let usage = Usage::without_page(value.into());
Local::Usage(usage).encode()
}
pub fn extended_usage(usage_page: impl Into<UsagePage>, usage: impl Into<u16>) -> DescriptorBytes {
let extended_usage: ExtendedUsage = ExtendedUsage::new(usage_page.into(), usage.into());
Local::ExtendedUsage(extended_usage).encode()
}
pub fn usage_minimum(value: impl Into<u16>) -> DescriptorBytes {
let value = value.into();
Local::UsageMinimum(value.into()).encode()
}
pub fn usage_maximum(value: impl Into<u16>) -> DescriptorBytes {
let value = value.into();
Local::UsageMaximum(value.into()).encode()
}
pub fn designator_index(value: u32) -> DescriptorBytes {
Local::DesignatorIndex(value).encode()
}
pub fn designator_minimum(value: u32) -> DescriptorBytes {
Local::DesignatorMinimum(value).encode()
}
pub fn designator_maximum(value: u32) -> DescriptorBytes {
Local::DesignatorMaximum(value).encode()
}
pub fn string_index(value: u32) -> DescriptorBytes {
Local::StringIndex(value).encode()
}
pub fn string_minimum(value: u32) -> DescriptorBytes {
Local::StringMinimum(value).encode()
}
pub fn string_maximum(value: u32) -> DescriptorBytes {
Local::StringMaximum(value).encode()
}
pub fn string_(value: bool) -> DescriptorBytes {
Local::Delimiter(value).encode()
}
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::id::{KnownCollectionType, 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, ]
);
}
}