pub mod event;
#[cfg(test)]
mod tests;
use std::sync::Arc;
use num_enum::{IntoPrimitive, TryFromPrimitive};
pub use event::{ActivityState, ButtonState, CrownEvent, CrownGesture, CrownUpdate, RotationState};
use crate::{
channel::{HidppChannel, MessageListenerGuard},
event::EventEmitter,
feature::{CreatableFeature, EmittingFeature, Feature, FeatureEndpoint, event_payload},
protocol::v20::Hidpp20Error,
};
bitflags::bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct CrownControlCapabilities: u8 {
const BUTTON = 1 << 0;
const BUTTON_LONG_PRESS = 1 << 1;
const MECHANIZED_RATCHET = 1 << 2;
const ROTATION_TIMEOUT_CONFIGURABLE = 1 << 3;
const SHORT_LONG_TIMEOUT_CONFIGURABLE = 1 << 4;
const DOUBLE_TAP_SPEED_CONFIGURABLE = 1 << 5;
}
}
bitflags::bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct CrownSensorCapabilities: u8 {
const PROXIMITY = 1 << 0;
const TOUCH = 1 << 1;
const TAP_GESTURE = 1 << 2;
const DOUBLE_TAP_GESTURE = 1 << 3;
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
#[repr(u8)]
pub enum ReportingMode {
NoChange = 0,
Hid = 1,
Diverted = 2,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
#[repr(u8)]
pub enum RatchetMode {
NoChange = 0,
Free = 1,
Ratchet = 2,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct CrownInfo {
pub controls: CrownControlCapabilities,
pub sensors: CrownSensorCapabilities,
pub slots: u16,
pub ratchets: u16,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct CrownMode {
pub diverting: ReportingMode,
pub ratchet_mode: RatchetMode,
pub rotation_timeout: u8,
pub short_long_timeout: u8,
pub double_tap_speed: u8,
}
impl CrownMode {
fn from_payload(payload: &[u8; 16]) -> Result<Self, Hidpp20Error> {
Ok(Self {
diverting: ReportingMode::try_from(payload[0])
.map_err(|_| Hidpp20Error::UnsupportedResponse)?,
ratchet_mode: RatchetMode::try_from(payload[1])
.map_err(|_| Hidpp20Error::UnsupportedResponse)?,
rotation_timeout: payload[2],
short_long_timeout: payload[3],
double_tap_speed: payload[4],
})
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct SetCrownMode {
pub diverting: ReportingMode,
pub ratchet_mode: RatchetMode,
pub rotation_timeout: u8,
pub short_long_timeout: u8,
pub double_tap_speed: u8,
}
pub struct CrownFeature {
endpoint: FeatureEndpoint,
emitter: Arc<EventEmitter<CrownEvent>>,
_msg_listener: MessageListenerGuard,
}
impl CreatableFeature for CrownFeature {
const ID: u16 = 0x4600;
const STARTING_VERSION: u8 = 0;
fn new(chan: Arc<HidppChannel>, device_index: u8, feature_index: u8) -> Self {
let emitter = Arc::new(EventEmitter::new());
let listener = chan.add_msg_listener_guarded({
let emitter = Arc::clone(&emitter);
move |raw, matched| {
let Some((func, payload)) =
event_payload(raw, matched, device_index, feature_index)
else {
return;
};
if let Some(event) = event::decode_event(func.to_lo(), &payload) {
emitter.emit(event);
}
}
});
Self {
endpoint: FeatureEndpoint::new(chan, device_index, feature_index),
emitter,
_msg_listener: listener,
}
}
}
impl Feature for CrownFeature {}
impl EmittingFeature<CrownEvent> for CrownFeature {
fn listen(&self) -> async_channel::Receiver<CrownEvent> {
self.emitter.create_receiver()
}
}
impl CrownFeature {
pub async fn get_info(&self) -> Result<CrownInfo, Hidpp20Error> {
let payload = self.endpoint.call(0, [0; 3]).await?.extend_payload();
Ok(CrownInfo {
controls: CrownControlCapabilities::from_bits_retain(payload[0]),
sensors: CrownSensorCapabilities::from_bits_retain(payload[1]),
slots: u16::from_be_bytes([payload[2], payload[3]]),
ratchets: u16::from_be_bytes([payload[4], payload[5]]),
})
}
pub async fn get_mode(&self) -> Result<CrownMode, Hidpp20Error> {
let payload = self.endpoint.call(1, [0; 3]).await?.extend_payload();
CrownMode::from_payload(&payload)
}
pub async fn set_mode(&self, mode: SetCrownMode) -> Result<CrownMode, Hidpp20Error> {
let mut args = [0; 16];
args[..5].copy_from_slice(&[
mode.diverting.into(),
mode.ratchet_mode.into(),
mode.rotation_timeout,
mode.short_long_timeout,
mode.double_tap_speed,
]);
let payload = self.endpoint.call_long(2, args).await?.extend_payload();
CrownMode::from_payload(&payload)
}
}