hidpp/feature/extended_dpi/
mod.rs1pub mod event;
10pub mod types;
11
12#[cfg(test)]
13mod tests;
14
15use std::sync::Arc;
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 channel::{HidppChannel, MessageListenerGuard},
27 event::EventEmitter,
28 feature::{CreatableFeature, EmittingFeature, Feature, FeatureEndpoint, event_payload},
29 protocol::v20::Hidpp20Error,
30};
31
32const MAX_RANGE_PAGES: u8 = 16;
35
36pub struct ExtendedDpiFeature {
38 endpoint: FeatureEndpoint,
40
41 emitter: Arc<EventEmitter<ExtendedDpiEvent>>,
43
44 _msg_listener: MessageListenerGuard,
46}
47
48impl CreatableFeature for ExtendedDpiFeature {
49 const ID: u16 = 0x2202;
50 const STARTING_VERSION: u8 = 0;
51
52 fn new(chan: Arc<HidppChannel>, device_index: u8, feature_index: u8) -> Self {
53 let emitter = Arc::new(EventEmitter::new());
54
55 let listener = chan.add_msg_listener_guarded({
56 let emitter = Arc::clone(&emitter);
57
58 move |raw, matched| {
59 let Some((func, payload)) =
60 event_payload(raw, matched, device_index, feature_index)
61 else {
62 return;
63 };
64
65 if let Some(event) = event::decode_event(func.to_lo(), &payload) {
66 emitter.emit(event);
67 }
68 }
69 });
70
71 Self {
72 endpoint: FeatureEndpoint::new(chan, device_index, feature_index),
73 emitter,
74 _msg_listener: listener,
75 }
76 }
77}
78
79impl Feature for ExtendedDpiFeature {}
80
81impl EmittingFeature<ExtendedDpiEvent> for ExtendedDpiFeature {
82 fn listen(&self) -> async_channel::Receiver<ExtendedDpiEvent> {
83 self.emitter.create_receiver()
84 }
85}
86
87impl ExtendedDpiFeature {
88 pub async fn get_sensor_count(&self) -> Result<u8, Hidpp20Error> {
90 Ok(self.endpoint.call(0, [0; 3]).await?.extend_payload()[0])
91 }
92
93 pub async fn get_sensor_capabilities(
95 &self,
96 sensor_index: u8,
97 ) -> Result<SensorCapabilitiesInfo, Hidpp20Error> {
98 let payload = self
99 .endpoint
100 .call(1, [sensor_index, 0, 0])
101 .await?
102 .extend_payload();
103 Ok(SensorCapabilitiesInfo {
104 sensor_index: payload[0],
105 dpi_level_count: payload[1],
106 capabilities: SensorCapabilities::from_bits_retain(payload[2]),
107 })
108 }
109
110 pub async fn get_sensor_dpi_ranges(
118 &self,
119 sensor_index: u8,
120 direction: DpiDirection,
121 ) -> Result<Vec<DpiRange>, Hidpp20Error> {
122 let mut stream = Vec::new();
123 for page in 0..MAX_RANGE_PAGES {
124 let payload = self
125 .endpoint
126 .call(2, [sensor_index, direction.into(), page])
127 .await?
128 .extend_payload();
129 if payload[0] != sensor_index || payload[1] != u8::from(direction) || payload[2] != page
133 {
134 return Err(Hidpp20Error::UnsupportedResponse);
135 }
136 stream.extend_from_slice(&payload[3..16]);
137 if terminated_word_len(&stream).is_some() {
138 return parse_dpi_ranges(&stream);
139 }
140 }
141 Err(Hidpp20Error::UnsupportedResponse)
142 }
143
144 pub async fn get_sensor_dpi_list(
150 &self,
151 sensor_index: u8,
152 direction: DpiDirection,
153 ) -> Result<Vec<u16>, Hidpp20Error> {
154 let payload = self
155 .endpoint
156 .call(3, [sensor_index, direction.into(), 0])
157 .await?
158 .extend_payload();
159 parse_dpi_list(&payload[2..])
161 }
162
163 pub async fn get_sensor_lod_list(
170 &self,
171 sensor_index: u8,
172 dpi_level_count: u8,
173 ) -> Result<Vec<Lod>, Hidpp20Error> {
174 let payload = self
175 .endpoint
176 .call(4, [sensor_index, 0, 0])
177 .await?
178 .extend_payload();
179 parse_lod_list(&payload[1..], usize::from(dpi_level_count))
181 }
182
183 pub async fn get_sensor_dpi_parameters(
185 &self,
186 sensor_index: u8,
187 ) -> Result<DpiParameters, Hidpp20Error> {
188 let payload = self
189 .endpoint
190 .call(5, [sensor_index, 0, 0])
191 .await?
192 .extend_payload();
193 Ok(DpiParameters {
194 sensor_index: payload[0],
195 dpi_x: u16::from_be_bytes([payload[1], payload[2]]),
196 default_dpi_x: u16::from_be_bytes([payload[3], payload[4]]),
197 dpi_y: u16::from_be_bytes([payload[5], payload[6]]),
198 default_dpi_y: u16::from_be_bytes([payload[7], payload[8]]),
199 lod: Lod::try_from(payload[9]).map_err(|_| Hidpp20Error::UnsupportedResponse)?,
200 })
201 }
202
203 pub async fn set_sensor_dpi_parameters(
207 &self,
208 sensor_index: u8,
209 params: SetDpiParameters,
210 ) -> Result<(), Hidpp20Error> {
211 let [dpi_x_hi, dpi_x_lo] = params.dpi_x.to_be_bytes();
212 let [dpi_y_hi, dpi_y_lo] = params.dpi_y.to_be_bytes();
213 let mut args = [0; 16];
214 args[..6].copy_from_slice(&[
215 sensor_index,
216 dpi_x_hi,
217 dpi_x_lo,
218 dpi_y_hi,
219 dpi_y_lo,
220 params.lod.into(),
221 ]);
222 self.endpoint.call_long(6, args).await?;
223 Ok(())
224 }
225
226 pub async fn show_sensor_dpi_status(
230 &self,
231 sensor_index: u8,
232 params: ShowDpiStatus,
233 ) -> Result<(), Hidpp20Error> {
234 let mut args = [0; 16];
235 args[..4].copy_from_slice(&[
236 sensor_index,
237 params.dpi_level,
238 params.led_hold_type.into(),
239 params.button_num,
240 ]);
241 self.endpoint.call_long(7, args).await?;
242 Ok(())
243 }
244
245 pub async fn get_dpi_calibration_info(
248 &self,
249 sensor_index: u8,
250 ) -> Result<DpiCalibrationInfo, Hidpp20Error> {
251 let payload = self
252 .endpoint
253 .call(8, [sensor_index, 0, 0])
254 .await?
255 .extend_payload();
256 Ok(DpiCalibrationInfo {
257 sensor_index: payload[0],
258 mouse_width: payload[1],
259 mouse_length: u16::from_be_bytes([payload[2], payload[3]]),
260 calib_dpi_x: u16::from_be_bytes([payload[4], payload[5]]),
261 calib_dpi_y: u16::from_be_bytes([payload[6], payload[7]]),
262 })
263 }
264
265 pub async fn start_dpi_calibration(
272 &self,
273 sensor_index: u8,
274 params: StartDpiCalibration,
275 ) -> Result<(), Hidpp20Error> {
276 let [count_hi, count_lo] = params.expected_count.to_be_bytes();
277 let mut args = [0; 16];
278 args[..8].copy_from_slice(&[
279 sensor_index,
280 params.direction.into(),
281 count_hi,
282 count_lo,
283 params.calib_type.into(),
284 params.start_timeout,
285 params.hw_process_timeout,
286 params.sw_process_timeout,
287 ]);
288 self.endpoint.call_long(9, args).await?;
289 Ok(())
290 }
291
292 pub async fn set_dpi_calibration(
297 &self,
298 sensor_index: u8,
299 direction: DpiDirection,
300 correction: DpiCalibrationCorrection,
301 ) -> Result<(), Hidpp20Error> {
302 let [cor_hi, cor_lo] = correction.to_wire()?.to_be_bytes();
303 let mut args = [0; 16];
304 args[..4].copy_from_slice(&[sensor_index, direction.into(), cor_hi, cor_lo]);
305 self.endpoint.call_long(10, args).await?;
306 Ok(())
307 }
308}