haptics!
https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/radial-controller-protocol-implementation With a little bit of trial and error (and a crash-course in how the heck HID even works), I figured out how to get the dial to provide haptic feedback! Along the way, I also learned that you can take advantage of the (incorrectly named) Resolution Multiplier field to customize how many "steps" the dial should have, offloading the work to the device itself! Very cool!!
This commit is contained in:
39
Cargo.lock
generated
39
Cargo.lock
generated
@@ -1,5 +1,11 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
[[package]]
|
||||
name = "arc-swap"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4d25d88fd6b8041580a654f9d0c581a047baee2b3efee13275f2fc392fc75034"
|
||||
|
||||
[[package]]
|
||||
name = "arrayref"
|
||||
version = "0.3.6"
|
||||
@@ -148,6 +154,17 @@ dependencies = [
|
||||
"wasi 0.9.0+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hidapi"
|
||||
version = "1.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c6ffb97f2ec5835ec73bcea5256fc2cd57a13c5958230778ef97f11900ba661"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
@@ -299,6 +316,26 @@ dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.1.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "604508c1418b99dfe1925ca9224829bb2a8a9a04dda655cc01fcad46f4ab05ed"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"signal-hook-registry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3e12110bc539e657a646068aaf5eb5b63af9d0c1f7b29c97113fad80e15f035"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.8.0"
|
||||
@@ -320,7 +357,9 @@ name = "surface-dial-daemon"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"evdev-rs",
|
||||
"hidapi",
|
||||
"notify-rust",
|
||||
"signal-hook",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -7,4 +7,6 @@ edition = "2018"
|
||||
[dependencies]
|
||||
# master includes a PR that implements `Send` for `Device` and `UInputDevice`
|
||||
evdev-rs = { git = "https://github.com/ndesh26/evdev-rs.git" }
|
||||
hidapi = { version = "1.2.3", default-features = false, features = ["linux-shared-hidraw"] }
|
||||
notify-rust = "4"
|
||||
signal-hook = "0.1.16"
|
||||
|
||||
28
README.md
28
README.md
@@ -4,6 +4,7 @@ A Linux userspace controller for the [Microsoft Surface Dial](https://www.micros
|
||||
|
||||
- Uses the [`evdev`](https://en.wikipedia.org/wiki/Evdev) API + `libevdev` to read events from the surface dial.
|
||||
- Uses `libevdev` to fake input via `/dev/uinput` (for keypresses / media controls)
|
||||
- Uses `hidapi` to configure dial sensitivity + haptics
|
||||
|
||||
**DISCLAIMER: This is WIP software!**
|
||||
|
||||
@@ -34,33 +35,42 @@ It would be cool to create some sort of GUI overlay (similar to the Windows one)
|
||||
- [x] Scrolling / Zooming
|
||||
- [ ] \(meta\) Specify modes via config file(s)
|
||||
- [ ] Dynamically switch between operating modes
|
||||
- _currently required re-compiling the daemon_
|
||||
- [ ] Context-sensitive (based on currently open application)
|
||||
- [ ] Using `surface-dial-cli` application
|
||||
- _currently requires re-compiling the daemon_
|
||||
- [ ] Using some-sort of on-device mechanism (e.g: long-press)
|
||||
- [ ] Haptic Feedback
|
||||
- This is tough one, as it doesn't seem like the kernel driver exposes any haptic feedback interface...
|
||||
- [ ] Using `surface-dial-cli` application
|
||||
- [ ] Context-sensitive (based on currently open application)
|
||||
- [x] Haptic Feedback
|
||||
- https://docs.microsoft.com/en-us/windows-hardware/design/component-guidelines/radial-controller-protocol-implementation
|
||||
- https://www.usb.org/sites/default/files/hutrr63b_-_haptics_page_redline_0.pdf
|
||||
- https://www.usb.org/sites/default/files/hut1_21.pdf
|
||||
- _This was tricky to figure out, but in the end, it was surprisingly straightforward! Big thanks to [Geo](https://www.linkedin.com/in/geo-palakunnel-57718245/) for pointing me in the right direction!_
|
||||
- [x] Desktop Notifications
|
||||
- [x] On Launch
|
||||
- [x] When switching between sub-modes (e.g: scroll/zoom)
|
||||
|
||||
Feel free to contribute new features!
|
||||
|
||||
## Building
|
||||
## Dependencies
|
||||
|
||||
Building `surface-dial-daemon` requires the following:
|
||||
|
||||
- Linux Kernel 4.19 or higher
|
||||
- A fairly recent version of the Rust compiler
|
||||
- `libevdev`
|
||||
- `hidapi`
|
||||
|
||||
If `libevdev` is not installed, the `evdev_rs` Rust library will try to build it from source, which may require other bits of build tooling. As such, it's recommended to install `libevdev` if it's available through your distribution.
|
||||
You can install Rust through [`rustup`](https://rustup.rs/).
|
||||
|
||||
Unless you're a cool hackerman, the easiest way to get `libevdev` and `hidapi` is via your distro's package manager.
|
||||
|
||||
```bash
|
||||
# e.g: on ubuntu
|
||||
sudo apt install libevdev-dev
|
||||
sudo apt install libevdev-dev libhidapi-dev
|
||||
```
|
||||
|
||||
Otherwise, `surface-dial-daemon` uses the bog-standard `cargo build` flow.
|
||||
## Building
|
||||
|
||||
`surface-dial-daemon` uses the bog-standard `cargo build` flow.
|
||||
|
||||
```bash
|
||||
cargo build -p surface-dial-daemon --release
|
||||
|
||||
@@ -1,234 +1,234 @@
|
||||
# extracted using hid-example.c + https://eleccelerator.com/usbdescreqparser/
|
||||
# extracted using cat /sys/bus/hid/devices/*:045E:091B.*/report_descriptor | hidrd-convert -o spec
|
||||
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x09, 0x0E, // Usage (0x0E)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x85, 0x01, // Report ID (1)
|
||||
0x05, 0x0D, // Usage Page (Digitizer)
|
||||
0x09, 0x21, // Usage (Puck)
|
||||
0xA1, 0x02, // Collection (Logical)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0x01, // Logical Maximum (1)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0xA1, 0x00, // Collection (Physical)
|
||||
0x05, 0x09, // Usage Page (Button)
|
||||
0x09, 0x01, // Usage (0x01)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x05, 0x0D, // Usage Page (Digitizer)
|
||||
0x09, 0x33, // Usage (Touch)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x95, 0x06, // Report Count (6)
|
||||
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0xA1, 0x02, // Collection (Logical)
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x09, 0x37, // Usage (Dial)
|
||||
0x16, 0x01, 0x80, // Logical Minimum (-32767)
|
||||
0x26, 0xFF, 0x7F, // Logical Maximum (32767)
|
||||
0x75, 0x10, // Report Size (16)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x81, 0x06, // Input (Data,Var,Rel,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x35, 0x00, // Physical Minimum (0)
|
||||
0x46, 0x10, 0x0E, // Physical Maximum (3600)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x26, 0x10, 0x0E, // Logical Maximum (3600)
|
||||
0x09, 0x48, // Usage (0x48)
|
||||
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0x45, 0x00, // Physical Maximum (0)
|
||||
0xC0, // End Collection
|
||||
0x55, 0x0E, // Unit Exponent (-2)
|
||||
0x65, 0x11, // Unit (System: SI Linear, Length: Centimeter)
|
||||
0x46, 0x00, 0x00, // Physical Maximum (0)
|
||||
0x26, 0x00, 0x00, // Logical Maximum (0)
|
||||
0x09, 0x30, // Usage (X)
|
||||
0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)
|
||||
0x09, 0x31, // Usage (Y)
|
||||
0x46, 0x00, 0x00, // Physical Maximum (0)
|
||||
0x26, 0x00, 0x00, // Logical Maximum (0)
|
||||
0x81, 0x42, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State)
|
||||
0x05, 0x0D, // Usage Page (Digitizer)
|
||||
0x09, 0x48, // Usage (0x48)
|
||||
0x15, 0x3A, // Logical Minimum (58)
|
||||
0x25, 0x3A, // Logical Maximum (58)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x55, 0x0F, // Unit Exponent (-1)
|
||||
0x35, 0x3A, // Physical Minimum (58)
|
||||
0x45, 0x3A, // Physical Maximum (58)
|
||||
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x55, 0x00, // Unit Exponent (0)
|
||||
0x65, 0x00, // Unit (None)
|
||||
0x35, 0x00, // Physical Minimum (0)
|
||||
0x45, 0x00, // Physical Maximum (0)
|
||||
0x05, 0x0E, // Usage Page (Reserved 0x0E)
|
||||
0x09, 0x01, // Usage (0x01)
|
||||
0xA1, 0x02, // Collection (Logical)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x26, 0xFF, 0x00, // Logical Maximum (255)
|
||||
0x09, 0x24, // Usage (0x24)
|
||||
0xB1, 0x42, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State,Non-volatile)
|
||||
0x09, 0x24, // Usage (0x24)
|
||||
0x91, 0x42, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State,Non-volatile)
|
||||
0x15, 0x01, // Logical Minimum (1)
|
||||
0x25, 0x07, // Logical Maximum (7)
|
||||
0x09, 0x20, // Usage (0x20)
|
||||
0xB1, 0x42, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State,Non-volatile)
|
||||
0x09, 0x21, // Usage (0x21)
|
||||
0x91, 0x42, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State,Non-volatile)
|
||||
0x25, 0x0A, // Logical Maximum (10)
|
||||
0x09, 0x28, // Usage (0x28)
|
||||
0xB1, 0x42, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State,Non-volatile)
|
||||
0x75, 0x10, // Report Size (16)
|
||||
0x26, 0xD0, 0x07, // Logical Maximum (2000)
|
||||
0x09, 0x25, // Usage (0x25)
|
||||
0xB1, 0x42, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State,Non-volatile)
|
||||
0x09, 0x25, // Usage (0x25)
|
||||
0x91, 0x42, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,Null State,Non-volatile)
|
||||
0x85, 0x02, // Report ID (2)
|
||||
0x75, 0x20, // Report Size (32)
|
||||
0x17, 0x37, 0x00, 0x01, 0x00, // Logical Minimum (65590)
|
||||
0x27, 0x37, 0x00, 0x01, 0x00, // Logical Maximum (65590)
|
||||
0x09, 0x22, // Usage (0x22)
|
||||
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0x09, 0x11, // Usage (0x11)
|
||||
0xA1, 0x02, // Collection (Logical)
|
||||
0x05, 0x0A, // Usage Page (Ordinal)
|
||||
0x95, 0x03, // Report Count (3)
|
||||
0x09, 0x03, // Usage (0x03)
|
||||
0x09, 0x04, // Usage (0x04)
|
||||
0x09, 0x05, // Usage (0x05)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0xFF, // Logical Maximum (-1)
|
||||
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0xC0, // End Collection
|
||||
0x05, 0x0E, // Usage Page (Reserved 0x0E)
|
||||
0x09, 0x10, // Usage (0x10)
|
||||
0xA1, 0x02, // Collection (Logical)
|
||||
0x05, 0x0A, // Usage Page (Ordinal)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x15, 0x03, // Logical Minimum (3)
|
||||
0x25, 0x03, // Logical Maximum (3)
|
||||
0x36, 0x03, 0x10, // Physical Minimum (4099)
|
||||
0x46, 0x03, 0x10, // Physical Maximum (4099)
|
||||
0x09, 0x03, // Usage (0x03)
|
||||
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0x15, 0x04, // Logical Minimum (4)
|
||||
0x25, 0x04, // Logical Maximum (4)
|
||||
0x36, 0x04, 0x10, // Physical Minimum (4100)
|
||||
0x46, 0x04, 0x10, // Physical Maximum (4100)
|
||||
0x09, 0x04, // Usage (0x04)
|
||||
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0x15, 0x05, // Logical Minimum (5)
|
||||
0x25, 0x05, // Logical Maximum (5)
|
||||
0x36, 0x04, 0x10, // Physical Minimum (4100)
|
||||
0x46, 0x04, 0x10, // Physical Maximum (4100)
|
||||
0x09, 0x05, // Usage (0x05)
|
||||
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0x35, 0x00, // Physical Minimum (0)
|
||||
0x45, 0x00, // Physical Maximum (0)
|
||||
0xC0, // End Collection
|
||||
0xC0, // End Collection
|
||||
0xC0, // End Collection
|
||||
0xC0, // End Collection
|
||||
0xC0, // End Collection
|
||||
0x06, 0x07, 0xFF, // Usage Page (Vendor Defined 0xFF07)
|
||||
0x09, 0x70, // Usage (0x70)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x85, 0x30, // Report ID (48)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0xFF, // Logical Maximum (-1)
|
||||
0x95, 0x01, // Report Count (1)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x09, 0x00, // Usage (0x00)
|
||||
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0xC0, // End Collection
|
||||
0x09, 0x71, // Usage (0x71)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0xFF, // Logical Maximum (-1)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x95, 0x48, // Report Count (72)
|
||||
0x85, 0x2A, // Report ID (42)
|
||||
0x09, 0xC6, // Usage (0xC6)
|
||||
0x82, 0x02, 0x01, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Buffered Bytes)
|
||||
0x09, 0xC7, // Usage (0xC7)
|
||||
0x92, 0x02, 0x01, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile,Buffered Bytes)
|
||||
0x95, 0x34, // Report Count (52)
|
||||
0x09, 0xC8, // Usage (0xC8)
|
||||
0xB2, 0x03, 0x01, // Feature (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile,Buffered Bytes)
|
||||
0x85, 0x2B, // Report ID (43)
|
||||
0x09, 0xC9, // Usage (0xC9)
|
||||
0x82, 0x02, 0x01, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Buffered Bytes)
|
||||
0x09, 0xCA, // Usage (0xCA)
|
||||
0x92, 0x02, 0x01, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile,Buffered Bytes)
|
||||
0x09, 0xCB, // Usage (0xCB)
|
||||
0xB2, 0x02, 0x01, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile,Buffered Bytes)
|
||||
0x17, 0x00, 0x00, 0x00, 0x80, // Logical Minimum (-2147483649)
|
||||
0x27, 0xFF, 0xFF, 0xFF, 0x7F, // Logical Maximum (2147483646)
|
||||
0x75, 0x20, // Report Size (32)
|
||||
0x95, 0x04, // Report Count (4)
|
||||
0x85, 0x2C, // Report ID (44)
|
||||
0x19, 0xCC, // Usage Minimum (0xCC)
|
||||
0x29, 0xCF, // Usage Maximum (0xCF)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x95, 0x04, // Report Count (4)
|
||||
0x85, 0x2D, // Report ID (45)
|
||||
0x19, 0xD8, // Usage Minimum (0xD8)
|
||||
0x29, 0xDB, // Usage Maximum (0xDB)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x95, 0x04, // Report Count (4)
|
||||
0x19, 0xDC, // Usage Minimum (0xDC)
|
||||
0x29, 0xDF, // Usage Maximum (0xDF)
|
||||
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0x19, 0xE0, // Usage Minimum (0xE0)
|
||||
0x29, 0xE3, // Usage Maximum (0xE3)
|
||||
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0x85, 0x2E, // Report ID (46)
|
||||
0x19, 0xE4, // Usage Minimum (0xE4)
|
||||
0x29, 0xE7, // Usage Maximum (0xE7)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x19, 0xE8, // Usage Minimum (0xE8)
|
||||
0x29, 0xEB, // Usage Maximum (0xEB)
|
||||
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0x95, 0x0B, // Report Count (11)
|
||||
0x19, 0xEC, // Usage Minimum (0xEC)
|
||||
0x29, 0xEF, // Usage Maximum (0xEF)
|
||||
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0x95, 0x04, // Report Count (4)
|
||||
0x85, 0x2F, // Report ID (47)
|
||||
0x19, 0xF0, // Usage Minimum (0xF0)
|
||||
0x29, 0xF3, // Usage Maximum (0xF3)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x19, 0xF4, // Usage Minimum (0xF4)
|
||||
0x29, 0xF7, // Usage Maximum (0xF7)
|
||||
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0x19, 0xF8, // Usage Minimum (0xF8)
|
||||
0x29, 0xFB, // Usage Maximum (0xFB)
|
||||
0xB1, 0x02, // Feature (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0xC0, // End Collection
|
||||
0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
|
||||
0x09, 0x80, // Usage (Sys Control)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x85, 0x32, // Report ID (50)
|
||||
0x09, 0x82, // Usage (Sys Sleep)
|
||||
0x09, 0x83, // Usage (Sys Wake Up)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0x01, // Logical Maximum (1)
|
||||
0x95, 0x02, // Report Count (2)
|
||||
0x75, 0x01, // Report Size (1)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x95, 0x06, // Report Count (6)
|
||||
0x81, 0x03, // Input (Const,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0xC0, // End Collection
|
||||
0x09, 0x72, // Usage (0x72)
|
||||
0xA1, 0x01, // Collection (Application)
|
||||
0x85, 0x31, // Report ID (49)
|
||||
0x95, 0x0A, // Report Count (10)
|
||||
0x75, 0x08, // Report Size (8)
|
||||
0x15, 0x00, // Logical Minimum (0)
|
||||
0x25, 0xFF, // Logical Maximum (-1)
|
||||
0x09, 0xC6, // Usage (0xC6)
|
||||
0x81, 0x02, // Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
|
||||
0x09, 0xC7, // Usage (0xC7)
|
||||
0x91, 0x02, // Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Non-volatile)
|
||||
0xC0, // End Collection
|
||||
Usage Page (Desktop), ; Generic desktop controls (01h)
|
||||
Usage (0Eh),
|
||||
Collection (Application),
|
||||
Report ID (1),
|
||||
Usage Page (Digitizer), ; Digitizer (0Dh)
|
||||
Usage (Puck), ; Puck (21h, logical collection)
|
||||
Collection (Logical),
|
||||
Logical Minimum (0),
|
||||
Logical Maximum (1),
|
||||
Report Size (1),
|
||||
Report Count (1),
|
||||
Collection (Physical),
|
||||
Usage Page (Button), ; Button (09h)
|
||||
Usage (01h),
|
||||
Input (Variable),
|
||||
Usage Page (Digitizer), ; Digitizer (0Dh)
|
||||
Usage (Touch), ; Touch (33h, momentary control)
|
||||
Input (Variable),
|
||||
Report Count (6),
|
||||
Input (Constant, Variable),
|
||||
Collection (Logical),
|
||||
Usage Page (Desktop), ; Generic desktop controls (01h)
|
||||
Usage (Dial), ; Dial (37h, dynamic value)
|
||||
Logical Minimum (-32767),
|
||||
Logical Maximum (32767),
|
||||
Report Size (16),
|
||||
Report Count (1),
|
||||
Input (Variable, Relative),
|
||||
Physical Minimum (0),
|
||||
Physical Maximum (3600),
|
||||
Logical Minimum (0),
|
||||
Logical Maximum (3600),
|
||||
Usage (Resolution Multiplier), ; Resolution multiplier (48h, dynamic value)
|
||||
Feature (Variable),
|
||||
Physical Maximum (0),
|
||||
End Collection,
|
||||
Unit Exponent (14),
|
||||
Unit (Centimeter),
|
||||
Physical Maximum (0),
|
||||
Logical Maximum (0),
|
||||
Usage (X), ; X (30h, dynamic value)
|
||||
Input (Variable, Null State),
|
||||
Usage (Y), ; Y (31h, dynamic value)
|
||||
Physical Maximum (0),
|
||||
Logical Maximum (0),
|
||||
Input (Variable, Null State),
|
||||
Usage Page (Digitizer), ; Digitizer (0Dh)
|
||||
Usage (Width), ; Width (48h, dynamic value)
|
||||
Logical Minimum (58),
|
||||
Logical Maximum (58),
|
||||
Report Size (8),
|
||||
Unit Exponent (15),
|
||||
Physical Minimum (58),
|
||||
Physical Maximum (58),
|
||||
Input (Constant, Variable),
|
||||
Unit Exponent (0),
|
||||
Unit,
|
||||
Physical Minimum (0),
|
||||
Physical Maximum (0),
|
||||
Usage Page (0Eh), ; 0Eh, reserved
|
||||
Usage (01h),
|
||||
Collection (Logical),
|
||||
Logical Minimum (0),
|
||||
Logical Maximum (255),
|
||||
Usage (24h),
|
||||
Feature (Variable, Null State),
|
||||
Usage (24h),
|
||||
Output (Variable, Null State),
|
||||
Logical Minimum (1),
|
||||
Logical Maximum (7),
|
||||
Usage (20h),
|
||||
Feature (Variable, Null State),
|
||||
Usage (21h),
|
||||
Output (Variable, Null State),
|
||||
Logical Maximum (10),
|
||||
Usage (28h),
|
||||
Feature (Variable, Null State),
|
||||
Report Size (16),
|
||||
Logical Maximum (2000),
|
||||
Usage (25h),
|
||||
Feature (Variable, Null State),
|
||||
Usage (25h),
|
||||
Output (Variable, Null State),
|
||||
Report ID (2),
|
||||
Report Size (32),
|
||||
Logical Minimum (65591),
|
||||
Logical Maximum (65591),
|
||||
Usage (22h),
|
||||
Feature (Variable),
|
||||
Usage (11h),
|
||||
Collection (Logical),
|
||||
Usage Page (Ordinal), ; Ordinal (0Ah)
|
||||
Report Count (3),
|
||||
Usage (03h),
|
||||
Usage (04h),
|
||||
Usage (05h),
|
||||
Report Size (8),
|
||||
Logical Minimum (0),
|
||||
Logical Maximum (-1),
|
||||
Feature (Variable),
|
||||
End Collection,
|
||||
Usage Page (0Eh), ; 0Eh, reserved
|
||||
Usage (10h),
|
||||
Collection (Logical),
|
||||
Usage Page (Ordinal), ; Ordinal (0Ah)
|
||||
Report Count (1),
|
||||
Logical Minimum (3),
|
||||
Logical Maximum (3),
|
||||
Physical Minimum (4099),
|
||||
Physical Maximum (4099),
|
||||
Usage (03h),
|
||||
Feature (Variable),
|
||||
Logical Minimum (4),
|
||||
Logical Maximum (4),
|
||||
Physical Minimum (4100),
|
||||
Physical Maximum (4100),
|
||||
Usage (04h),
|
||||
Feature (Variable),
|
||||
Logical Minimum (5),
|
||||
Logical Maximum (5),
|
||||
Physical Minimum (4100),
|
||||
Physical Maximum (4100),
|
||||
Usage (05h),
|
||||
Feature (Variable),
|
||||
Physical Minimum (0),
|
||||
Physical Maximum (0),
|
||||
End Collection,
|
||||
End Collection,
|
||||
End Collection,
|
||||
End Collection,
|
||||
End Collection,
|
||||
Usage Page (FF07h), ; FF07h, vendor-defined
|
||||
Usage (70h),
|
||||
Collection (Application),
|
||||
Report ID (48),
|
||||
Logical Minimum (0),
|
||||
Logical Maximum (-1),
|
||||
Report Count (1),
|
||||
Report Size (8),
|
||||
Usage (00h),
|
||||
Output (Variable),
|
||||
End Collection,
|
||||
Usage (71h),
|
||||
Collection (Application),
|
||||
Logical Minimum (0),
|
||||
Logical Maximum (-1),
|
||||
Report Size (8),
|
||||
Report Count (72),
|
||||
Report ID (42),
|
||||
Usage (C6h),
|
||||
Input (Variable, Buffered Bytes),
|
||||
Usage (C7h),
|
||||
Output (Variable, Buffered Bytes),
|
||||
Report Count (52),
|
||||
Usage (C8h),
|
||||
Feature (Constant, Variable, Buffered Bytes),
|
||||
Report ID (43),
|
||||
Usage (C9h),
|
||||
Input (Variable, Buffered Bytes),
|
||||
Usage (CAh),
|
||||
Output (Variable, Buffered Bytes),
|
||||
Usage (CBh),
|
||||
Feature (Variable, Buffered Bytes),
|
||||
Logical Minimum (-2147483648),
|
||||
Logical Maximum (2147483647),
|
||||
Report Size (32),
|
||||
Report Count (4),
|
||||
Report ID (44),
|
||||
Usage Minimum (CCh),
|
||||
Usage Maximum (CFh),
|
||||
Input (Variable),
|
||||
Report Count (4),
|
||||
Report ID (45),
|
||||
Usage Minimum (D8h),
|
||||
Usage Maximum (DBh),
|
||||
Input (Variable),
|
||||
Report Count (4),
|
||||
Usage Minimum (DCh),
|
||||
Usage Maximum (DFh),
|
||||
Output (Variable),
|
||||
Usage Minimum (E0h),
|
||||
Usage Maximum (E3h),
|
||||
Feature (Variable),
|
||||
Report ID (46),
|
||||
Usage Minimum (E4h),
|
||||
Usage Maximum (E7h),
|
||||
Input (Variable),
|
||||
Usage Minimum (E8h),
|
||||
Usage Maximum (EBh),
|
||||
Output (Variable),
|
||||
Report Count (11),
|
||||
Usage Minimum (ECh),
|
||||
Usage Maximum (EFh),
|
||||
Feature (Variable),
|
||||
Report Count (4),
|
||||
Report ID (47),
|
||||
Usage Minimum (F0h),
|
||||
Usage Maximum (F3h),
|
||||
Input (Variable),
|
||||
Usage Minimum (F4h),
|
||||
Usage Maximum (F7h),
|
||||
Output (Variable),
|
||||
Usage Minimum (F8h),
|
||||
Usage Maximum (FBh),
|
||||
Feature (Variable),
|
||||
End Collection,
|
||||
Usage Page (Desktop), ; Generic desktop controls (01h)
|
||||
Usage (Sys Control), ; System control (80h, application collection)
|
||||
Collection (Application),
|
||||
Report ID (50),
|
||||
Usage (Sys Sleep), ; System sleep (82h, one-shot control)
|
||||
Usage (Sys Wake Up), ; System wake up (83h, one-shot control)
|
||||
Logical Minimum (0),
|
||||
Logical Maximum (1),
|
||||
Report Count (2),
|
||||
Report Size (1),
|
||||
Input (Variable),
|
||||
Report Count (6),
|
||||
Input (Constant, Variable),
|
||||
End Collection,
|
||||
Usage (72h),
|
||||
Collection (Application),
|
||||
Report ID (49),
|
||||
Report Count (10),
|
||||
Report Size (8),
|
||||
Logical Minimum (0),
|
||||
Logical Maximum (-1),
|
||||
Usage (C6h),
|
||||
Input (Variable),
|
||||
Usage (C7h),
|
||||
Output (Variable),
|
||||
End Collection
|
||||
|
||||
621
notes/descriptor.c
Normal file
621
notes/descriptor.c
Normal file
@@ -0,0 +1,621 @@
|
||||
// generated by https://github.com/abend0c1/hidrdd
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Decoded Application Collection
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
05 01 (GLOBAL) USAGE_PAGE 0x0001 Generic Desktop Page
|
||||
09 0E (LOCAL) USAGE 0x0001000E System Multi-axis Controller (Application Collection)
|
||||
A1 01 (MAIN) COLLECTION 0x01 Application (Usage=0x0001000E: Page=Generic Desktop Page, Usage=System Multi-axis Controller, Type=Application Collection)
|
||||
85 01 (GLOBAL) REPORT_ID 0x01 (1)
|
||||
05 0D (GLOBAL) USAGE_PAGE 0x000D Digitizer Device Page
|
||||
09 21 (LOCAL) USAGE 0x000D0021 Puck (Logical Collection)
|
||||
A1 02 (MAIN) COLLECTION 0x02 Logical (Usage=0x000D0021: Page=Digitizer Device Page, Usage=Puck, Type=Logical Collection)
|
||||
15 00 (GLOBAL) LOGICAL_MINIMUM 0x00 (0) <-- Info: Consider replacing 15 00 with 14
|
||||
25 01 (GLOBAL) LOGICAL_MAXIMUM 0x01 (1)
|
||||
75 01 (GLOBAL) REPORT_SIZE 0x01 (1) Number of bits per field
|
||||
95 01 (GLOBAL) REPORT_COUNT 0x01 (1) Number of fields
|
||||
A1 00 (MAIN) COLLECTION 0x00 Physical (Usage=0x0: Page=, Usage=, Type=) <-- Error: COLLECTION must be preceded by a known USAGE <-- Warning: USAGE type should be CP (Physical Collection)
|
||||
05 09 (GLOBAL) USAGE_PAGE 0x0009 Button Page
|
||||
09 01 (LOCAL) USAGE 0x00090001 Button 1 Primary/trigger (Selector, On/Off Control, Momentary Control, or One Shot Control)
|
||||
81 02 (MAIN) INPUT 0x00000002 (1 field x 1 bit) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
|
||||
05 0D (GLOBAL) USAGE_PAGE 0x000D Digitizer Device Page
|
||||
09 33 (LOCAL) USAGE 0x000D0033 Touch (Momentary Control)
|
||||
81 02 (MAIN) INPUT 0x00000002 (1 field x 1 bit) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
|
||||
95 06 (GLOBAL) REPORT_COUNT 0x06 (6) Number of fields
|
||||
81 03 (MAIN) INPUT 0x00000003 (6 fields x 1 bit) 1=Constant 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
|
||||
A1 02 (MAIN) COLLECTION 0x02 Logical (Usage=0x0: Page=, Usage=, Type=) <-- Error: COLLECTION must be preceded by a known USAGE <-- Warning: USAGE type should be CL (Logical Collection)
|
||||
05 01 (GLOBAL) USAGE_PAGE 0x0001 Generic Desktop Page
|
||||
09 37 (LOCAL) USAGE 0x00010037 Dial (Dynamic Value)
|
||||
16 0180 (GLOBAL) LOGICAL_MINIMUM 0x8001 (-32767)
|
||||
26 FF7F (GLOBAL) LOGICAL_MAXIMUM 0x7FFF (32767)
|
||||
75 10 (GLOBAL) REPORT_SIZE 0x10 (16) Number of bits per field
|
||||
95 01 (GLOBAL) REPORT_COUNT 0x01 (1) Number of fields
|
||||
81 06 (MAIN) INPUT 0x00000006 (1 field x 16 bits) 0=Data 1=Variable 1=Relative 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
|
||||
35 00 (GLOBAL) PHYSICAL_MINIMUM 0x00 (0) <-- Info: Consider replacing 35 00 with 34
|
||||
46 100E (GLOBAL) PHYSICAL_MAXIMUM 0x0E10 (3600)
|
||||
15 00 (GLOBAL) LOGICAL_MINIMUM 0x00 (0) <-- Info: Consider replacing 15 00 with 14
|
||||
26 100E (GLOBAL) LOGICAL_MAXIMUM 0x0E10 (3600)
|
||||
09 48 (LOCAL) USAGE 0x00010048 Resolution Multiplier (Dynamic Value)
|
||||
B1 02 (MAIN) FEATURE 0x00000002 (1 field x 16 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
|
||||
45 00 (GLOBAL) PHYSICAL_MAXIMUM 0x00 (0) <-- Info: Consider replacing 45 00 with 44
|
||||
C0 (MAIN) END_COLLECTION Logical
|
||||
55 0E (GLOBAL) UNIT_EXPONENT 0x0E (Unit Value x 10⁻²)
|
||||
65 11 (GLOBAL) UNIT 0x11 Distance in metres [1 cm units] (1=System=SI Linear, 1=Length=Centimetre)
|
||||
46 0000 (GLOBAL) PHYSICAL_MAXIMUM 0x0000 (0) <-- Redundant: PHYSICAL_MAXIMUM is already 0 <-- Info: Consider replacing 46 0000 with 44
|
||||
26 0000 (GLOBAL) LOGICAL_MAXIMUM 0x0000 (0) <-- Info: Consider replacing 26 0000 with 24
|
||||
09 30 (LOCAL) USAGE 0x00010030 X (Dynamic Value)
|
||||
81 42 (MAIN) INPUT 0x00000042 (1 field x 16 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 1=Null 0=NonVolatile 0=Bitmap
|
||||
09 31 (LOCAL) USAGE 0x00010031 Y (Dynamic Value)
|
||||
46 0000 (GLOBAL) PHYSICAL_MAXIMUM 0x0000 (0) <-- Redundant: PHYSICAL_MAXIMUM is already 0 <-- Info: Consider replacing 46 0000 with 44
|
||||
26 0000 (GLOBAL) LOGICAL_MAXIMUM 0x0000 (0) <-- Redundant: LOGICAL_MAXIMUM is already 0 <-- Info: Consider replacing 26 0000 with 24
|
||||
81 42 (MAIN) INPUT 0x00000042 (1 field x 16 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 1=Null 0=NonVolatile 0=Bitmap
|
||||
05 0D (GLOBAL) USAGE_PAGE 0x000D Digitizer Device Page
|
||||
09 48 (LOCAL) USAGE 0x000D0048 Width (Dynamic Value)
|
||||
15 3A (GLOBAL) LOGICAL_MINIMUM 0x3A (58)
|
||||
25 3A (GLOBAL) LOGICAL_MAXIMUM 0x3A (58)
|
||||
75 08 (GLOBAL) REPORT_SIZE 0x08 (8) Number of bits per field
|
||||
55 0F (GLOBAL) UNIT_EXPONENT 0x0F (Unit Value x 10⁻¹)
|
||||
35 3A (GLOBAL) PHYSICAL_MINIMUM 0x3A (58)
|
||||
45 3A (GLOBAL) PHYSICAL_MAXIMUM 0x3A (58)
|
||||
81 03 (MAIN) INPUT 0x00000003 (1 field x 8 bits) 1=Constant 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
|
||||
55 00 (GLOBAL) UNIT_EXPONENT 0x00 (Unit Value x 10⁰) <-- Info: Consider replacing 55 00 with 54
|
||||
65 00 (GLOBAL) UNIT 0x00 No unit (0=None) <-- Info: Consider replacing 65 00 with 64
|
||||
35 00 (GLOBAL) PHYSICAL_MINIMUM 0x00 (0) <-- Info: Consider replacing 35 00 with 34
|
||||
45 00 (GLOBAL) PHYSICAL_MAXIMUM 0x00 (0) <-- Info: Consider replacing 45 00 with 44
|
||||
05 0E (GLOBAL) USAGE_PAGE 0x000E Haptics Page
|
||||
09 01 (LOCAL) USAGE 0x000E0001 Simple Haptic Controller (Application or Logical Collection)
|
||||
A1 02 (MAIN) COLLECTION 0x02 Logical (Usage=0x000E0001: Page=Haptics Page, Usage=Simple Haptic Controller, Type=Application or Logical Collection)
|
||||
15 00 (GLOBAL) LOGICAL_MINIMUM 0x00 (0) <-- Info: Consider replacing 15 00 with 14
|
||||
26 FF00 (GLOBAL) LOGICAL_MAXIMUM 0x00FF (255)
|
||||
09 24 (LOCAL) USAGE 0x000E0024 Repeat Count (Dynamic Value)
|
||||
B1 42 (MAIN) FEATURE 0x00000042 (1 field x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 1=Null 0=NonVolatile 0=Bitmap
|
||||
09 24 (LOCAL) USAGE 0x000E0024 Repeat Count (Dynamic Value)
|
||||
91 42 (MAIN) OUTPUT 0x00000042 (1 field x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 1=Null 0=NonVolatile 0=Bitmap
|
||||
15 01 (GLOBAL) LOGICAL_MINIMUM 0x01 (1)
|
||||
25 07 (GLOBAL) LOGICAL_MAXIMUM 0x07 (7)
|
||||
09 20 (LOCAL) USAGE 0x000E0020 Auto Trigger (Dynamic Value)
|
||||
B1 42 (MAIN) FEATURE 0x00000042 (1 field x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 1=Null 0=NonVolatile 0=Bitmap
|
||||
09 21 (LOCAL) USAGE 0x000E0021 Manual Trigger (Dynamic Value)
|
||||
91 42 (MAIN) OUTPUT 0x00000042 (1 field x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 1=Null 0=NonVolatile 0=Bitmap
|
||||
25 0A (GLOBAL) LOGICAL_MAXIMUM 0x0A (10)
|
||||
09 28 (LOCAL) USAGE 0x000E0028 Waveform Cutoff Time (Static Value)
|
||||
B1 42 (MAIN) FEATURE 0x00000042 (1 field x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 1=Null 0=NonVolatile 0=Bitmap
|
||||
75 10 (GLOBAL) REPORT_SIZE 0x10 (16) Number of bits per field
|
||||
26 D007 (GLOBAL) LOGICAL_MAXIMUM 0x07D0 (2000)
|
||||
09 25 (LOCAL) USAGE 0x000E0025 Retrigger Period (Dynamic Value)
|
||||
B1 42 (MAIN) FEATURE 0x00000042 (1 field x 16 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 1=Null 0=NonVolatile 0=Bitmap
|
||||
09 25 (LOCAL) USAGE 0x000E0025 Retrigger Period (Dynamic Value)
|
||||
91 42 (MAIN) OUTPUT 0x00000042 (1 field x 16 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 1=Null 0=NonVolatile 0=Bitmap
|
||||
85 02 (GLOBAL) REPORT_ID 0x02 (2)
|
||||
75 20 (GLOBAL) REPORT_SIZE 0x20 (32) Number of bits per field
|
||||
17 37000100 (GLOBAL) LOGICAL_MINIMUM 0x00010037 (65591)
|
||||
27 37000100 (GLOBAL) LOGICAL_MAXIMUM 0x00010037 (65591)
|
||||
09 22 (LOCAL) USAGE 0x000E0022 Auto Trigger Associated Control (Static Value)
|
||||
B1 02 (MAIN) FEATURE 0x00000002 (1 field x 32 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
|
||||
09 11 (LOCAL) USAGE 0x000E0011 Duration List (Named Array Collection)
|
||||
A1 02 (MAIN) COLLECTION 0x02 Logical (Usage=0x000E0011: Page=Haptics Page, Usage=Duration List, Type=Named Array Collection) <-- Warning: USAGE type should be CL (Logical Collection)
|
||||
05 0A (GLOBAL) USAGE_PAGE 0x000A Ordinal Page
|
||||
95 03 (GLOBAL) REPORT_COUNT 0x03 (3) Number of fields
|
||||
09 03 (LOCAL) USAGE 0x000A0003 Instance 3 (Usage Modifier Collection)
|
||||
09 04 (LOCAL) USAGE 0x000A0004 Instance 4 (Usage Modifier Collection)
|
||||
09 05 (LOCAL) USAGE 0x000A0005 Instance 5 (Usage Modifier Collection)
|
||||
75 08 (GLOBAL) REPORT_SIZE 0x08 (8) Number of bits per field
|
||||
15 00 (GLOBAL) LOGICAL_MINIMUM 0x00 (0) <-- Info: Consider replacing 15 00 with 14
|
||||
25 FF (GLOBAL) LOGICAL_MAXIMUM 0xFF (-1)
|
||||
B1 02 (MAIN) FEATURE 0x00000002 (3 fields x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap <-- Error: LOGICAL_MAXIMUM (-1) is less than LOGICAL_MINIMUM (0)
|
||||
C0 (MAIN) END_COLLECTION Logical
|
||||
05 0E (GLOBAL) USAGE_PAGE 0x000E Haptics Page
|
||||
09 10 (LOCAL) USAGE 0x000E0010 Waveform List (Named Array Collection)
|
||||
A1 02 (MAIN) COLLECTION 0x02 Logical (Usage=0x000E0010: Page=Haptics Page, Usage=Waveform List, Type=Named Array Collection) <-- Warning: USAGE type should be CL (Logical Collection)
|
||||
05 0A (GLOBAL) USAGE_PAGE 0x000A Ordinal Page
|
||||
95 01 (GLOBAL) REPORT_COUNT 0x01 (1) Number of fields
|
||||
15 03 (GLOBAL) LOGICAL_MINIMUM 0x03 (3)
|
||||
25 03 (GLOBAL) LOGICAL_MAXIMUM 0x03 (3)
|
||||
36 0310 (GLOBAL) PHYSICAL_MINIMUM 0x1003 (4099)
|
||||
46 0310 (GLOBAL) PHYSICAL_MAXIMUM 0x1003 (4099)
|
||||
09 03 (LOCAL) USAGE 0x000A0003 Instance 3 (Usage Modifier Collection)
|
||||
B1 02 (MAIN) FEATURE 0x00000002 (1 field x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
|
||||
15 04 (GLOBAL) LOGICAL_MINIMUM 0x04 (4)
|
||||
25 04 (GLOBAL) LOGICAL_MAXIMUM 0x04 (4)
|
||||
36 0410 (GLOBAL) PHYSICAL_MINIMUM 0x1004 (4100)
|
||||
46 0410 (GLOBAL) PHYSICAL_MAXIMUM 0x1004 (4100)
|
||||
09 04 (LOCAL) USAGE 0x000A0004 Instance 4 (Usage Modifier Collection)
|
||||
B1 02 (MAIN) FEATURE 0x00000002 (1 field x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
|
||||
15 05 (GLOBAL) LOGICAL_MINIMUM 0x05 (5)
|
||||
25 05 (GLOBAL) LOGICAL_MAXIMUM 0x05 (5)
|
||||
36 0410 (GLOBAL) PHYSICAL_MINIMUM 0x1004 (4100) <-- Redundant: PHYSICAL_MINIMUM is already 4100
|
||||
46 0410 (GLOBAL) PHYSICAL_MAXIMUM 0x1004 (4100) <-- Redundant: PHYSICAL_MAXIMUM is already 4100
|
||||
09 05 (LOCAL) USAGE 0x000A0005 Instance 5 (Usage Modifier Collection)
|
||||
B1 02 (MAIN) FEATURE 0x00000002 (1 field x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
|
||||
35 00 (GLOBAL) PHYSICAL_MINIMUM 0x00 (0) <-- Info: Consider replacing 35 00 with 34
|
||||
45 00 (GLOBAL) PHYSICAL_MAXIMUM 0x00 (0) <-- Info: Consider replacing 45 00 with 44
|
||||
C0 (MAIN) END_COLLECTION Logical
|
||||
C0 (MAIN) END_COLLECTION Logical
|
||||
C0 (MAIN) END_COLLECTION Physical
|
||||
C0 (MAIN) END_COLLECTION Logical
|
||||
C0 (MAIN) END_COLLECTION Application
|
||||
*/
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Generic Desktop Page featureReport 01 (Device <-> Host)
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t reportId; // Report ID = 0x01 (1)
|
||||
// Collection: CA:SystemMulti-axisController CL:Puck CP: CL:
|
||||
uint16_t GD_SystemMultiaxisControllerPuckResolutionMultiplier; // Usage 0x00010048: Resolution Multiplier, Value = 0 to 3600, Physical = Value
|
||||
// Collection: CA:SystemMulti-axisController CL:Puck CP: CL:SimpleHapticController
|
||||
uint8_t HAP_SystemMultiaxisControllerPuckSimpleHapticControllerRepeatCount; // Usage 0x000E0024: Repeat Count, Value = 0 to 255
|
||||
uint8_t HAP_SystemMultiaxisControllerPuckSimpleHapticControllerAutoTrigger; // Usage 0x000E0020: Auto Trigger, Value = 1 to 7
|
||||
uint8_t HAP_SystemMultiaxisControllerPuckSimpleHapticControllerWaveformCutoffTime; // Usage 0x000E0028: Waveform Cutoff Time, Value = 1 to 10
|
||||
uint16_t HAP_SystemMultiaxisControllerPuckSimpleHapticControllerRetriggerPeriod; // Usage 0x000E0025: Retrigger Period, Value = 1 to 2000
|
||||
} featureReport01_t;
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Haptics Page featureReport 02 (Device <-> Host)
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t reportId; // Report ID = 0x02 (2)
|
||||
// Collection: CA:SystemMulti-axisController CL:Puck CP: CL:SimpleHapticController
|
||||
uint32_t HAP_SystemMultiaxisControllerPuckSimpleHapticControllerAutoTriggerAssociatedControl; // Usage 0x000E0022: Auto Trigger Associated Control, Value = 65591 to 65591
|
||||
// Collection: CA:SystemMulti-axisController CL:Puck CP: CL:SimpleHapticController CL:DurationList
|
||||
uint8_t ORD_SystemMultiaxisControllerPuckSimpleHapticControllerDurationListInstance3; // Usage 0x000A0003: Instance 3, Value = 0 to -1
|
||||
uint8_t ORD_SystemMultiaxisControllerPuckSimpleHapticControllerDurationListInstance4; // Usage 0x000A0004: Instance 4, Value = 0 to -1
|
||||
uint8_t ORD_SystemMultiaxisControllerPuckSimpleHapticControllerDurationListInstance5; // Usage 0x000A0005: Instance 5, Value = 0 to -1
|
||||
// Collection: CA:SystemMulti-axisController CL:Puck CP: CL:SimpleHapticController CL:WaveformList
|
||||
uint8_t ORD_SystemMultiaxisControllerPuckSimpleHapticControllerWaveformListInstance3; // Usage 0x000A0003: Instance 3, Value = 3 to 3, Physical = ((Value - 3) x 0 / 0 + 4099)
|
||||
uint8_t ORD_SystemMultiaxisControllerPuckSimpleHapticControllerWaveformListInstance4; // Usage 0x000A0004: Instance 4, Value = 4 to 4, Physical = ((Value - 4) x 0 / 0 + 4100)
|
||||
uint8_t ORD_SystemMultiaxisControllerPuckSimpleHapticControllerWaveformListInstance5; // Usage 0x000A0005: Instance 5, Value = 5 to 5, Physical = ((Value - 5) x 0 / 0 + 4100)
|
||||
} featureReport02_t;
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Button Page inputReport 01 (Device --> Host)
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t reportId; // Report ID = 0x01 (1)
|
||||
// Collection: CA:SystemMulti-axisController CL:Puck CP:
|
||||
uint8_t BTN_SystemMultiaxisControllerPuckButton1 : 1; // Usage 0x00090001: Button 1 Primary/trigger, Value = 0 to 1
|
||||
uint8_t DIG_SystemMultiaxisControllerPuckTouch : 1; // Usage 0x000D0033: Touch, Value = 0 to 1
|
||||
uint8_t : 1; // Pad
|
||||
uint8_t : 1; // Pad
|
||||
uint8_t : 1; // Pad
|
||||
uint8_t : 1; // Pad
|
||||
uint8_t : 1; // Pad
|
||||
uint8_t : 1; // Pad
|
||||
// Collection: CA:SystemMulti-axisController CL:Puck CP: CL:
|
||||
int16_t GD_SystemMultiaxisControllerPuckDial; // Usage 0x00010037: Dial, Value = -32767 to 32767
|
||||
// Collection: CA:SystemMulti-axisController CL:Puck CP:
|
||||
uint16_t GD_SystemMultiaxisControllerPuckX; // Usage 0x00010030: X, Value = 0 to 0, Physical = Value x 0 / 0 in 10⁻⁴ m units
|
||||
uint16_t GD_SystemMultiaxisControllerPuckY; // Usage 0x00010031: Y, Value = 0 to 0, Physical = Value x 0 / 0 in 10⁻⁴ m units
|
||||
uint8_t DIG_SystemMultiaxisControllerPuckWidth; // Usage 0x000D0048: Width, Value = 58 to 58, Physical = ((Value - 58) x 0 / 0 + 58) in 10⁻³ m units
|
||||
} inputReport01_t;
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Haptics Page outputReport 01 (Device <-- Host)
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t reportId; // Report ID = 0x01 (1)
|
||||
// Collection: CA:SystemMulti-axisController CL:Puck CP: CL:SimpleHapticController
|
||||
uint8_t HAP_SystemMultiaxisControllerPuckSimpleHapticControllerRepeatCount; // Usage 0x000E0024: Repeat Count, Value = 0 to 255
|
||||
uint8_t HAP_SystemMultiaxisControllerPuckSimpleHapticControllerManualTrigger; // Usage 0x000E0021: Manual Trigger, Value = 1 to 7
|
||||
uint16_t HAP_SystemMultiaxisControllerPuckSimpleHapticControllerRetriggerPeriod; // Usage 0x000E0025: Retrigger Period, Value = 1 to 2000
|
||||
} outputReport01_t;
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Decoded Application Collection
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
06 07FF (GLOBAL) USAGE_PAGE 0xFF07 Vendor-defined
|
||||
09 70 (LOCAL) USAGE 0xFF070070 <-- Warning: Undocumented usage (document it by inserting 0070 into file FF07.conf) <-- Error: Usage Modifier COLLECTION item (A1 06) expected for USAGE 0x000A0005 Instance 5 (Usage Modifier Collection)
|
||||
A1 01 (MAIN) COLLECTION 0x01 Application (Usage=0xFF070070: Page=Vendor-defined, Usage=, Type=) <-- Error: COLLECTION must be preceded by a known USAGE
|
||||
85 30 (GLOBAL) REPORT_ID 0x30 (48) '0'
|
||||
15 00 (GLOBAL) LOGICAL_MINIMUM 0x00 (0) <-- Info: Consider replacing 15 00 with 14
|
||||
25 FF (GLOBAL) LOGICAL_MAXIMUM 0xFF (-1)
|
||||
95 01 (GLOBAL) REPORT_COUNT 0x01 (1) Number of fields <-- Redundant: REPORT_COUNT is already 1
|
||||
75 08 (GLOBAL) REPORT_SIZE 0x08 (8) Number of bits per field <-- Redundant: REPORT_SIZE is already 8
|
||||
09 00 (LOCAL) USAGE 0xFF070000 <-- Info: Consider replacing 09 00 with 08
|
||||
91 02 (MAIN) OUTPUT 0x00000002 (1 field x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap <-- Error: LOGICAL_MAXIMUM (-1) is less than LOGICAL_MINIMUM (0)
|
||||
C0 (MAIN) END_COLLECTION Application
|
||||
*/
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Vendor-defined outputReport 30 (Device <-- Host)
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t reportId; // Report ID = 0x30 (48) '0'
|
||||
// Collection: CA:
|
||||
uint8_t VEN_0000; // Usage 0xFF070000: , Value = 0 to -1
|
||||
} outputReport30_t;
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Decoded Application Collection
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
09 71 (LOCAL) USAGE 0xFF070071 <-- Warning: Undocumented usage (document it by inserting 0071 into file FF07.conf)
|
||||
A1 01 (MAIN) COLLECTION 0x01 Application (Usage=0xFF070071: Page=Vendor-defined, Usage=, Type=) <-- Error: COLLECTION must be preceded by a known USAGE
|
||||
15 00 (GLOBAL) LOGICAL_MINIMUM 0x00 (0) <-- Redundant: LOGICAL_MINIMUM is already 0 <-- Info: Consider replacing 15 00 with 14
|
||||
25 FF (GLOBAL) LOGICAL_MAXIMUM 0xFF (-1) <-- Redundant: LOGICAL_MAXIMUM is already -1
|
||||
75 08 (GLOBAL) REPORT_SIZE 0x08 (8) Number of bits per field <-- Redundant: REPORT_SIZE is already 8
|
||||
95 48 (GLOBAL) REPORT_COUNT 0x48 (72) Number of fields
|
||||
85 2A (GLOBAL) REPORT_ID 0x2A (42)
|
||||
09 C6 (LOCAL) USAGE 0xFF0700C6 <-- Warning: Undocumented usage (document it by inserting 00C6 into file FF07.conf)
|
||||
82 0201 (MAIN) INPUT 0x00000102 (72 fields x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 1=Buffer <-- Error: LOGICAL_MAXIMUM (-1) is less than LOGICAL_MINIMUM (0)
|
||||
09 C7 (LOCAL) USAGE 0xFF0700C7 <-- Warning: Undocumented usage (document it by inserting 00C7 into file FF07.conf)
|
||||
92 0201 (MAIN) OUTPUT 0x00000102 (72 fields x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 1=Buffer <-- Error: LOGICAL_MAXIMUM (-1) is less than LOGICAL_MINIMUM (0)
|
||||
95 34 (GLOBAL) REPORT_COUNT 0x34 (52) Number of fields
|
||||
09 C8 (LOCAL) USAGE 0xFF0700C8 <-- Warning: Undocumented usage (document it by inserting 00C8 into file FF07.conf)
|
||||
B2 0301 (MAIN) FEATURE 0x00000103 (52 fields x 8 bits) 1=Constant 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 1=Buffer
|
||||
85 2B (GLOBAL) REPORT_ID 0x2B (43)
|
||||
09 C9 (LOCAL) USAGE 0xFF0700C9 <-- Warning: Undocumented usage (document it by inserting 00C9 into file FF07.conf)
|
||||
82 0201 (MAIN) INPUT 0x00000102 (52 fields x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 1=Buffer <-- Error: LOGICAL_MAXIMUM (-1) is less than LOGICAL_MINIMUM (0)
|
||||
09 CA (LOCAL) USAGE 0xFF0700CA <-- Warning: Undocumented usage (document it by inserting 00CA into file FF07.conf)
|
||||
92 0201 (MAIN) OUTPUT 0x00000102 (52 fields x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 1=Buffer <-- Error: LOGICAL_MAXIMUM (-1) is less than LOGICAL_MINIMUM (0)
|
||||
09 CB (LOCAL) USAGE 0xFF0700CB <-- Warning: Undocumented usage (document it by inserting 00CB into file FF07.conf)
|
||||
B2 0201 (MAIN) FEATURE 0x00000102 (52 fields x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 1=Buffer <-- Error: LOGICAL_MAXIMUM (-1) is less than LOGICAL_MINIMUM (0)
|
||||
17 00000080 (GLOBAL) LOGICAL_MINIMUM 0x80000000 (-2147483648)
|
||||
27 FFFFFF7F (GLOBAL) LOGICAL_MAXIMUM 0x7FFFFFFF (2147483647)
|
||||
75 20 (GLOBAL) REPORT_SIZE 0x20 (32) Number of bits per field
|
||||
95 04 (GLOBAL) REPORT_COUNT 0x04 (4) Number of fields
|
||||
85 2C (GLOBAL) REPORT_ID 0x2C (44)
|
||||
19 CC (LOCAL) USAGE_MINIMUM 0xFF0700CC <-- Warning: Undocumented usage (document it by inserting 00CC into file FF07.conf)
|
||||
29 CF (LOCAL) USAGE_MAXIMUM 0xFF0700CF <-- Warning: Undocumented usage (document it by inserting 00CF into file FF07.conf)
|
||||
81 02 (MAIN) INPUT 0x00000002 (4 fields x 32 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
|
||||
95 04 (GLOBAL) REPORT_COUNT 0x04 (4) Number of fields <-- Redundant: REPORT_COUNT is already 4
|
||||
85 2D (GLOBAL) REPORT_ID 0x2D (45)
|
||||
19 D8 (LOCAL) USAGE_MINIMUM 0xFF0700D8 <-- Warning: Undocumented usage (document it by inserting 00D8 into file FF07.conf)
|
||||
29 DB (LOCAL) USAGE_MAXIMUM 0xFF0700DB <-- Warning: Undocumented usage (document it by inserting 00DB into file FF07.conf)
|
||||
81 02 (MAIN) INPUT 0x00000002 (4 fields x 32 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
|
||||
95 04 (GLOBAL) REPORT_COUNT 0x04 (4) Number of fields <-- Redundant: REPORT_COUNT is already 4
|
||||
19 DC (LOCAL) USAGE_MINIMUM 0xFF0700DC <-- Warning: Undocumented usage (document it by inserting 00DC into file FF07.conf)
|
||||
29 DF (LOCAL) USAGE_MAXIMUM 0xFF0700DF <-- Warning: Undocumented usage (document it by inserting 00DF into file FF07.conf)
|
||||
91 02 (MAIN) OUTPUT 0x00000002 (4 fields x 32 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
|
||||
19 E0 (LOCAL) USAGE_MINIMUM 0xFF0700E0 <-- Warning: Undocumented usage (document it by inserting 00E0 into file FF07.conf)
|
||||
29 E3 (LOCAL) USAGE_MAXIMUM 0xFF0700E3 <-- Warning: Undocumented usage (document it by inserting 00E3 into file FF07.conf)
|
||||
B1 02 (MAIN) FEATURE 0x00000002 (4 fields x 32 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
|
||||
85 2E (GLOBAL) REPORT_ID 0x2E (46)
|
||||
19 E4 (LOCAL) USAGE_MINIMUM 0xFF0700E4 <-- Warning: Undocumented usage (document it by inserting 00E4 into file FF07.conf)
|
||||
29 E7 (LOCAL) USAGE_MAXIMUM 0xFF0700E7 <-- Warning: Undocumented usage (document it by inserting 00E7 into file FF07.conf)
|
||||
81 02 (MAIN) INPUT 0x00000002 (4 fields x 32 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
|
||||
19 E8 (LOCAL) USAGE_MINIMUM 0xFF0700E8 <-- Warning: Undocumented usage (document it by inserting 00E8 into file FF07.conf)
|
||||
29 EB (LOCAL) USAGE_MAXIMUM 0xFF0700EB <-- Warning: Undocumented usage (document it by inserting 00EB into file FF07.conf)
|
||||
91 02 (MAIN) OUTPUT 0x00000002 (4 fields x 32 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
|
||||
95 0B (GLOBAL) REPORT_COUNT 0x0B (11) Number of fields
|
||||
19 EC (LOCAL) USAGE_MINIMUM 0xFF0700EC <-- Warning: Undocumented usage (document it by inserting 00EC into file FF07.conf)
|
||||
29 EF (LOCAL) USAGE_MAXIMUM 0xFF0700EF <-- Warning: Undocumented usage (document it by inserting 00EF into file FF07.conf)
|
||||
B1 02 (MAIN) FEATURE 0x00000002 (11 fields x 32 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
|
||||
95 04 (GLOBAL) REPORT_COUNT 0x04 (4) Number of fields
|
||||
85 2F (GLOBAL) REPORT_ID 0x2F (47)
|
||||
19 F0 (LOCAL) USAGE_MINIMUM 0xFF0700F0 <-- Warning: Undocumented usage (document it by inserting 00F0 into file FF07.conf)
|
||||
29 F3 (LOCAL) USAGE_MAXIMUM 0xFF0700F3 <-- Warning: Undocumented usage (document it by inserting 00F3 into file FF07.conf)
|
||||
81 02 (MAIN) INPUT 0x00000002 (4 fields x 32 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
|
||||
19 F4 (LOCAL) USAGE_MINIMUM 0xFF0700F4 <-- Warning: Undocumented usage (document it by inserting 00F4 into file FF07.conf)
|
||||
29 F7 (LOCAL) USAGE_MAXIMUM 0xFF0700F7 <-- Warning: Undocumented usage (document it by inserting 00F7 into file FF07.conf)
|
||||
91 02 (MAIN) OUTPUT 0x00000002 (4 fields x 32 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
|
||||
19 F8 (LOCAL) USAGE_MINIMUM 0xFF0700F8 <-- Warning: Undocumented usage (document it by inserting 00F8 into file FF07.conf)
|
||||
29 FB (LOCAL) USAGE_MAXIMUM 0xFF0700FB <-- Warning: Undocumented usage (document it by inserting 00FB into file FF07.conf)
|
||||
B1 02 (MAIN) FEATURE 0x00000002 (4 fields x 32 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
|
||||
C0 (MAIN) END_COLLECTION Application
|
||||
*/
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Vendor-defined featureReport 2A (Device <-> Host)
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t reportId; // Report ID = 0x2A (42)
|
||||
// Collection: CA:
|
||||
uint8_t VEN_00C8[52]; // Usage 0xFF0700C8: , Value = 0 to -1
|
||||
} featureReport2A_t;
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Vendor-defined featureReport 2B (Device <-> Host)
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t reportId; // Report ID = 0x2B (43)
|
||||
// Collection: CA:
|
||||
uint8_t VEN_00CB[52]; // Usage 0xFF0700CB: , Value = 0 to -1
|
||||
} featureReport2B_t;
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Vendor-defined featureReport 2D (Device <-> Host)
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t reportId; // Report ID = 0x2D (45)
|
||||
// Collection: CA:
|
||||
int32_t VEN_00E0; // Usage 0xFF0700E0: , Value = -2147483648 to 2147483647
|
||||
int32_t VEN_00E1; // Usage 0xFF0700E1: , Value = -2147483648 to 2147483647
|
||||
int32_t VEN_00E2; // Usage 0xFF0700E2: , Value = -2147483648 to 2147483647
|
||||
int32_t VEN_00E3; // Usage 0xFF0700E3: , Value = -2147483648 to 2147483647
|
||||
} featureReport2D_t;
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Vendor-defined featureReport 2E (Device <-> Host)
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t reportId; // Report ID = 0x2E (46)
|
||||
// Collection: CA:
|
||||
int32_t VEN_00EC; // Usage 0xFF0700EC: , Value = -2147483648 to 2147483647
|
||||
int32_t VEN_00ED; // Usage 0xFF0700ED: , Value = -2147483648 to 2147483647
|
||||
int32_t VEN_00EE; // Usage 0xFF0700EE: , Value = -2147483648 to 2147483647
|
||||
int32_t VEN_00EF[8]; // Usage 0xFF0700EF: , Value = -2147483648 to 2147483647
|
||||
} featureReport2E_t;
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Vendor-defined featureReport 2F (Device <-> Host)
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t reportId; // Report ID = 0x2F (47)
|
||||
// Collection: CA:
|
||||
int32_t VEN_00F8; // Usage 0xFF0700F8: , Value = -2147483648 to 2147483647
|
||||
int32_t VEN_00F9; // Usage 0xFF0700F9: , Value = -2147483648 to 2147483647
|
||||
int32_t VEN_00FA; // Usage 0xFF0700FA: , Value = -2147483648 to 2147483647
|
||||
int32_t VEN_00FB; // Usage 0xFF0700FB: , Value = -2147483648 to 2147483647
|
||||
} featureReport2F_t;
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Vendor-defined inputReport 2A (Device --> Host)
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t reportId; // Report ID = 0x2A (42)
|
||||
// Collection: CA:
|
||||
uint8_t VEN_00C6[72]; // Usage 0xFF0700C6: , Value = 0 to -1
|
||||
} inputReport2A_t;
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Vendor-defined inputReport 2B (Device --> Host)
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t reportId; // Report ID = 0x2B (43)
|
||||
// Collection: CA:
|
||||
uint8_t VEN_00C9[52]; // Usage 0xFF0700C9: , Value = 0 to -1
|
||||
} inputReport2B_t;
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Vendor-defined inputReport 2C (Device --> Host)
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t reportId; // Report ID = 0x2C (44)
|
||||
// Collection: CA:
|
||||
int32_t VEN_00CC; // Usage 0xFF0700CC: , Value = -2147483648 to 2147483647
|
||||
int32_t VEN_00CD; // Usage 0xFF0700CD: , Value = -2147483648 to 2147483647
|
||||
int32_t VEN_00CE; // Usage 0xFF0700CE: , Value = -2147483648 to 2147483647
|
||||
int32_t VEN_00CF; // Usage 0xFF0700CF: , Value = -2147483648 to 2147483647
|
||||
} inputReport2C_t;
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Vendor-defined inputReport 2D (Device --> Host)
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t reportId; // Report ID = 0x2D (45)
|
||||
// Collection: CA:
|
||||
int32_t VEN_00D8; // Usage 0xFF0700D8: , Value = -2147483648 to 2147483647
|
||||
int32_t VEN_00D9; // Usage 0xFF0700D9: , Value = -2147483648 to 2147483647
|
||||
int32_t VEN_00DA; // Usage 0xFF0700DA: , Value = -2147483648 to 2147483647
|
||||
int32_t VEN_00DB; // Usage 0xFF0700DB: , Value = -2147483648 to 2147483647
|
||||
} inputReport2D_t;
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Vendor-defined inputReport 2E (Device --> Host)
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t reportId; // Report ID = 0x2E (46)
|
||||
// Collection: CA:
|
||||
int32_t VEN_00E4; // Usage 0xFF0700E4: , Value = -2147483648 to 2147483647
|
||||
int32_t VEN_00E5; // Usage 0xFF0700E5: , Value = -2147483648 to 2147483647
|
||||
int32_t VEN_00E6; // Usage 0xFF0700E6: , Value = -2147483648 to 2147483647
|
||||
int32_t VEN_00E7; // Usage 0xFF0700E7: , Value = -2147483648 to 2147483647
|
||||
} inputReport2E_t;
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Vendor-defined inputReport 2F (Device --> Host)
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t reportId; // Report ID = 0x2F (47)
|
||||
// Collection: CA:
|
||||
int32_t VEN_00F0; // Usage 0xFF0700F0: , Value = -2147483648 to 2147483647
|
||||
int32_t VEN_00F1; // Usage 0xFF0700F1: , Value = -2147483648 to 2147483647
|
||||
int32_t VEN_00F2; // Usage 0xFF0700F2: , Value = -2147483648 to 2147483647
|
||||
int32_t VEN_00F3; // Usage 0xFF0700F3: , Value = -2147483648 to 2147483647
|
||||
} inputReport2F_t;
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Vendor-defined outputReport 2A (Device <-- Host)
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t reportId; // Report ID = 0x2A (42)
|
||||
// Collection: CA:
|
||||
uint8_t VEN_00C7[72]; // Usage 0xFF0700C7: , Value = 0 to -1
|
||||
} outputReport2A_t;
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Vendor-defined outputReport 2B (Device <-- Host)
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t reportId; // Report ID = 0x2B (43)
|
||||
// Collection: CA:
|
||||
uint8_t VEN_00CA[52]; // Usage 0xFF0700CA: , Value = 0 to -1
|
||||
} outputReport2B_t;
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Vendor-defined outputReport 2D (Device <-- Host)
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t reportId; // Report ID = 0x2D (45)
|
||||
// Collection: CA:
|
||||
int32_t VEN_00DC; // Usage 0xFF0700DC: , Value = -2147483648 to 2147483647
|
||||
int32_t VEN_00DD; // Usage 0xFF0700DD: , Value = -2147483648 to 2147483647
|
||||
int32_t VEN_00DE; // Usage 0xFF0700DE: , Value = -2147483648 to 2147483647
|
||||
int32_t VEN_00DF; // Usage 0xFF0700DF: , Value = -2147483648 to 2147483647
|
||||
} outputReport2D_t;
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Vendor-defined outputReport 2E (Device <-- Host)
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t reportId; // Report ID = 0x2E (46)
|
||||
// Collection: CA:
|
||||
int32_t VEN_00E8; // Usage 0xFF0700E8: , Value = -2147483648 to 2147483647
|
||||
int32_t VEN_00E9; // Usage 0xFF0700E9: , Value = -2147483648 to 2147483647
|
||||
int32_t VEN_00EA; // Usage 0xFF0700EA: , Value = -2147483648 to 2147483647
|
||||
int32_t VEN_00EB; // Usage 0xFF0700EB: , Value = -2147483648 to 2147483647
|
||||
} outputReport2E_t;
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Vendor-defined outputReport 2F (Device <-- Host)
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t reportId; // Report ID = 0x2F (47)
|
||||
// Collection: CA:
|
||||
int32_t VEN_00F4; // Usage 0xFF0700F4: , Value = -2147483648 to 2147483647
|
||||
int32_t VEN_00F5; // Usage 0xFF0700F5: , Value = -2147483648 to 2147483647
|
||||
int32_t VEN_00F6; // Usage 0xFF0700F6: , Value = -2147483648 to 2147483647
|
||||
int32_t VEN_00F7; // Usage 0xFF0700F7: , Value = -2147483648 to 2147483647
|
||||
} outputReport2F_t;
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Decoded Application Collection
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
05 01 (GLOBAL) USAGE_PAGE 0x0001 Generic Desktop Page
|
||||
09 80 (LOCAL) USAGE 0x00010080 System Control (Application Collection)
|
||||
A1 01 (MAIN) COLLECTION 0x01 Application (Usage=0x00010080: Page=Generic Desktop Page, Usage=System Control, Type=Application Collection)
|
||||
85 32 (GLOBAL) REPORT_ID 0x32 (50) '2'
|
||||
09 82 (LOCAL) USAGE 0x00010082 System Sleep (One Shot Control)
|
||||
09 83 (LOCAL) USAGE 0x00010083 System Wake Up (One Shot Control)
|
||||
15 00 (GLOBAL) LOGICAL_MINIMUM 0x00 (0) <-- Info: Consider replacing 15 00 with 14
|
||||
25 01 (GLOBAL) LOGICAL_MAXIMUM 0x01 (1)
|
||||
95 02 (GLOBAL) REPORT_COUNT 0x02 (2) Number of fields
|
||||
75 01 (GLOBAL) REPORT_SIZE 0x01 (1) Number of bits per field
|
||||
81 02 (MAIN) INPUT 0x00000002 (2 fields x 1 bit) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
|
||||
95 06 (GLOBAL) REPORT_COUNT 0x06 (6) Number of fields
|
||||
81 03 (MAIN) INPUT 0x00000003 (6 fields x 1 bit) 1=Constant 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap
|
||||
C0 (MAIN) END_COLLECTION Application
|
||||
*/
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Generic Desktop Page inputReport 32 (Device --> Host)
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t reportId; // Report ID = 0x32 (50) '2'
|
||||
// Collection: CA:SystemControl
|
||||
uint8_t GD_SystemControlSystemSleep : 1; // Usage 0x00010082: System Sleep, Value = 0 to 1
|
||||
uint8_t GD_SystemControlSystemWakeUp : 1; // Usage 0x00010083: System Wake Up, Value = 0 to 1
|
||||
uint8_t : 1; // Pad
|
||||
uint8_t : 1; // Pad
|
||||
uint8_t : 1; // Pad
|
||||
uint8_t : 1; // Pad
|
||||
uint8_t : 1; // Pad
|
||||
uint8_t : 1; // Pad
|
||||
} inputReport32_t;
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Decoded Application Collection
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
/*
|
||||
09 72 (LOCAL) USAGE 0x00010072 <-- Warning: Undocumented usage (document it by inserting 0072 into file 0001.conf)
|
||||
A1 01 (MAIN) COLLECTION 0x01 Application (Usage=0x00010072: Page=Generic Desktop Page, Usage=, Type=) <-- Error: COLLECTION must be preceded by a known USAGE <-- Warning: USAGE type should be CA (Application Collection)
|
||||
85 31 (GLOBAL) REPORT_ID 0x31 (49) '1'
|
||||
95 0A (GLOBAL) REPORT_COUNT 0x0A (10) Number of fields
|
||||
75 08 (GLOBAL) REPORT_SIZE 0x08 (8) Number of bits per field
|
||||
15 00 (GLOBAL) LOGICAL_MINIMUM 0x00 (0) <-- Redundant: LOGICAL_MINIMUM is already 0 <-- Info: Consider replacing 15 00 with 14
|
||||
25 FF (GLOBAL) LOGICAL_MAXIMUM 0xFF (-1)
|
||||
09 C6 (LOCAL) USAGE 0x000100C6 Wireless Radio Button (On/Off Control)
|
||||
81 02 (MAIN) INPUT 0x00000002 (10 fields x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap <-- Error: LOGICAL_MAXIMUM (-1) is less than LOGICAL_MINIMUM (0)
|
||||
09 C7 (LOCAL) USAGE 0x000100C7 Wireless Radio LED (On/Off Control)
|
||||
91 02 (MAIN) OUTPUT 0x00000002 (10 fields x 8 bits) 0=Data 1=Variable 0=Absolute 0=NoWrap 0=Linear 0=PrefState 0=NoNull 0=NonVolatile 0=Bitmap <-- Error: LOGICAL_MAXIMUM (-1) is less than LOGICAL_MINIMUM (0)
|
||||
C0 (MAIN) END_COLLECTION Application
|
||||
*/
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Generic Desktop Page inputReport 31 (Device --> Host)
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t reportId; // Report ID = 0x31 (49) '1'
|
||||
// Collection: CA:
|
||||
uint8_t GD_WirelessRadioButton[10]; // Usage 0x000100C6: Wireless Radio Button, Value = 0 to -1
|
||||
} inputReport31_t;
|
||||
|
||||
|
||||
//--------------------------------------------------------------------------------
|
||||
// Generic Desktop Page outputReport 31 (Device <-- Host)
|
||||
//--------------------------------------------------------------------------------
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t reportId; // Report ID = 0x31 (49) '1'
|
||||
// Collection: CA:
|
||||
uint8_t GD_WirelessRadioLed[10]; // Usage 0x000100C7: Wireless Radio LED, Value = 0 to -1
|
||||
} outputReport31_t;
|
||||
|
||||
@@ -1,38 +1,3 @@
|
||||
pub enum DialDir {
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
pub struct ThresholdHelper {
|
||||
sensitivity: i32,
|
||||
pos: i32,
|
||||
}
|
||||
|
||||
impl ThresholdHelper {
|
||||
pub fn new(sensitivity: i32) -> ThresholdHelper {
|
||||
ThresholdHelper {
|
||||
sensitivity,
|
||||
pos: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, delta: i32) -> Option<DialDir> {
|
||||
self.pos += delta;
|
||||
|
||||
if self.pos > self.sensitivity {
|
||||
self.pos -= self.sensitivity;
|
||||
return Some(DialDir::Right);
|
||||
}
|
||||
|
||||
if self.pos < -self.sensitivity {
|
||||
self.pos += self.sensitivity;
|
||||
return Some(DialDir::Left);
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
use notify_rust::error::Result as NotifyResult;
|
||||
use notify_rust::{Hint, Notification, NotificationHandle, Timeout};
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ use std::thread::JoinHandle;
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::controller::ControlMode;
|
||||
use crate::dial_device::DialHaptics;
|
||||
use crate::fake_input::FakeInput;
|
||||
use crate::DynResult;
|
||||
|
||||
@@ -106,12 +107,6 @@ impl Drop for DPad {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DPad {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl DPad {
|
||||
pub fn new() -> DPad {
|
||||
let (msg_tx, msg_rx) = mpsc::channel();
|
||||
@@ -128,17 +123,22 @@ impl DPad {
|
||||
}
|
||||
|
||||
impl ControlMode for DPad {
|
||||
fn on_btn_press(&mut self) -> DynResult<()> {
|
||||
fn on_start(&mut self, haptics: &DialHaptics) -> DynResult<()> {
|
||||
haptics.set_mode(false, Some(3600))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_btn_press(&mut self, _: &DialHaptics) -> DynResult<()> {
|
||||
eprintln!("space");
|
||||
self.fake_input.key_click(&[EV_KEY::KEY_SPACE])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_btn_release(&mut self) -> DynResult<()> {
|
||||
fn on_btn_release(&mut self, _: &DialHaptics) -> DynResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_dial(&mut self, delta: i32) -> DynResult<()> {
|
||||
fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> DynResult<()> {
|
||||
self.msg.send(Msg::Delta(delta))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,47 +1,44 @@
|
||||
use crate::common::{DialDir, ThresholdHelper};
|
||||
use crate::controller::ControlMode;
|
||||
use crate::dial_device::DialHaptics;
|
||||
use crate::fake_input::FakeInput;
|
||||
use crate::DynResult;
|
||||
|
||||
use evdev_rs::enums::EV_KEY;
|
||||
|
||||
pub struct Media {
|
||||
thresh: ThresholdHelper,
|
||||
|
||||
fake_input: FakeInput,
|
||||
}
|
||||
|
||||
impl Media {
|
||||
pub fn new(sensitivity: i32) -> Media {
|
||||
pub fn new() -> Media {
|
||||
Media {
|
||||
thresh: ThresholdHelper::new(sensitivity),
|
||||
|
||||
fake_input: FakeInput::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ControlMode for Media {
|
||||
fn on_btn_press(&mut self) -> DynResult<()> {
|
||||
fn on_start(&mut self, haptics: &DialHaptics) -> DynResult<()> {
|
||||
haptics.set_mode(false, Some(36))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_btn_release(&mut self) -> DynResult<()> {
|
||||
fn on_btn_press(&mut self, _: &DialHaptics) -> DynResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_btn_release(&mut self, _: &DialHaptics) -> DynResult<()> {
|
||||
self.fake_input.key_click(&[EV_KEY::KEY_PLAYPAUSE])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_dial(&mut self, delta: i32) -> DynResult<()> {
|
||||
match self.thresh.update(delta) {
|
||||
Some(DialDir::Left) => {
|
||||
eprintln!("next song");
|
||||
self.fake_input.key_click(&[EV_KEY::KEY_NEXTSONG])?;
|
||||
}
|
||||
Some(DialDir::Right) => {
|
||||
fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> DynResult<()> {
|
||||
if delta > 0 {
|
||||
eprintln!("last song");
|
||||
self.fake_input.key_click(&[EV_KEY::KEY_PREVIOUSSONG])?;
|
||||
}
|
||||
None => {}
|
||||
} else {
|
||||
eprintln!("next song");
|
||||
self.fake_input.key_click(&[EV_KEY::KEY_NEXTSONG])?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
mod dpad;
|
||||
mod media;
|
||||
mod null;
|
||||
mod scroll_zoom;
|
||||
mod volume;
|
||||
|
||||
pub use self::dpad::*;
|
||||
pub use self::media::*;
|
||||
pub use self::null::*;
|
||||
pub use self::scroll_zoom::*;
|
||||
pub use self::volume::*;
|
||||
|
||||
30
src/controller/controls/null.rs
Normal file
30
src/controller/controls/null.rs
Normal file
@@ -0,0 +1,30 @@
|
||||
use crate::controller::ControlMode;
|
||||
use crate::dial_device::DialHaptics;
|
||||
use crate::DynResult;
|
||||
|
||||
pub struct Null {}
|
||||
|
||||
impl Null {
|
||||
pub fn new() -> Null {
|
||||
Null {}
|
||||
}
|
||||
}
|
||||
|
||||
impl ControlMode for Null {
|
||||
fn on_start(&mut self, haptics: &DialHaptics) -> DynResult<()> {
|
||||
haptics.set_mode(false, Some(0))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_btn_press(&mut self, _haptics: &DialHaptics) -> DynResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_btn_release(&mut self, _haptics: &DialHaptics) -> DynResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_dial(&mut self, _haptics: &DialHaptics, _delta: i32) -> DynResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,20 @@
|
||||
use crate::common::{action_notification, DialDir, ThresholdHelper};
|
||||
use crate::common::action_notification;
|
||||
use crate::controller::ControlMode;
|
||||
use crate::dial_device::DialHaptics;
|
||||
use crate::fake_input::{FakeInput, ScrollStep};
|
||||
use crate::DynResult;
|
||||
|
||||
use evdev_rs::enums::EV_KEY;
|
||||
|
||||
pub struct ScrollZoom {
|
||||
thresh: ThresholdHelper,
|
||||
zoom: bool,
|
||||
|
||||
fake_input: FakeInput,
|
||||
}
|
||||
|
||||
impl ScrollZoom {
|
||||
pub fn new(sensitivity: i32) -> ScrollZoom {
|
||||
pub fn new() -> ScrollZoom {
|
||||
ScrollZoom {
|
||||
thresh: ThresholdHelper::new(sensitivity),
|
||||
zoom: false,
|
||||
|
||||
fake_input: FakeInput::new(),
|
||||
@@ -23,26 +22,45 @@ impl ScrollZoom {
|
||||
}
|
||||
}
|
||||
|
||||
const ZOOM_SENSITIVITY: u16 = 36;
|
||||
const SCROLL_SENSITIVITY: u16 = 90;
|
||||
|
||||
impl ControlMode for ScrollZoom {
|
||||
fn on_btn_press(&mut self) -> DynResult<()> {
|
||||
fn on_start(&mut self, haptics: &DialHaptics) -> DynResult<()> {
|
||||
haptics.set_mode(false, Some(SCROLL_SENSITIVITY))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_btn_release(&mut self) -> DynResult<()> {
|
||||
fn on_btn_press(&mut self, _: &DialHaptics) -> DynResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_btn_release(&mut self, haptics: &DialHaptics) -> DynResult<()> {
|
||||
self.zoom = !self.zoom;
|
||||
haptics.buzz(1)?;
|
||||
|
||||
if self.zoom {
|
||||
action_notification("Zoom Mode", "zoom-in")?;
|
||||
haptics.set_mode(false, Some(ZOOM_SENSITIVITY))?;
|
||||
} else {
|
||||
action_notification("ScrollZoom Mode", "input-mouse")?;
|
||||
haptics.set_mode(false, Some(SCROLL_SENSITIVITY))?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_dial(&mut self, delta: i32) -> DynResult<()> {
|
||||
match self.thresh.update(delta) {
|
||||
None => {}
|
||||
Some(DialDir::Left) => {
|
||||
fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> DynResult<()> {
|
||||
if delta > 0 {
|
||||
if self.zoom {
|
||||
eprintln!("zoom in");
|
||||
self.fake_input
|
||||
.key_click(&[EV_KEY::KEY_LEFTCTRL, EV_KEY::KEY_EQUAL])?;
|
||||
} else {
|
||||
eprintln!("scroll down");
|
||||
self.fake_input.scroll_step(ScrollStep::Down)?;
|
||||
}
|
||||
} else {
|
||||
if self.zoom {
|
||||
eprintln!("zoom out");
|
||||
self.fake_input
|
||||
@@ -52,17 +70,7 @@ impl ControlMode for ScrollZoom {
|
||||
self.fake_input.scroll_step(ScrollStep::Up)?;
|
||||
}
|
||||
}
|
||||
Some(DialDir::Right) => {
|
||||
if self.zoom {
|
||||
eprintln!("zoom in");
|
||||
self.fake_input
|
||||
.key_click(&[EV_KEY::KEY_LEFTCTRL, EV_KEY::KEY_EQUAL])?;
|
||||
} else {
|
||||
eprintln!("scroll down");
|
||||
self.fake_input.scroll_step(ScrollStep::Down)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,53 +1,51 @@
|
||||
use crate::common::{DialDir, ThresholdHelper};
|
||||
use crate::controller::ControlMode;
|
||||
use crate::dial_device::DialHaptics;
|
||||
use crate::fake_input::FakeInput;
|
||||
use crate::DynResult;
|
||||
|
||||
use evdev_rs::enums::EV_KEY;
|
||||
|
||||
pub struct Volume {
|
||||
thresh: ThresholdHelper,
|
||||
|
||||
fake_input: FakeInput,
|
||||
}
|
||||
|
||||
impl Volume {
|
||||
pub fn new(sensitivity: i32) -> Volume {
|
||||
pub fn new() -> Volume {
|
||||
Volume {
|
||||
thresh: ThresholdHelper::new(sensitivity),
|
||||
|
||||
fake_input: FakeInput::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ControlMode for Volume {
|
||||
fn on_btn_press(&mut self) -> DynResult<()> {
|
||||
fn on_start(&mut self, haptics: &DialHaptics) -> DynResult<()> {
|
||||
haptics.set_mode(false, Some(36 * 2))?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_btn_press(&mut self, _: &DialHaptics) -> DynResult<()> {
|
||||
// TODO: support double-click to mute
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_btn_release(&mut self) -> DynResult<()> {
|
||||
fn on_btn_release(&mut self, _: &DialHaptics) -> DynResult<()> {
|
||||
eprintln!("play/pause");
|
||||
// self.fake_input.mute()?
|
||||
self.fake_input.key_click(&[EV_KEY::KEY_PLAYPAUSE])?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn on_dial(&mut self, delta: i32) -> DynResult<()> {
|
||||
match self.thresh.update(delta) {
|
||||
Some(DialDir::Left) => {
|
||||
eprintln!("volume down");
|
||||
self.fake_input
|
||||
.key_click(&[EV_KEY::KEY_LEFTSHIFT, EV_KEY::KEY_VOLUMEDOWN])?
|
||||
}
|
||||
Some(DialDir::Right) => {
|
||||
fn on_dial(&mut self, _: &DialHaptics, delta: i32) -> DynResult<()> {
|
||||
if delta > 0 {
|
||||
eprintln!("volume up");
|
||||
self.fake_input
|
||||
.key_click(&[EV_KEY::KEY_LEFTSHIFT, EV_KEY::KEY_VOLUMEUP])?
|
||||
}
|
||||
None => {}
|
||||
.key_click(&[EV_KEY::KEY_LEFTSHIFT, EV_KEY::KEY_VOLUMEUP])?;
|
||||
} else {
|
||||
eprintln!("volume down");
|
||||
self.fake_input
|
||||
.key_click(&[EV_KEY::KEY_LEFTSHIFT, EV_KEY::KEY_VOLUMEDOWN])?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
use crate::DynResult;
|
||||
|
||||
use crate::dial_device::{DialDevice, DialEventKind};
|
||||
use crate::dial_device::{DialDevice, DialEventKind, DialHaptics};
|
||||
|
||||
pub mod controls;
|
||||
|
||||
pub trait ControlMode {
|
||||
fn on_btn_press(&mut self) -> DynResult<()>;
|
||||
fn on_btn_release(&mut self) -> DynResult<()>;
|
||||
fn on_dial(&mut self, delta: i32) -> DynResult<()>;
|
||||
fn on_start(&mut self, haptics: &DialHaptics) -> DynResult<()>;
|
||||
fn on_btn_press(&mut self, haptics: &DialHaptics) -> DynResult<()>;
|
||||
fn on_btn_release(&mut self, haptics: &DialHaptics) -> DynResult<()>;
|
||||
fn on_dial(&mut self, haptics: &DialHaptics, delta: i32) -> DynResult<()>;
|
||||
}
|
||||
|
||||
pub struct DialController {
|
||||
@@ -26,16 +27,20 @@ impl DialController {
|
||||
}
|
||||
|
||||
pub fn run(&mut self) -> DynResult<()> {
|
||||
let haptics = self.device.haptics();
|
||||
|
||||
self.mode.on_start(haptics)?;
|
||||
|
||||
loop {
|
||||
let evt = self.device.next_event()?;
|
||||
|
||||
// TODO: press and hold + rotate to switch between modes
|
||||
// TODO: press and hold (+ rotate?) to switch between modes
|
||||
|
||||
match evt.kind {
|
||||
DialEventKind::Ignored => {}
|
||||
DialEventKind::ButtonPress => self.mode.on_btn_press()?,
|
||||
DialEventKind::ButtonRelease => self.mode.on_btn_release()?,
|
||||
DialEventKind::Dial(delta) => self.mode.on_dial(delta)?,
|
||||
DialEventKind::ButtonPress => self.mode.on_btn_press(haptics)?,
|
||||
DialEventKind::ButtonRelease => self.mode.on_btn_release(haptics)?,
|
||||
DialEventKind::Dial(delta) => self.mode.on_dial(haptics, delta)?,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ use std::fs;
|
||||
use std::time::Duration;
|
||||
|
||||
use evdev_rs::{Device, InputEvent};
|
||||
use hidapi::{HidApi, HidDevice};
|
||||
|
||||
use crate::error::Error;
|
||||
|
||||
@@ -9,6 +10,7 @@ pub struct DialDevice {
|
||||
// TODO: explore what the control channel can be used for...
|
||||
_control: Device,
|
||||
axis: Device,
|
||||
haptics: DialHaptics,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -30,6 +32,7 @@ impl DialDevice {
|
||||
let mut control = None;
|
||||
let mut axis = None;
|
||||
|
||||
// discover the evdev devices
|
||||
for e in fs::read_dir("/dev/input/").map_err(Error::OpenDevInputDir)? {
|
||||
let e = e.map_err(Error::OpenDevInputDir)?;
|
||||
if !e.file_name().to_str().unwrap().starts_with("event") {
|
||||
@@ -62,6 +65,7 @@ impl DialDevice {
|
||||
Ok(DialDevice {
|
||||
_control: control.ok_or(Error::MissingDial)?,
|
||||
axis: axis.ok_or(Error::MissingDial)?,
|
||||
haptics: DialHaptics::new()?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -79,6 +83,10 @@ impl DialDevice {
|
||||
|
||||
Ok(event)
|
||||
}
|
||||
|
||||
pub fn haptics(&self) -> &DialHaptics {
|
||||
&self.haptics
|
||||
}
|
||||
}
|
||||
|
||||
impl DialEvent {
|
||||
@@ -110,3 +118,67 @@ impl DialEvent {
|
||||
Some(evt)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct DialHaptics {
|
||||
hid_device: HidDevice,
|
||||
}
|
||||
|
||||
impl DialHaptics {
|
||||
fn new() -> Result<DialHaptics, Error> {
|
||||
let api = HidApi::new().map_err(Error::HidError)?;
|
||||
let hid_device = api.open(0x045e, 0x091b).map_err(|_| Error::MissingDial)?;
|
||||
|
||||
// let mut buf = [0; 256];
|
||||
|
||||
// buf[0] = 1;
|
||||
// let len = device
|
||||
// .get_feature_report(&mut buf)
|
||||
// .map_err(Error::HidError)?;
|
||||
// eprintln!("1: {:02x?}", &buf[..len]);
|
||||
|
||||
// buf[0] = 2;
|
||||
// let len = device
|
||||
// .get_feature_report(&mut buf)
|
||||
// .map_err(Error::HidError)?;
|
||||
// eprintln!("2: {:02x?}", &buf[..len]);
|
||||
|
||||
Ok(DialHaptics { hid_device })
|
||||
}
|
||||
|
||||
/// `steps` should be a value between 0 and 3600, which corresponds to the
|
||||
/// number of subdivisions the dial should use. If left unspecified, this
|
||||
/// defaults to 36 (an arbitrary choice that "feels good" most of the time)
|
||||
pub fn set_mode(&self, haptics: bool, steps: Option<u16>) -> Result<(), Error> {
|
||||
let steps = steps.unwrap_or(36);
|
||||
assert!(steps <= 3600);
|
||||
|
||||
let steps_lo = steps & 0xff;
|
||||
let steps_hi = (steps >> 8) & 0xff;
|
||||
|
||||
let mut buf = [0; 8];
|
||||
buf[0] = 1;
|
||||
buf[1] = steps_lo as u8; // steps
|
||||
buf[2] = steps_hi as u8; // steps
|
||||
buf[3] = 0x00; // Repeat Count
|
||||
buf[4] = if haptics { 0x03 } else { 0x02 }; // auto trigger
|
||||
buf[5] = 0x00; // Waveform Cutoff Time
|
||||
buf[6] = 0x00; // retrigger period
|
||||
buf[7] = 0x00; // retrigger period
|
||||
self.hid_device
|
||||
.send_feature_report(&buf[..8])
|
||||
.map_err(Error::HidError)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn buzz(&self, repeat: u8) -> Result<(), Error> {
|
||||
let mut buf = [0; 5];
|
||||
buf[0] = 0x01; // Report ID
|
||||
buf[1] = repeat; // RepeatCount
|
||||
buf[2] = 0x03; // ManualTrigger
|
||||
buf[3] = 0x00; // RetriggerPeriod (lo)
|
||||
buf[4] = 0x00; // RetriggerPeriod (hi)
|
||||
self.hid_device.write(&buf).map_err(Error::HidError)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ use evdev_rs::InputEvent;
|
||||
pub enum Error {
|
||||
OpenDevInputDir(io::Error),
|
||||
OpenEventFile(std::path::PathBuf, io::Error),
|
||||
HidError(hidapi::HidError),
|
||||
MissingDial,
|
||||
MultipleDials,
|
||||
UnexpectedEvt(InputEvent),
|
||||
@@ -19,6 +20,7 @@ impl fmt::Display for Error {
|
||||
match self {
|
||||
Error::OpenDevInputDir(e) => write!(f, "Could not open /dev/input directory: {}", e),
|
||||
Error::OpenEventFile(path, e) => write!(f, "Could not open {:?}: {}", path, e),
|
||||
Error::HidError(e) => write!(f, "HID API Error: {}", e),
|
||||
Error::MissingDial => write!(f, "Could not find the Surface Dial"),
|
||||
Error::MultipleDials => write!(f, "Found multiple dials"),
|
||||
Error::UnexpectedEvt(evt) => write!(f, "Unexpected event: {:?}", evt),
|
||||
|
||||
31
src/main.rs
31
src/main.rs
@@ -1,3 +1,5 @@
|
||||
#![allow(clippy::collapsible_if, clippy::new_without_default)]
|
||||
|
||||
mod common;
|
||||
pub mod controller;
|
||||
mod dial_device;
|
||||
@@ -10,6 +12,9 @@ use crate::controller::DialController;
|
||||
use crate::dial_device::DialDevice;
|
||||
use crate::error::Error;
|
||||
|
||||
use notify_rust::{Hint, Notification, Timeout};
|
||||
use signal_hook::{iterator::Signals, SIGINT, SIGTERM};
|
||||
|
||||
fn main() {
|
||||
if let Err(e) = true_main() {
|
||||
println!("{}", e);
|
||||
@@ -22,11 +27,29 @@ fn true_main() -> DynResult<()> {
|
||||
let dial = DialDevice::new()?;
|
||||
println!("Found the dial.");
|
||||
|
||||
common::action_notification("Active!", "input-mouse")?;
|
||||
std::thread::spawn(move || {
|
||||
let active_notification = Notification::new()
|
||||
.hint(Hint::Resident(true))
|
||||
.hint(Hint::Category("device".into()))
|
||||
.timeout(Timeout::Never)
|
||||
.summary("Surface Dial")
|
||||
.body("Active!")
|
||||
.icon("input-mouse")
|
||||
.show()
|
||||
.expect("failed to send notification");
|
||||
|
||||
let default_mode = Box::new(controller::controls::ScrollZoom::new(30));
|
||||
// let default_mode = Box::new(controller::controls::Volume::new(30));
|
||||
// let default_mode = Box::new(controller::controls::Media::new(50));
|
||||
let signals = Signals::new(&[SIGTERM, SIGINT]).unwrap();
|
||||
for sig in signals.forever() {
|
||||
eprintln!("received signal {:?}", sig);
|
||||
active_notification.close();
|
||||
std::process::exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
// let default_mode = Box::new(controller::controls::Null::new());
|
||||
let default_mode = Box::new(controller::controls::ScrollZoom::new());
|
||||
// let default_mode = Box::new(controller::controls::Volume::new());
|
||||
// let default_mode = Box::new(controller::controls::Media::new());
|
||||
// let default_mode = Box::new(controller::controls::DPad::new());
|
||||
|
||||
let mut controller = DialController::new(dial, default_mode);
|
||||
|
||||
Reference in New Issue
Block a user