Crate hut

Source
Expand description

A wrapper around the HID Usage Tables (HUT).

In this document and unless stated otherwise, a reference to

This module is created through code generation from the HID Usage Tables.

§Terminology

See HID Section 5.5: a HID Usage is a 32 bit value comprising of a 16-bit Usage Page (MSB) and a 16-bit Usage ID (LSB) so that:

let usage: u32 = (usage_page << 16) | usage_id;

Note that the HID encoding requires little endian byte order on the wire.

In this module:

  • “Usage Page” refers to the 16-bit value. Where the Usage Page is converted to or from a 32-bit value the Usage Page is in the upper 16 bits of that value and the lower 16 bits are ignored or set to zero.
    let usage_page: u16 = (usage >> 16) as u16 & 0xffff;
  • “Usage ID” refers to the 16-bit value. Where the Usage ID is converted to or from a 32-bit value the Usage is in the lower 16 bits of that value and the upper 16 bits are ignored or set to zero.
    let usage_id: u16 = (usage & 0xffff) as u16;
  • “Usage” refers to the 32-bit value comprising a Usage Page and a Usage.

§Converting between types

All defined Usages and UsagePages implement AsUsagePage and (if applicable) AsUsage as well as the From<u16>, From<u32>, TryFrom<u16>, and TryFrom<u32> conversions so that:

let usage_page_value: u16 = 0x01; // Generic Desktop
let usage_id_value: u16 = 0x02; // Mouse
let usage_value: u32 = (usage_page_value as u32) << 16 | usage_id_value as u32;

// Create a known Usage from a 32-bit value
let u: Usage = Usage::try_from(usage_value).unwrap();
assert!(matches!(u, Usage::GenericDesktop(GenericDesktop::Mouse)));

// Create a known Usage from the Usage Page and Usage ID values
let u2 = Usage::new_from_page_and_id(usage_page_value, usage_id_value).unwrap();
assert_eq!(u, u2);

// Create a known Usage from an individual Usage Page enum item
let u3 = Usage::from(GenericDesktop::Mouse);
assert_eq!(u, u3);

// Create a known Usage from an known Usage Page enum item
let gd_mouse = GenericDesktop::try_from(usage_id_value).unwrap();
let u4 = Usage::from(gd_mouse);
assert_eq!(u, u4);

// Convert to and fro the Usage either via u32 or the AsUsage trait
let u = GenericDesktop::Mouse;
assert_eq!(u32::from(&u), usage_value);
assert_eq!(u.usage_value(), usage_value);

// Extract the 16-bit Usage ID either via u16 or the AsUsage trait
assert_eq!(u16::from(&u), usage_id_value);
assert_eq!(u.usage_id_value(), usage_id_value);

// Extract the Usage Page from the Usage enum value
let up = u.usage_page();
assert!(matches!(up, UsagePage::GenericDesktop));
let up: UsagePage = UsagePage::from(&u);
assert!(matches!(up, UsagePage::GenericDesktop));

// Get the Usage Page numeric value is via the AsUsagePage
assert_eq!(u16::from(&up), usage_page_value);
assert_eq!(up.usage_page_value(), usage_page_value);

Naming Usages (e.g. GenericDesktop::Mouse) above works for Defined Usage Pages, Generated Usage Pages (see below) need to be destructured via their individual elements:

let usage_page_value: u16 = 0x09; // Button
let usage_id_value: u16 = 8; // Button number 8
let usage_value: u32 = (usage_page_value as u32) << 16 | usage_id_value as u32;

let u = Usage::try_from(usage_value).unwrap();
let button = Usage::Button(Button::Button(8));
assert!(matches!(Usage::try_from(usage_value).unwrap(), button));
// or via from() or into()
let button: Usage = Button::Button(8).into();
assert!(matches!(Usage::try_from(usage_value).unwrap(), button));

Once a Usage is created, the AsUsagePage and AsUsage traits and conversion to and from u16 and u32 work the same as for a Defined Usage Page.

§Names of Usage Pages and Usage IDs

All defined Usages and UsagePages implement name() to return a string representing that page or usage:

let up = UsagePage::GenericDesktop;
assert_eq!(up.name(), "Generic Desktop");
let up = UsagePage::SimulationControls;
assert_eq!(up.name(), "Simulation Controls");

let usage = GenericDesktop::Mouse;
assert_eq!(usage.name(), "Mouse");
let usage = SimulationControls::CyclicControl;
assert_eq!(usage.name(), "Cyclic Control");

§Generated Usage Pages

The HUT differ between “Defined” and “Generated” Usage Pages. The former define Usage ID values and their meanings, the latter define a Usage ID range, with the actual Usage ID simply referring to “nth thing in this usage page”. One example for this is the Button Usage Page (0x09) where a Usage ID of 3 means “Button 3”.

let b = Button::Button(3);
let o = Ordinal::Ordinal(23);

Unlike Defined Usage Pages these Generated Usage Pages need to be destructured in match statements:

let b = Button::Button(3);
match b {
    Button::Button(b) => println!("Button {b}"),
    _ => {},
}

The following usage pages are Generated:

A further special case of this is the Unicode usage page which is not in the HUT document and was inserted during code generation.

§Vendor Defined Usage Pages (0xFF00 to 0xFFFF)

Vendor Defined Usage Pages and VendorUsages are not autogenerated and thus follow a different approach: the Usage inside the Usage Page is a simple numeric usage that needs to be destructured in match statements.

let v = Usage::VendorDefinedPage {
    vendor_page: VendorPage::try_from(0xff00 as u16).unwrap(),
    usage: VendorDefinedPage::VendorUsage { usage_id: 0x01 },
};
match v {
    Usage::VendorDefinedPage {
        vendor_page,
        usage,
    } => println!("Vendor Usage ID {usage}"),
    _ => {},
}

A notable exception is the Wacom (0xFF0D) which is technically a Vendor-defined page but with defined Usages. Converting from a UsagePage or Usage numeric value will produce the correct or Wacom Usage, not a VendorDefinedPage::VendorUsage.

§Reserved Usage Pages

Reserved Usage Pages and ReservedUsages are not autogenerated and thus follow a different approach: the Usage inside the Usage Page is a simple numeric usage that needs to be destructured in match statements.

Unlike the Vendor Defined Usage Pages a Reserved Usage Page may become a defined page in a later version of the HUT standard and thus in a future version of this crate. A caller must not rely on a Reserved Usage Page or Reserved Usage to remain so.

The following Usage Pages are reserved as of HUT 1.5 (see HUT Section 3, p15):

  • 0x13, 0x15-0x1F
  • 0x21-0x3F
  • 0x42-0x58
  • 0x5A-0x7F
  • 0x83-0x83
  • 0x86-0x8B
  • 0x8F-0x8F
  • 0x93-0xF1CF
  • 0xF1D1-0xFEFF

§Renames

For technical reasons, spaces, ( ), dashes (-), and slashes (/) are stripped out of Usage Page and Usage names. The string representation via the Display trait will have the unmodified value.

Structs§

Enums§

Traits§

  • A trait to return the Usage and Usage ID as numeric value
  • A trait to return the Usage Page as numeric value