1use num_enum::TryFromPrimitive;
4use openlogi_hidpp_derive::Feature;
5
6use crate::{
7 feature::{FeatureEndpoint, hosts_info::HostIndex},
8 protocol::v20::Hidpp20Error,
9};
10
11bitflags::bitflags! {
12 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
14 #[cfg_attr(feature = "serde", derive(serde::Serialize))]
15 pub struct MultiPlatformCapabilities: u16 {
16 const OS_DETECTION = 1 << 0;
18 const SET_HOST_PLATFORM = 1 << 1;
20 }
21}
22
23bitflags::bitflags! {
24 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
26 #[cfg_attr(feature = "serde", derive(serde::Serialize))]
27 pub struct OsMask: u16 {
28 const WINDOWS = 1 << 0;
30 const WINDOWS_EMBEDDED = 1 << 1;
32 const LINUX = 1 << 2;
34 const CHROME = 1 << 3;
36 const ANDROID = 1 << 4;
38 const MACOS = 1 << 5;
40 const IOS = 1 << 6;
42 const WEBOS = 1 << 7;
44 const TIZEN = 1 << 8;
46 }
47}
48
49#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TryFromPrimitive)]
51#[cfg_attr(feature = "serde", derive(serde::Serialize))]
52#[non_exhaustive]
53#[repr(u8)]
54pub enum PlatformSource {
55 Default = 0,
57 Auto = 1,
59 Manual = 2,
61 Software = 3,
63}
64
65#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
67#[cfg_attr(feature = "serde", derive(serde::Serialize))]
68#[non_exhaustive]
69pub struct MultiPlatformInfo {
70 pub capabilities: MultiPlatformCapabilities,
72 pub platform_count: u8,
74 pub descriptor_count: u8,
76 pub host_count: u8,
78 pub current_host: HostIndex,
80 pub current_host_platform: Option<u8>,
82}
83
84#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
86#[cfg_attr(feature = "serde", derive(serde::Serialize))]
87#[non_exhaustive]
88pub struct PlatformDescriptor {
89 pub platform_index: u8,
91 pub descriptor_index: u8,
93 pub os_mask: OsMask,
95 pub from_version: u8,
97 pub from_revision: u8,
99 pub to_version: u8,
101 pub to_revision: u8,
103}
104
105#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
107#[cfg_attr(feature = "serde", derive(serde::Serialize))]
108#[non_exhaustive]
109pub struct HostPlatform {
110 pub host_index: HostIndex,
112 pub status: u8,
114 pub platform_index: Option<u8>,
116 pub source: PlatformSource,
118 pub auto_platform_index: Option<u8>,
120 pub auto_descriptor_index: Option<u8>,
122}
123
124#[derive(Clone, Feature)]
126#[creatable(id = 0x4531, version = 1)]
127pub struct MultiPlatformFeature {
128 endpoint: FeatureEndpoint,
130}
131
132impl MultiPlatformFeature {
133 pub async fn get_feature_infos(&self) -> Result<MultiPlatformInfo, Hidpp20Error> {
135 let payload = self.endpoint.call(0, [0; 3]).await?.extend_payload();
136 Ok(MultiPlatformInfo {
137 capabilities: MultiPlatformCapabilities::from_bits_retain(u16::from_be_bytes([
138 payload[0], payload[1],
139 ])),
140 platform_count: payload[2],
141 descriptor_count: payload[3],
142 host_count: payload[4],
143 current_host: HostIndex::from(payload[5]),
144 current_host_platform: optional_index(payload[6]),
145 })
146 }
147
148 pub async fn get_platform_descriptor(
150 &self,
151 descriptor_index: u8,
152 ) -> Result<PlatformDescriptor, Hidpp20Error> {
153 let payload = self
154 .endpoint
155 .call(1, [descriptor_index, 0, 0])
156 .await?
157 .extend_payload();
158 Ok(PlatformDescriptor {
159 platform_index: payload[0],
160 descriptor_index: payload[1],
161 os_mask: OsMask::from_bits_retain(u16::from_be_bytes([payload[2], payload[3]])),
162 from_version: payload[4],
163 from_revision: payload[5],
164 to_version: payload[6],
165 to_revision: payload[7],
166 })
167 }
168
169 pub async fn get_host_platform(&self, host: HostIndex) -> Result<HostPlatform, Hidpp20Error> {
171 let payload = self
172 .endpoint
173 .call(2, [u8::from(host), 0, 0])
174 .await?
175 .extend_payload();
176 Ok(HostPlatform {
177 host_index: HostIndex::from(payload[0]),
178 status: payload[1],
179 platform_index: optional_index(payload[2]),
180 source: PlatformSource::try_from(payload[3])
181 .map_err(|_| Hidpp20Error::UnsupportedResponse)?,
182 auto_platform_index: optional_index(payload[4]),
183 auto_descriptor_index: optional_index(payload[5]),
184 })
185 }
186}
187
188fn optional_index(value: u8) -> Option<u8> {
189 (value != 0xff).then_some(value)
190}