1pub mod event;
10pub mod types;
11
12#[cfg(test)]
13mod tests;
14
15use openlogi_hidpp_derive::Feature;
16
17pub use event::{DpiCalibrationCompleted, DpiParametersChanged, ExtendedDpiEvent};
18pub use types::{
19 CalibrationType, DpiCalibrationCorrection, DpiCalibrationInfo, DpiDirection, DpiParameters,
20 DpiRange, LedHoldType, Lod, SensorCapabilities, SensorCapabilitiesInfo, SetDpiParameters,
21 ShowDpiStatus, StartDpiCalibration,
22};
23
24use self::types::{parse_dpi_list, parse_dpi_ranges, parse_lod_list, terminated_word_len};
25use crate::{
26 feature::{EventSource, FeatureEndpoint},
27 protocol::v20::Hidpp20Error,
28};
29
30const MAX_RANGE_PAGES: u8 = 16;
33
34#[derive(Feature)]
36#[creatable(id = 0x2202, version = 0)]
37pub struct ExtendedDpiFeature {
38 endpoint: FeatureEndpoint,
40
41 events: EventSource<ExtendedDpiEvent>,
43}
44
45impl ExtendedDpiFeature {
46 pub async fn get_sensor_count(&self) -> Result<u8, Hidpp20Error> {
48 Ok(self.endpoint.call(0, [0; 3]).await?.extend_payload()[0])
49 }
50
51 pub async fn get_sensor_capabilities(
53 &self,
54 sensor_index: u8,
55 ) -> Result<SensorCapabilitiesInfo, Hidpp20Error> {
56 let payload = self
57 .endpoint
58 .call(1, [sensor_index, 0, 0])
59 .await?
60 .extend_payload();
61 Ok(SensorCapabilitiesInfo {
62 sensor_index: payload[0],
63 dpi_level_count: payload[1],
64 capabilities: SensorCapabilities::from_bits_retain(payload[2]),
65 })
66 }
67
68 pub async fn get_sensor_dpi_ranges(
76 &self,
77 sensor_index: u8,
78 direction: DpiDirection,
79 ) -> Result<Vec<DpiRange>, Hidpp20Error> {
80 let mut stream = Vec::new();
81 for page in 0..MAX_RANGE_PAGES {
82 let payload = self
83 .endpoint
84 .call(2, [sensor_index, direction.into(), page])
85 .await?
86 .extend_payload();
87 if payload[0] != sensor_index || payload[1] != u8::from(direction) || payload[2] != page
91 {
92 return Err(Hidpp20Error::UnsupportedResponse);
93 }
94 stream.extend_from_slice(&payload[3..16]);
95 if terminated_word_len(&stream).is_some() {
96 return parse_dpi_ranges(&stream);
97 }
98 }
99 Err(Hidpp20Error::UnsupportedResponse)
100 }
101
102 pub async fn get_sensor_dpi_list(
108 &self,
109 sensor_index: u8,
110 direction: DpiDirection,
111 ) -> Result<Vec<u16>, Hidpp20Error> {
112 let payload = self
113 .endpoint
114 .call(3, [sensor_index, direction.into(), 0])
115 .await?
116 .extend_payload();
117 Ok(parse_dpi_list(&payload[2..]))
119 }
120
121 pub async fn get_sensor_lod_list(
128 &self,
129 sensor_index: u8,
130 dpi_level_count: u8,
131 ) -> Result<Vec<Lod>, Hidpp20Error> {
132 let payload = self
133 .endpoint
134 .call(4, [sensor_index, 0, 0])
135 .await?
136 .extend_payload();
137 parse_lod_list(&payload[1..], usize::from(dpi_level_count))
139 }
140
141 pub async fn get_sensor_dpi_parameters(
143 &self,
144 sensor_index: u8,
145 ) -> Result<DpiParameters, Hidpp20Error> {
146 let payload = self
147 .endpoint
148 .call(5, [sensor_index, 0, 0])
149 .await?
150 .extend_payload();
151 Ok(DpiParameters {
152 sensor_index: payload[0],
153 dpi_x: u16::from_be_bytes([payload[1], payload[2]]),
154 default_dpi_x: u16::from_be_bytes([payload[3], payload[4]]),
155 dpi_y: u16::from_be_bytes([payload[5], payload[6]]),
156 default_dpi_y: u16::from_be_bytes([payload[7], payload[8]]),
157 lod: Lod::try_from(payload[9]).map_err(|_| Hidpp20Error::UnsupportedResponse)?,
158 })
159 }
160
161 pub async fn set_sensor_dpi_parameters(
165 &self,
166 sensor_index: u8,
167 params: SetDpiParameters,
168 ) -> Result<(), Hidpp20Error> {
169 let mut args = [0; 16];
170 args[0] = sensor_index;
171 args[1..3].copy_from_slice(¶ms.dpi_x.to_be_bytes());
172 args[3..5].copy_from_slice(¶ms.dpi_y.to_be_bytes());
173 args[5] = params.lod.into();
174 self.endpoint.call_long(6, args).await?;
175 Ok(())
176 }
177
178 pub async fn show_sensor_dpi_status(
182 &self,
183 sensor_index: u8,
184 params: ShowDpiStatus,
185 ) -> Result<(), Hidpp20Error> {
186 let mut args = [0; 16];
187 args[..4].copy_from_slice(&[
188 sensor_index,
189 params.dpi_level,
190 params.led_hold_type.into(),
191 params.button_num,
192 ]);
193 self.endpoint.call_long(7, args).await?;
194 Ok(())
195 }
196
197 pub async fn get_dpi_calibration_info(
200 &self,
201 sensor_index: u8,
202 ) -> Result<DpiCalibrationInfo, Hidpp20Error> {
203 let payload = self
204 .endpoint
205 .call(8, [sensor_index, 0, 0])
206 .await?
207 .extend_payload();
208 Ok(DpiCalibrationInfo {
209 sensor_index: payload[0],
210 mouse_width: payload[1],
211 mouse_length: u16::from_be_bytes([payload[2], payload[3]]),
212 calib_dpi_x: u16::from_be_bytes([payload[4], payload[5]]),
213 calib_dpi_y: u16::from_be_bytes([payload[6], payload[7]]),
214 })
215 }
216
217 pub async fn start_dpi_calibration(
224 &self,
225 sensor_index: u8,
226 params: StartDpiCalibration,
227 ) -> Result<(), Hidpp20Error> {
228 let [count_hi, count_lo] = params.expected_count.to_be_bytes();
229 let mut args = [0; 16];
230 args[..8].copy_from_slice(&[
231 sensor_index,
232 params.direction.into(),
233 count_hi,
234 count_lo,
235 params.calib_type.into(),
236 params.start_timeout,
237 params.hw_process_timeout,
238 params.sw_process_timeout,
239 ]);
240 self.endpoint.call_long(9, args).await?;
241 Ok(())
242 }
243
244 pub async fn set_dpi_calibration(
249 &self,
250 sensor_index: u8,
251 direction: DpiDirection,
252 correction: DpiCalibrationCorrection,
253 ) -> Result<(), Hidpp20Error> {
254 let [cor_hi, cor_lo] = correction.to_wire()?.to_be_bytes();
255 let mut args = [0; 16];
256 args[..4].copy_from_slice(&[sensor_index, direction.into(), cor_hi, cor_lo]);
257 self.endpoint.call_long(10, args).await?;
258 Ok(())
259 }
260}