hidpp/feature/
mouse_pointer.rs1use num_enum::{IntoPrimitive, TryFromPrimitive};
5use openlogi_hidpp_derive::Feature;
6
7use crate::{feature::FeatureEndpoint, protocol::v20::Hidpp20Error};
8
9#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
15#[cfg_attr(feature = "serde", derive(serde::Serialize))]
16#[non_exhaustive]
17#[repr(u8)]
18pub enum PointerAcceleration {
19 None = 0,
21 Low = 1,
23 Medium = 2,
25 High = 3,
27}
28
29#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
32#[cfg_attr(feature = "serde", derive(serde::Serialize))]
33#[non_exhaustive]
34pub struct MousePointerInfo {
35 pub sensor_resolution: u16,
40
41 pub pointer_acceleration: PointerAcceleration,
43
44 pub suggest_os_ballistics: bool,
49
50 pub suggest_vertical_tuning: bool,
55}
56
57#[derive(Clone, Feature)]
59#[creatable(id = 0x2200, version = 0)]
60pub struct MousePointerFeature {
61 endpoint: FeatureEndpoint,
63}
64
65impl MousePointerFeature {
66 pub async fn get_mouse_pointer_info(&self) -> Result<MousePointerInfo, Hidpp20Error> {
68 let payload = self.endpoint.call(0, [0; 3]).await?.extend_payload();
69 MousePointerInfo::from_payload(payload)
70 }
71}
72
73impl MousePointerInfo {
74 fn from_payload(payload: [u8; 16]) -> Result<Self, Hidpp20Error> {
76 let flags = payload[2];
77 Ok(Self {
78 sensor_resolution: u16::from_be_bytes([payload[0], payload[1]]),
79 pointer_acceleration: PointerAcceleration::try_from(flags & 0b11)
82 .map_err(|_| Hidpp20Error::UnsupportedResponse)?,
83 suggest_os_ballistics: flags & (1 << 2) != 0,
84 suggest_vertical_tuning: flags & (1 << 3) != 0,
85 })
86 }
87}
88
89#[cfg(test)]
90#[allow(clippy::unwrap_used, reason = "expect/unwrap are idiomatic in tests")]
91mod tests {
92 use super::{MousePointerInfo, PointerAcceleration};
93
94 #[test]
95 fn decodes_resolution_and_flags() {
96 let mut payload = [0; 16];
97 payload[0..2].copy_from_slice(&1600u16.to_be_bytes());
98 payload[2] = 0b0000_0111;
100
101 let info = MousePointerInfo::from_payload(payload).unwrap();
102 assert_eq!(info.sensor_resolution, 1600);
103 assert_eq!(info.pointer_acceleration, PointerAcceleration::High);
104 assert!(info.suggest_os_ballistics);
105 assert!(!info.suggest_vertical_tuning);
106 }
107
108 #[test]
109 fn decodes_trackball_vertical_tuning() {
110 let mut payload = [0; 16];
111 payload[0..2].copy_from_slice(&400u16.to_be_bytes());
112 payload[2] = 0b0000_1000;
114
115 let info = MousePointerInfo::from_payload(payload).unwrap();
116 assert_eq!(info.pointer_acceleration, PointerAcceleration::None);
117 assert!(!info.suggest_os_ballistics);
118 assert!(info.suggest_vertical_tuning);
119 }
120}