hidpp/feature/dual_platform/
mod.rs1use std::sync::Arc;
9
10use num_enum::{IntoPrimitive, TryFromPrimitive};
11
12use crate::{
13 channel::{HidppChannel, MessageListenerGuard},
14 event::EventEmitter,
15 feature::{CreatableFeature, EmittingFeature, Feature, FeatureEndpoint, event_payload},
16 protocol::v20::Hidpp20Error,
17};
18
19#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
24#[cfg_attr(feature = "serde", derive(serde::Serialize))]
25#[non_exhaustive]
26#[repr(u8)]
27pub enum DualPlatformSelection {
28 IosOrMac = 0,
30 AndroidOrWindows = 1,
32}
33
34#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
36#[cfg_attr(feature = "serde", derive(serde::Serialize))]
37#[non_exhaustive]
38pub enum DualPlatformEvent {
39 PlatformChanged(DualPlatformSelection),
41}
42
43pub struct DualPlatformFeature {
45 endpoint: FeatureEndpoint,
47
48 emitter: Arc<EventEmitter<DualPlatformEvent>>,
50
51 _msg_listener: MessageListenerGuard,
53}
54
55impl CreatableFeature for DualPlatformFeature {
56 const ID: u16 = 0x4530;
57 const STARTING_VERSION: u8 = 0;
58
59 fn new(chan: Arc<HidppChannel>, device_index: u8, feature_index: u8) -> Self {
60 let emitter = Arc::new(EventEmitter::new());
61
62 let listener = chan.add_msg_listener_guarded({
63 let emitter = Arc::clone(&emitter);
64
65 move |raw, matched| {
66 let Some((func, payload)) =
67 event_payload(raw, matched, device_index, feature_index)
68 else {
69 return;
70 };
71 if func.to_lo() != 0 {
73 return;
74 }
75 if let Ok(platform) = DualPlatformSelection::try_from(payload[0]) {
76 emitter.emit(DualPlatformEvent::PlatformChanged(platform));
77 }
78 }
79 });
80
81 Self {
82 endpoint: FeatureEndpoint::new(chan, device_index, feature_index),
83 emitter,
84 _msg_listener: listener,
85 }
86 }
87}
88
89impl Feature for DualPlatformFeature {}
90
91impl EmittingFeature<DualPlatformEvent> for DualPlatformFeature {
92 fn listen(&self) -> async_channel::Receiver<DualPlatformEvent> {
93 self.emitter.create_receiver()
94 }
95}
96
97impl DualPlatformFeature {
98 pub async fn get_platform(&self) -> Result<DualPlatformSelection, Hidpp20Error> {
100 let payload = self.endpoint.call(1, [0; 3]).await?.extend_payload();
102 DualPlatformSelection::try_from(payload[0]).map_err(|_| Hidpp20Error::UnsupportedResponse)
103 }
104
105 pub async fn set_platform(
109 &self,
110 platform: DualPlatformSelection,
111 ) -> Result<DualPlatformSelection, Hidpp20Error> {
112 let payload = self
113 .endpoint
114 .call(2, [platform.into(), 0, 0])
115 .await?
116 .extend_payload();
117 DualPlatformSelection::try_from(payload[0]).map_err(|_| Hidpp20Error::UnsupportedResponse)
118 }
119}
120
121#[cfg(test)]
122mod tests {
123 use super::DualPlatformSelection;
124
125 #[test]
126 fn maps_platform_wire_values() {
127 assert_eq!(
128 DualPlatformSelection::try_from(0).unwrap(),
129 DualPlatformSelection::IosOrMac
130 );
131 assert_eq!(
132 DualPlatformSelection::try_from(1).unwrap(),
133 DualPlatformSelection::AndroidOrWindows
134 );
135 assert!(DualPlatformSelection::try_from(2).is_err());
136 assert_eq!(u8::from(DualPlatformSelection::AndroidOrWindows), 1);
137 }
138}