hidpp/feature/multi_platform/
mod.rs1use std::sync::Arc;
4
5use num_enum::TryFromPrimitive;
6
7use crate::{
8 channel::HidppChannel,
9 feature::{CreatableFeature, Feature, FeatureEndpoint, hosts_info::HostIndex},
10 protocol::v20::Hidpp20Error,
11};
12
13bitflags::bitflags! {
14 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
16 #[cfg_attr(feature = "serde", derive(serde::Serialize))]
17 pub struct MultiPlatformCapabilities: u16 {
18 const OS_DETECTION = 1 << 0;
20 const SET_HOST_PLATFORM = 1 << 1;
22 }
23}
24
25bitflags::bitflags! {
26 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
28 #[cfg_attr(feature = "serde", derive(serde::Serialize))]
29 pub struct OsMask: u16 {
30 const WINDOWS = 1 << 0;
32 const WINDOWS_EMBEDDED = 1 << 1;
34 const LINUX = 1 << 2;
36 const CHROME = 1 << 3;
38 const ANDROID = 1 << 4;
40 const MACOS = 1 << 5;
42 const IOS = 1 << 6;
44 const WEBOS = 1 << 7;
46 const TIZEN = 1 << 8;
48 }
49}
50
51#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TryFromPrimitive)]
53#[cfg_attr(feature = "serde", derive(serde::Serialize))]
54#[non_exhaustive]
55#[repr(u8)]
56pub enum PlatformSource {
57 Default = 0,
59 Auto = 1,
61 Manual = 2,
63 Software = 3,
65}
66
67#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
69#[cfg_attr(feature = "serde", derive(serde::Serialize))]
70#[non_exhaustive]
71pub struct MultiPlatformInfo {
72 pub capabilities: MultiPlatformCapabilities,
74 pub platform_count: u8,
76 pub descriptor_count: u8,
78 pub host_count: u8,
80 pub current_host: HostIndex,
82 pub current_host_platform: Option<u8>,
84}
85
86#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
88#[cfg_attr(feature = "serde", derive(serde::Serialize))]
89#[non_exhaustive]
90pub struct PlatformDescriptor {
91 pub platform_index: u8,
93 pub descriptor_index: u8,
95 pub os_mask: OsMask,
97 pub from_version: u8,
99 pub from_revision: u8,
101 pub to_version: u8,
103 pub to_revision: u8,
105}
106
107#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
109#[cfg_attr(feature = "serde", derive(serde::Serialize))]
110#[non_exhaustive]
111pub struct HostPlatform {
112 pub host_index: HostIndex,
114 pub status: u8,
116 pub platform_index: Option<u8>,
118 pub source: PlatformSource,
120 pub auto_platform_index: Option<u8>,
122 pub auto_descriptor_index: Option<u8>,
124}
125
126#[derive(Clone)]
128pub struct MultiPlatformFeature {
129 endpoint: FeatureEndpoint,
131}
132
133impl CreatableFeature for MultiPlatformFeature {
134 const ID: u16 = 0x4531;
135 const STARTING_VERSION: u8 = 1;
136
137 fn new(chan: Arc<HidppChannel>, device_index: u8, feature_index: u8) -> Self {
138 Self {
139 endpoint: FeatureEndpoint::new(chan, device_index, feature_index),
140 }
141 }
142}
143
144impl Feature for MultiPlatformFeature {}
145
146impl MultiPlatformFeature {
147 pub async fn get_feature_infos(&self) -> Result<MultiPlatformInfo, Hidpp20Error> {
149 let payload = self.endpoint.call(0, [0; 3]).await?.extend_payload();
150 Ok(MultiPlatformInfo {
151 capabilities: MultiPlatformCapabilities::from_bits_retain(u16::from_be_bytes([
152 payload[0], payload[1],
153 ])),
154 platform_count: payload[2],
155 descriptor_count: payload[3],
156 host_count: payload[4],
157 current_host: HostIndex::from(payload[5]),
158 current_host_platform: optional_index(payload[6]),
159 })
160 }
161
162 pub async fn get_platform_descriptor(
164 &self,
165 descriptor_index: u8,
166 ) -> Result<PlatformDescriptor, Hidpp20Error> {
167 let payload = self
168 .endpoint
169 .call(1, [descriptor_index, 0, 0])
170 .await?
171 .extend_payload();
172 Ok(PlatformDescriptor {
173 platform_index: payload[0],
174 descriptor_index: payload[1],
175 os_mask: OsMask::from_bits_retain(u16::from_be_bytes([payload[2], payload[3]])),
176 from_version: payload[4],
177 from_revision: payload[5],
178 to_version: payload[6],
179 to_revision: payload[7],
180 })
181 }
182
183 pub async fn get_host_platform(&self, host: HostIndex) -> Result<HostPlatform, Hidpp20Error> {
185 let payload = self
186 .endpoint
187 .call(2, [u8::from(host), 0, 0])
188 .await?
189 .extend_payload();
190 Ok(HostPlatform {
191 host_index: HostIndex::from(payload[0]),
192 status: payload[1],
193 platform_index: optional_index(payload[2]),
194 source: PlatformSource::try_from(payload[3])
195 .map_err(|_| Hidpp20Error::UnsupportedResponse)?,
196 auto_platform_index: optional_index(payload[4]),
197 auto_descriptor_index: optional_index(payload[5]),
198 })
199 }
200}
201
202fn optional_index(value: u8) -> Option<u8> {
203 (value != 0xff).then_some(value)
204}