1use std::hash::Hash;
5
6use num_enum::{IntoPrimitive, TryFromPrimitive};
7use openlogi_hidpp_derive::Feature;
8
9use crate::{
10 feature::{DecodeEvent, EventSource, FeatureEndpoint},
11 nibble::U4,
12 protocol::v20::Hidpp20Error,
13};
14
15#[derive(Feature)]
20#[creatable(id = 0x2121, version = 0)]
21pub struct HiResWheelFeature {
22 endpoint: FeatureEndpoint,
24
25 events: EventSource<HiResWheelEvent>,
27}
28
29impl DecodeEvent for HiResWheelEvent {
30 fn decode(sub_id: u8, payload: &[u8; 16]) -> Option<Self> {
31 match sub_id {
33 0 => {
34 let resolution = WheelResolution::try_from((payload[0] & (1 << 4)) >> 4).ok()?;
35 Some(HiResWheelEvent::WheelMovement(WheelMovementData {
36 resolution,
37 periods: U4::from_lo(payload[0]),
38 delta_vertical: i16::from_be_bytes([payload[1], payload[2]]),
39 }))
40 }
41 1 => {
42 let state = WheelRatchetState::try_from(payload[0] & 1).ok()?;
43 Some(HiResWheelEvent::RatchetSwitch(state))
44 }
45 _ => None,
46 }
47 }
48}
49
50impl HiResWheelFeature {
51 pub async fn get_wheel_capabilities(&self) -> Result<WheelCapabilities, Hidpp20Error> {
53 let payload = self.endpoint.call(0, [0; 3]).await?.extend_payload();
54
55 Ok(WheelCapabilities {
56 multiplier: payload[0],
57 has_invert: payload[1] & (1 << 3) != 0,
58 has_switch: payload[1] & (1 << 2) != 0,
59 ratches_per_rotation: payload[2],
60 wheel_diameter: payload[3],
61 })
62 }
63
64 pub async fn get_wheel_mode(&self) -> Result<WheelMode, Hidpp20Error> {
66 let payload = self.endpoint.call(1, [0; 3]).await?.extend_payload();
67
68 Ok(WheelMode {
69 inverted: payload[0] & (1 << 2) != 0,
70 resolution: WheelResolution::try_from((payload[0] & (1 << 1)) >> 1)
71 .map_err(|_| Hidpp20Error::UnsupportedResponse)?,
72 target: WheelEventTarget::try_from(payload[0] & 1)
73 .map_err(|_| Hidpp20Error::UnsupportedResponse)?,
74 })
75 }
76
77 pub async fn set_wheel_mode(
85 &self,
86 target: WheelEventTarget,
87 resolution: WheelResolution,
88 inverted: bool,
89 ) -> Result<WheelMode, Hidpp20Error> {
90 let mut mode_byte = 0u8;
91 if inverted {
92 mode_byte |= 1 << 2;
93 }
94 mode_byte |= u8::from(resolution) << 1;
95 mode_byte |= u8::from(target);
96
97 let payload = self
98 .endpoint
99 .call(2, [mode_byte, 0x00, 0x00])
100 .await?
101 .extend_payload();
102
103 Ok(WheelMode {
104 inverted: payload[0] & (1 << 2) != 0,
105 resolution: WheelResolution::try_from((payload[0] & (1 << 1)) >> 1)
106 .map_err(|_| Hidpp20Error::UnsupportedResponse)?,
107 target: WheelEventTarget::try_from(payload[0] & 1)
108 .map_err(|_| Hidpp20Error::UnsupportedResponse)?,
109 })
110 }
111
112 pub async fn get_ratchet_switch_state(&self) -> Result<WheelRatchetState, Hidpp20Error> {
114 let payload = self.endpoint.call(3, [0; 3]).await?.extend_payload();
115
116 WheelRatchetState::try_from(payload[0] & 1).map_err(|_| Hidpp20Error::UnsupportedResponse)
117 }
118}
119
120#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
123#[cfg_attr(feature = "serde", derive(serde::Serialize))]
124#[non_exhaustive]
125pub struct WheelCapabilities {
126 pub multiplier: u8,
130
131 pub has_invert: bool,
136
137 pub has_switch: bool,
139
140 pub ratches_per_rotation: u8,
143
144 pub wheel_diameter: u8,
146}
147
148#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
151#[cfg_attr(feature = "serde", derive(serde::Serialize))]
152#[non_exhaustive]
153pub struct WheelMode {
154 pub inverted: bool,
157
158 pub resolution: WheelResolution,
160
161 pub target: WheelEventTarget,
163}
164
165#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
167#[cfg_attr(feature = "serde", derive(serde::Serialize))]
168#[non_exhaustive]
169#[repr(u8)]
170pub enum WheelResolution {
171 Low = 0,
173 High = 1,
175}
176
177#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
179#[cfg_attr(feature = "serde", derive(serde::Serialize))]
180#[non_exhaustive]
181#[repr(u8)]
182pub enum WheelEventTarget {
183 Native = 0,
185 Diverted = 1,
187}
188
189#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
191#[cfg_attr(feature = "serde", derive(serde::Serialize))]
192#[non_exhaustive]
193#[repr(u8)]
194pub enum WheelRatchetState {
195 Freespin = 0,
197 Ratchet = 1,
199}
200
201#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
203#[cfg_attr(feature = "serde", derive(serde::Serialize))]
204#[non_exhaustive]
205pub enum HiResWheelEvent {
206 WheelMovement(WheelMovementData),
208
209 RatchetSwitch(WheelRatchetState),
213}
214
215#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
217#[cfg_attr(feature = "serde", derive(serde::Serialize))]
218#[non_exhaustive]
219pub struct WheelMovementData {
220 pub resolution: WheelResolution,
222
223 pub periods: U4,
225
226 pub delta_vertical: i16,
229}