hidpp/feature/mouse_pointer/
mod.rs1use std::sync::Arc;
5
6use num_enum::{IntoPrimitive, TryFromPrimitive};
7
8use crate::{
9 channel::HidppChannel,
10 feature::{CreatableFeature, Feature, FeatureEndpoint},
11 protocol::v20::Hidpp20Error,
12};
13
14#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
20#[cfg_attr(feature = "serde", derive(serde::Serialize))]
21#[non_exhaustive]
22#[repr(u8)]
23pub enum PointerAcceleration {
24 None = 0,
26 Low = 1,
28 Medium = 2,
30 High = 3,
32}
33
34#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
37#[cfg_attr(feature = "serde", derive(serde::Serialize))]
38#[non_exhaustive]
39pub struct MousePointerInfo {
40 pub sensor_resolution: u16,
45
46 pub pointer_acceleration: PointerAcceleration,
48
49 pub suggest_os_ballistics: bool,
54
55 pub suggest_vertical_tuning: bool,
60}
61
62#[derive(Clone)]
64pub struct MousePointerFeature {
65 endpoint: FeatureEndpoint,
67}
68
69impl CreatableFeature for MousePointerFeature {
70 const ID: u16 = 0x2200;
71 const STARTING_VERSION: u8 = 0;
72
73 fn new(chan: Arc<HidppChannel>, device_index: u8, feature_index: u8) -> Self {
74 Self {
75 endpoint: FeatureEndpoint::new(chan, device_index, feature_index),
76 }
77 }
78}
79
80impl Feature for MousePointerFeature {}
81
82impl MousePointerFeature {
83 pub async fn get_mouse_pointer_info(&self) -> Result<MousePointerInfo, Hidpp20Error> {
85 let payload = self.endpoint.call(0, [0; 3]).await?.extend_payload();
86 MousePointerInfo::from_payload(payload)
87 }
88}
89
90impl MousePointerInfo {
91 fn from_payload(payload: [u8; 16]) -> Result<Self, Hidpp20Error> {
93 let flags = payload[2];
94 Ok(Self {
95 sensor_resolution: u16::from_be_bytes([payload[0], payload[1]]),
96 pointer_acceleration: PointerAcceleration::try_from(flags & 0b11)
99 .map_err(|_| Hidpp20Error::UnsupportedResponse)?,
100 suggest_os_ballistics: flags & (1 << 2) != 0,
101 suggest_vertical_tuning: flags & (1 << 3) != 0,
102 })
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use super::{MousePointerInfo, PointerAcceleration};
109
110 #[test]
111 fn decodes_resolution_and_flags() {
112 let mut payload = [0; 16];
113 payload[0..2].copy_from_slice(&1600u16.to_be_bytes());
114 payload[2] = 0b0000_0111;
116
117 let info = MousePointerInfo::from_payload(payload).unwrap();
118 assert_eq!(info.sensor_resolution, 1600);
119 assert_eq!(info.pointer_acceleration, PointerAcceleration::High);
120 assert!(info.suggest_os_ballistics);
121 assert!(!info.suggest_vertical_tuning);
122 }
123
124 #[test]
125 fn decodes_trackball_vertical_tuning() {
126 let mut payload = [0; 16];
127 payload[0..2].copy_from_slice(&400u16.to_be_bytes());
128 payload[2] = 0b0000_1000;
130
131 let info = MousePointerInfo::from_payload(payload).unwrap();
132 assert_eq!(info.pointer_acceleration, PointerAcceleration::None);
133 assert!(!info.suggest_os_ballistics);
134 assert!(info.suggest_vertical_tuning);
135 }
136}