1use std::sync::Arc;
9
10use crate::{
11 channel::{HidppChannel, MessageListenerGuard},
12 event::EventEmitter,
13 feature::{CreatableFeature, EmittingFeature, Feature, FeatureEndpoint, event_payload},
14 protocol::v20::Hidpp20Error,
15};
16
17pub mod control_ids;
18mod event;
19pub mod task_ids;
20
21use event::decode_event_payload;
22pub use event::{AnalyticsKeyEvent, RawWheelResolution, ReprogControlsEvent, decode_event};
23
24pub struct ReprogControlsFeature {
26 endpoint: FeatureEndpoint,
27 emitter: Arc<EventEmitter<ReprogControlsEvent>>,
28 _msg_listener: MessageListenerGuard,
29}
30
31impl CreatableFeature for ReprogControlsFeature {
32 const ID: u16 = 0x1b04;
33 const STARTING_VERSION: u8 = 0;
34
35 fn new(chan: Arc<HidppChannel>, device_index: u8, feature_index: u8) -> Self {
36 let emitter = Arc::new(EventEmitter::new());
37
38 let listener = chan.add_msg_listener_guarded({
39 let emitter = Arc::clone(&emitter);
40
41 move |raw, matched| {
42 let Some((func, payload)) =
43 event_payload(raw, matched, device_index, feature_index)
44 else {
45 return;
46 };
47 let Some(event) = decode_event_payload(func.to_lo(), &payload) else {
48 return;
49 };
50 emitter.emit(event);
51 }
52 });
53
54 Self {
55 endpoint: FeatureEndpoint::new(chan, device_index, feature_index),
56 emitter,
57 _msg_listener: listener,
58 }
59 }
60}
61
62impl Feature for ReprogControlsFeature {}
63
64impl EmittingFeature<ReprogControlsEvent> for ReprogControlsFeature {
65 fn listen(&self) -> async_channel::Receiver<ReprogControlsEvent> {
66 self.emitter.create_receiver()
67 }
68}
69
70impl ReprogControlsFeature {
71 pub async fn get_count(&self) -> Result<u8, Hidpp20Error> {
73 Ok(self.endpoint.call(0, [0; 3]).await?.extend_payload()[0])
74 }
75
76 pub async fn get_cid_info(&self, index: u8) -> Result<CidInfo, Hidpp20Error> {
78 let mut params = [0u8; 16];
79 params[0] = index;
80 let payload = self.endpoint.call_long(1, params).await?.extend_payload();
81 Ok(CidInfo::from_payload(payload))
82 }
83
84 pub async fn get_cid_reporting(&self, cid: ControlId) -> Result<CidReporting, Hidpp20Error> {
86 let [cid_hi, cid_lo] = cid.0.to_be_bytes();
87 let payload = self
88 .endpoint
89 .call(2, [cid_hi, cid_lo, 0])
90 .await?
91 .extend_payload();
92 Ok(CidReporting::from_payload(payload))
93 }
94
95 pub async fn set_cid_reporting(
102 &self,
103 cid: ControlId,
104 change: CidReportingChange,
105 ) -> Result<CidReportingChangeEcho, Hidpp20Error> {
106 let payload = self
107 .endpoint
108 .call_long(3, change.to_payload(cid))
109 .await?
110 .extend_payload();
111 Ok(CidReportingChangeEcho::from_payload(payload))
112 }
113
114 pub async fn get_capabilities(&self) -> Result<ReprogControlsCapabilities, Hidpp20Error> {
119 let payload = self.endpoint.call(4, [0; 3]).await?.extend_payload();
120 Ok(ReprogControlsCapabilities {
121 reset_all_cid_report_settings: payload[0] & 1 != 0,
122 })
123 }
124
125 pub async fn reset_all_cid_report_settings(&self) -> Result<(), Hidpp20Error> {
130 self.endpoint.call(5, [0; 3]).await?;
131 Ok(())
132 }
133}
134
135#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
137#[cfg_attr(feature = "serde", derive(serde::Serialize))]
138pub struct ControlId(pub u16);
139
140impl ControlId {
141 fn from_payload(bytes: &[u8]) -> Self {
142 Self(u16_from_be_payload(bytes))
143 }
144}
145
146impl From<u16> for ControlId {
147 fn from(value: u16) -> Self {
148 Self(value)
149 }
150}
151
152impl From<ControlId> for u16 {
153 fn from(value: ControlId) -> Self {
154 value.0
155 }
156}
157
158fn u16_from_be_payload(bytes: &[u8]) -> u16 {
159 u16::from_be_bytes(bytes.try_into().unwrap())
160}
161
162#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
164#[cfg_attr(feature = "serde", derive(serde::Serialize))]
165pub struct TaskId(pub u16);
166
167impl From<u16> for TaskId {
168 fn from(value: u16) -> Self {
169 Self(value)
170 }
171}
172
173impl From<TaskId> for u16 {
174 fn from(value: TaskId) -> Self {
175 value.0
176 }
177}
178
179#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
181#[cfg_attr(feature = "serde", derive(serde::Serialize))]
182pub struct CidInfo {
183 pub cid: ControlId,
185 pub task_id: TaskId,
187 pub flags: CidFlags,
189 pub position: u8,
191 pub group: u8,
193 pub group_mask: GroupMask,
195}
196
197impl CidInfo {
198 fn from_payload(payload: [u8; 16]) -> Self {
199 Self {
200 cid: ControlId::from_payload(&payload[0..=1]),
201 task_id: TaskId(u16_from_be_payload(&payload[2..=3])),
202 flags: CidFlags::from_bytes(payload[4], payload[8]),
203 position: payload[5],
204 group: payload[6],
205 group_mask: GroupMask(payload[7]),
206 }
207 }
208}
209
210bitflags::bitflags! {
211 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
213 #[cfg_attr(feature = "serde", derive(serde::Serialize))]
214 pub struct CidFlags: u16 {
215 const MOUSE = 1 << 0;
217 const FUNCTION_KEY = 1 << 1;
219 const HOTKEY = 1 << 2;
221 const FN_TOGGLE = 1 << 3;
223 const REPROGRAMMABLE = 1 << 4;
225 const DIVERTABLE = 1 << 5;
227 const PERSISTENTLY_DIVERTABLE = 1 << 6;
229 const VIRTUAL_CONTROL = 1 << 7;
231 const RAW_XY = 1 << 8;
233 const FORCE_RAW_XY = 1 << 9;
235 const ANALYTICS_KEY_EVENTS = 1 << 10;
237 const RAW_WHEEL = 1 << 11;
239 }
240}
241
242impl CidFlags {
243 fn from_bytes(primary: u8, additional: u8) -> Self {
244 Self::from_bits_retain(u16::from(primary) | (u16::from(additional) << 8))
245 }
246
247 #[must_use]
250 pub fn raw(self) -> u16 {
251 self.bits()
252 }
253
254 #[must_use]
256 pub fn is_mouse(self) -> bool {
257 self.contains(Self::MOUSE)
258 }
259
260 #[must_use]
262 pub fn is_divertable(self) -> bool {
263 self.contains(Self::DIVERTABLE)
264 }
265
266 #[must_use]
268 pub fn is_persistently_divertable(self) -> bool {
269 self.contains(Self::PERSISTENTLY_DIVERTABLE)
270 }
271
272 #[must_use]
274 pub fn is_virtual_control(self) -> bool {
275 self.contains(Self::VIRTUAL_CONTROL)
276 }
277
278 #[must_use]
280 pub fn supports_raw_xy(self) -> bool {
281 self.contains(Self::RAW_XY)
282 }
283
284 #[must_use]
286 pub fn supports_force_raw_xy(self) -> bool {
287 self.contains(Self::FORCE_RAW_XY)
288 }
289
290 #[must_use]
292 pub fn supports_analytics_key_events(self) -> bool {
293 self.contains(Self::ANALYTICS_KEY_EVENTS)
294 }
295
296 #[must_use]
298 pub fn supports_raw_wheel(self) -> bool {
299 self.contains(Self::RAW_WHEEL)
300 }
301}
302
303#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
305#[cfg_attr(feature = "serde", derive(serde::Serialize))]
306pub struct GroupMask(pub u8);
307
308#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
310#[cfg_attr(feature = "serde", derive(serde::Serialize))]
311pub struct CidReporting {
312 pub cid: ControlId,
314 pub diverted: bool,
316 pub persistently_diverted: bool,
318 pub force_raw_xy: bool,
320 pub raw_xy: bool,
322 pub remap: Option<ControlId>,
324 pub analytics_key_events: bool,
326 pub raw_wheel: bool,
328}
329
330impl CidReporting {
331 fn from_payload(payload: [u8; 16]) -> Self {
332 let remap = ControlId::from_payload(&payload[3..=4]);
333 Self {
334 cid: ControlId::from_payload(&payload[0..=1]),
335 diverted: payload[2] & (1 << 0) != 0,
336 persistently_diverted: payload[2] & (1 << 2) != 0,
337 raw_xy: payload[2] & (1 << 4) != 0,
338 force_raw_xy: payload[2] & (1 << 6) != 0,
339 remap: (remap.0 != 0).then_some(remap),
340 analytics_key_events: payload[5] & (1 << 0) != 0,
341 raw_wheel: payload[5] & (1 << 2) != 0,
342 }
343 }
344}
345
346#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
351#[cfg_attr(feature = "serde", derive(serde::Serialize))]
352pub struct CidReportingChange {
353 pub diverted: Option<bool>,
355 pub persistently_diverted: Option<bool>,
357 pub force_raw_xy: Option<bool>,
359 pub raw_xy: Option<bool>,
361 pub remap: Option<ControlId>,
364 pub analytics_key_events: Option<bool>,
366 pub raw_wheel: Option<bool>,
368}
369
370impl CidReportingChange {
371 #[must_use]
373 pub fn temporary_diversion(diverted: bool, raw_xy: bool) -> Self {
374 Self {
375 diverted: Some(diverted),
376 raw_xy: Some(raw_xy),
377 ..Self::default()
378 }
379 }
380
381 fn to_payload(self, cid: ControlId) -> [u8; 16] {
382 let mut payload = [0u8; 16];
383 let [cid_hi, cid_lo] = cid.0.to_be_bytes();
384 payload[0] = cid_hi;
385 payload[1] = cid_lo;
386
387 if let Some(value) = self.diverted {
388 payload[2] |= 1 << 1;
389 payload[2] |= u8::from(value);
390 }
391 if let Some(value) = self.persistently_diverted {
392 payload[2] |= 1 << 3;
393 payload[2] |= u8::from(value) << 2;
394 }
395 if let Some(value) = self.raw_xy {
396 payload[2] |= 1 << 5;
397 payload[2] |= u8::from(value) << 4;
398 }
399 if let Some(value) = self.force_raw_xy {
400 payload[2] |= 1 << 7;
401 payload[2] |= u8::from(value) << 6;
402 }
403 if let Some(remap) = self.remap {
404 let [remap_hi, remap_lo] = remap.0.to_be_bytes();
405 payload[3] = remap_hi;
406 payload[4] = remap_lo;
407 }
408 if let Some(value) = self.analytics_key_events {
409 payload[5] |= 1 << 1;
410 payload[5] |= u8::from(value);
411 }
412 if let Some(value) = self.raw_wheel {
413 payload[5] |= 1 << 3;
414 payload[5] |= u8::from(value) << 2;
415 }
416
417 payload
418 }
419}
420
421#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
423#[cfg_attr(feature = "serde", derive(serde::Serialize))]
424pub struct CidReportingChangeEcho {
425 pub cid: ControlId,
427 pub diverted: Option<bool>,
429 pub persistently_diverted: Option<bool>,
431 pub force_raw_xy: Option<bool>,
433 pub raw_xy: Option<bool>,
435 pub remap: Option<ControlId>,
437 pub analytics_key_events: Option<bool>,
439 pub raw_wheel: Option<bool>,
441}
442
443impl CidReportingChangeEcho {
444 fn from_payload(payload: [u8; 16]) -> Self {
445 let remap = ControlId::from_payload(&payload[3..=4]);
446 Self {
447 cid: ControlId::from_payload(&payload[0..=1]),
448 diverted: (payload[2] & (1 << 1) != 0).then_some(payload[2] & (1 << 0) != 0),
449 persistently_diverted: (payload[2] & (1 << 3) != 0)
450 .then_some(payload[2] & (1 << 2) != 0),
451 raw_xy: (payload[2] & (1 << 5) != 0).then_some(payload[2] & (1 << 4) != 0),
452 force_raw_xy: (payload[2] & (1 << 7) != 0).then_some(payload[2] & (1 << 6) != 0),
453 remap: (remap.0 != 0).then_some(remap),
454 analytics_key_events: (payload[5] & (1 << 1) != 0)
455 .then_some(payload[5] & (1 << 0) != 0),
456 raw_wheel: (payload[5] & (1 << 3) != 0).then_some(payload[5] & (1 << 2) != 0),
457 }
458 }
459}
460
461#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
463#[cfg_attr(feature = "serde", derive(serde::Serialize))]
464pub struct ReprogControlsCapabilities {
465 pub reset_all_cid_report_settings: bool,
467}
468
469#[cfg(test)]
470mod tests;