1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
//! Implements the `TouchpadRawXy` feature (ID `0x6100`) that exposes a
//! touchpad's raw multi-touch data: pad characteristics, the raw-report mode,
//! and a per-frame [`DualXyData`] event.
pub mod event;
#[cfg(test)]
mod tests;
use num_enum::{IntoPrimitive, TryFromPrimitive};
use openlogi_hidpp_derive::Feature;
pub use event::{DualXyData, TouchPoint, TouchpadRawEvent};
use crate::{
feature::{EventSource, FeatureEndpoint},
protocol::v20::Hidpp20Error,
};
bitflags::bitflags! {
/// Raw-report mode flags from
/// [`get_raw_report_state`](TouchpadRawXyFeature::get_raw_report_state).
///
/// Some combinations are mutually exclusive; common valid bitmaps are `0x00`
/// (off), `0x05`, `0x09`, `0x21` and `0x41` (see the feature spec).
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct RawReportFlags: u8 {
/// Raw reporting enabled.
const RAW = 1 << 0;
/// Add force data to 16-bit reporting (deprecated).
const FORCE_ADD = 1 << 1;
/// Enhanced reporting enabled.
const ENHANCED = 1 << 2;
/// Report width/height instead of area.
const WIDTH_HEIGHT = 1 << 3;
/// Report native gestures.
const NATIVE_GESTURE = 1 << 4;
/// Report major/minor/orientation.
const MAJOR_MINOR = 1 << 5;
/// Report 8-bit width and height bytes instead of area.
const WIDTH_HEIGHT_8BIT = 1 << 6;
}
}
/// The position of a touchpad's coordinate origin, viewed from above.
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
#[repr(u8)]
pub enum Origin {
/// Lower-left corner.
LowerLeft = 1,
/// Lower-right corner.
LowerRight = 2,
/// Upper-left corner.
UpperLeft = 3,
/// Upper-right corner.
UpperRight = 4,
}
/// Touchpad characteristics from
/// [`get_touchpad_info`](TouchpadRawXyFeature::get_touchpad_info).
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct TouchpadInfo {
/// Pad width in native coordinate units.
pub x_size: u16,
/// Pad height in native coordinate units.
pub y_size: u16,
/// Z-data range (`0x00` = none, `0x0f` = 16-bit).
pub z_data_range: u8,
/// Area-data range (`0x0f` = 16-bit).
pub area_data_range: u8,
/// Timestamp increment, in units of 0.1 ms.
pub timestamp_units: u8,
/// Maximum number of fingers that can be tracked.
pub max_finger_count: u8,
/// Position of the coordinate origin.
pub origin: Origin,
/// Whether pen input is supported.
pub pen_support: bool,
/// Raw-report mapping version.
pub raw_report_mapping_version: u8,
/// Native sensor DPI.
pub dpi: u16,
}
impl TouchpadInfo {
fn from_payload(payload: &[u8; 16]) -> Result<Self, Hidpp20Error> {
Ok(Self {
x_size: u16::from_be_bytes([payload[0], payload[1]]),
y_size: u16::from_be_bytes([payload[2], payload[3]]),
z_data_range: payload[4],
area_data_range: payload[5],
timestamp_units: payload[6],
max_finger_count: payload[7],
origin: Origin::try_from(payload[8]).map_err(|_| Hidpp20Error::UnsupportedResponse)?,
pen_support: payload[9] != 0,
raw_report_mapping_version: payload[12],
dpi: u16::from_be_bytes([payload[13], payload[14]]),
})
}
}
/// Implements the `TouchpadRawXy` / `0x6100` feature.
#[derive(Feature)]
#[creatable(id = 0x6100, version = 0)]
pub struct TouchpadRawXyFeature {
/// The endpoint this feature talks to.
endpoint: FeatureEndpoint,
/// Publishes decoded events to listeners.
events: EventSource<TouchpadRawEvent>,
}
impl TouchpadRawXyFeature {
/// Retrieves the touchpad's characteristics.
pub async fn get_touchpad_info(&self) -> Result<TouchpadInfo, Hidpp20Error> {
let payload = self.endpoint.call(0, [0; 3]).await?.extend_payload();
TouchpadInfo::from_payload(&payload)
}
/// Retrieves the current raw-report mode.
pub async fn get_raw_report_state(&self) -> Result<RawReportFlags, Hidpp20Error> {
let payload = self.endpoint.call(1, [0; 3]).await?.extend_payload();
Ok(RawReportFlags::from_bits_retain(payload[0]))
}
/// Sets the raw-report mode.
///
/// Enable [`RawReportFlags::RAW`] for [`TouchpadRawEvent`]s to be emitted.
pub async fn set_raw_report_state(&self, flags: RawReportFlags) -> Result<(), Hidpp20Error> {
self.endpoint.call(2, [flags.bits(), 0, 0]).await?;
Ok(())
}
}