1pub mod event;
7
8#[cfg(test)]
9mod tests;
10
11use std::num::NonZeroU8;
12
13use num_enum::{IntoPrimitive, TryFromPrimitive};
14use openlogi_hidpp_derive::Feature;
15
16pub use event::{ActivityState, ButtonState, CrownEvent, CrownGesture, CrownUpdate, RotationState};
17
18use crate::{
19 feature::{EventSource, FeatureEndpoint},
20 protocol::v20::Hidpp20Error,
21};
22
23bitflags::bitflags! {
24 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
26 #[cfg_attr(feature = "serde", derive(serde::Serialize))]
27 pub struct CrownControlCapabilities: u8 {
28 const BUTTON = 1 << 0;
30 const BUTTON_LONG_PRESS = 1 << 1;
32 const MECHANIZED_RATCHET = 1 << 2;
34 const ROTATION_TIMEOUT_CONFIGURABLE = 1 << 3;
36 const SHORT_LONG_TIMEOUT_CONFIGURABLE = 1 << 4;
38 const DOUBLE_TAP_SPEED_CONFIGURABLE = 1 << 5;
40 }
41}
42
43bitflags::bitflags! {
44 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
46 #[cfg_attr(feature = "serde", derive(serde::Serialize))]
47 pub struct CrownSensorCapabilities: u8 {
48 const PROXIMITY = 1 << 0;
50 const TOUCH = 1 << 1;
52 const TAP_GESTURE = 1 << 2;
54 const DOUBLE_TAP_GESTURE = 1 << 3;
56 }
57}
58
59#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
66#[cfg_attr(feature = "serde", derive(serde::Serialize))]
67#[non_exhaustive]
68#[repr(u8)]
69pub enum ReportingMode {
70 Hid = 1,
72 Diverted = 2,
74}
75
76#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
79#[cfg_attr(feature = "serde", derive(serde::Serialize))]
80#[non_exhaustive]
81#[repr(u8)]
82pub enum RatchetMode {
83 Free = 1,
85 Ratchet = 2,
87}
88
89#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
91#[cfg_attr(feature = "serde", derive(serde::Serialize))]
92#[non_exhaustive]
93pub struct CrownInfo {
94 pub controls: CrownControlCapabilities,
96 pub sensors: CrownSensorCapabilities,
98 pub slots: u16,
100 pub ratchets: u16,
102}
103
104#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
106#[cfg_attr(feature = "serde", derive(serde::Serialize))]
107#[non_exhaustive]
108pub struct CrownMode {
109 pub diverting: ReportingMode,
111 pub ratchet_mode: RatchetMode,
113 pub rotation_timeout: u8,
115 pub short_long_timeout: u8,
117 pub double_tap_speed: u8,
119}
120
121impl CrownMode {
122 fn from_payload(payload: &[u8; 16]) -> Result<Self, Hidpp20Error> {
123 Ok(Self {
124 diverting: ReportingMode::try_from(payload[0])
125 .map_err(|_| Hidpp20Error::UnsupportedResponse)?,
126 ratchet_mode: RatchetMode::try_from(payload[1])
127 .map_err(|_| Hidpp20Error::UnsupportedResponse)?,
128 rotation_timeout: payload[2],
129 short_long_timeout: payload[3],
130 double_tap_speed: payload[4],
131 })
132 }
133}
134
135#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
141#[cfg_attr(feature = "serde", derive(serde::Serialize))]
142pub struct SetCrownMode {
143 pub diverting: Option<ReportingMode>,
145 pub ratchet_mode: Option<RatchetMode>,
147 pub rotation_timeout: Option<NonZeroU8>,
149 pub short_long_timeout: Option<NonZeroU8>,
151 pub double_tap_speed: Option<NonZeroU8>,
153}
154
155#[derive(Feature)]
157#[creatable(id = 0x4600, version = 0)]
158pub struct CrownFeature {
159 endpoint: FeatureEndpoint,
161
162 events: EventSource<CrownEvent>,
164}
165
166impl CrownFeature {
167 pub async fn get_info(&self) -> Result<CrownInfo, Hidpp20Error> {
169 let payload = self.endpoint.call(0, [0; 3]).await?.extend_payload();
170 Ok(CrownInfo {
171 controls: CrownControlCapabilities::from_bits_retain(payload[0]),
172 sensors: CrownSensorCapabilities::from_bits_retain(payload[1]),
173 slots: u16::from_be_bytes([payload[2], payload[3]]),
174 ratchets: u16::from_be_bytes([payload[4], payload[5]]),
175 })
176 }
177
178 pub async fn get_mode(&self) -> Result<CrownMode, Hidpp20Error> {
180 let payload = self.endpoint.call(1, [0; 3]).await?.extend_payload();
181 CrownMode::from_payload(&payload)
182 }
183
184 pub async fn set_mode(&self, mode: SetCrownMode) -> Result<(), Hidpp20Error> {
195 let mut args = [0; 16];
196 args[..5].copy_from_slice(&[
197 mode.diverting.map_or(0, u8::from),
198 mode.ratchet_mode.map_or(0, u8::from),
199 mode.rotation_timeout.map_or(0, NonZeroU8::get),
200 mode.short_long_timeout.map_or(0, NonZeroU8::get),
201 mode.double_tap_speed.map_or(0, NonZeroU8::get),
202 ]);
203 self.endpoint.call_long(2, args).await?;
204 Ok(())
205 }
206}