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)]
90mod tests {
91 use super::{MousePointerInfo, PointerAcceleration};
92
93 #[test]
94 fn decodes_resolution_and_flags() {
95 let mut payload = [0; 16];
96 payload[0..2].copy_from_slice(&1600u16.to_be_bytes());
97 payload[2] = 0b0000_0111;
99
100 let info = MousePointerInfo::from_payload(payload).unwrap();
101 assert_eq!(info.sensor_resolution, 1600);
102 assert_eq!(info.pointer_acceleration, PointerAcceleration::High);
103 assert!(info.suggest_os_ballistics);
104 assert!(!info.suggest_vertical_tuning);
105 }
106
107 #[test]
108 fn decodes_trackball_vertical_tuning() {
109 let mut payload = [0; 16];
110 payload[0..2].copy_from_slice(&400u16.to_be_bytes());
111 payload[2] = 0b0000_1000;
113
114 let info = MousePointerInfo::from_payload(payload).unwrap();
115 assert_eq!(info.pointer_acceleration, PointerAcceleration::None);
116 assert!(!info.suggest_os_ballistics);
117 assert!(info.suggest_vertical_tuning);
118 }
119}