use std::sync::Arc;
use num_enum::TryFromPrimitive;
use crate::{
channel::HidppChannel,
feature::{CreatableFeature, Feature, FeatureEndpoint, hosts_info::HostIndex},
protocol::v20::Hidpp20Error,
};
bitflags::bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct MultiPlatformCapabilities: u16 {
const OS_DETECTION = 1 << 0;
const SET_HOST_PLATFORM = 1 << 1;
}
}
bitflags::bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct OsMask: u16 {
const WINDOWS = 1 << 0;
const WINDOWS_EMBEDDED = 1 << 1;
const LINUX = 1 << 2;
const CHROME = 1 << 3;
const ANDROID = 1 << 4;
const MACOS = 1 << 5;
const IOS = 1 << 6;
const WEBOS = 1 << 7;
const TIZEN = 1 << 8;
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, TryFromPrimitive)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
#[repr(u8)]
pub enum PlatformSource {
Default = 0,
Auto = 1,
Manual = 2,
Software = 3,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct MultiPlatformInfo {
pub capabilities: MultiPlatformCapabilities,
pub platform_count: u8,
pub descriptor_count: u8,
pub host_count: u8,
pub current_host: HostIndex,
pub current_host_platform: Option<u8>,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct PlatformDescriptor {
pub platform_index: u8,
pub descriptor_index: u8,
pub os_mask: OsMask,
pub from_version: u8,
pub from_revision: u8,
pub to_version: u8,
pub to_revision: u8,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct HostPlatform {
pub host_index: HostIndex,
pub status: u8,
pub platform_index: Option<u8>,
pub source: PlatformSource,
pub auto_platform_index: Option<u8>,
pub auto_descriptor_index: Option<u8>,
}
#[derive(Clone)]
pub struct MultiPlatformFeature {
endpoint: FeatureEndpoint,
}
impl CreatableFeature for MultiPlatformFeature {
const ID: u16 = 0x4531;
const STARTING_VERSION: u8 = 1;
fn new(chan: Arc<HidppChannel>, device_index: u8, feature_index: u8) -> Self {
Self {
endpoint: FeatureEndpoint::new(chan, device_index, feature_index),
}
}
}
impl Feature for MultiPlatformFeature {}
impl MultiPlatformFeature {
pub async fn get_feature_infos(&self) -> Result<MultiPlatformInfo, Hidpp20Error> {
let payload = self.endpoint.call(0, [0; 3]).await?.extend_payload();
Ok(MultiPlatformInfo {
capabilities: MultiPlatformCapabilities::from_bits_retain(u16::from_be_bytes([
payload[0], payload[1],
])),
platform_count: payload[2],
descriptor_count: payload[3],
host_count: payload[4],
current_host: HostIndex::from(payload[5]),
current_host_platform: optional_index(payload[6]),
})
}
pub async fn get_platform_descriptor(
&self,
descriptor_index: u8,
) -> Result<PlatformDescriptor, Hidpp20Error> {
let payload = self
.endpoint
.call(1, [descriptor_index, 0, 0])
.await?
.extend_payload();
Ok(PlatformDescriptor {
platform_index: payload[0],
descriptor_index: payload[1],
os_mask: OsMask::from_bits_retain(u16::from_be_bytes([payload[2], payload[3]])),
from_version: payload[4],
from_revision: payload[5],
to_version: payload[6],
to_revision: payload[7],
})
}
pub async fn get_host_platform(&self, host: HostIndex) -> Result<HostPlatform, Hidpp20Error> {
let payload = self
.endpoint
.call(2, [u8::from(host), 0, 0])
.await?
.extend_payload();
Ok(HostPlatform {
host_index: HostIndex::from(payload[0]),
status: payload[1],
platform_index: optional_index(payload[2]),
source: PlatformSource::try_from(payload[3])
.map_err(|_| Hidpp20Error::UnsupportedResponse)?,
auto_platform_index: optional_index(payload[4]),
auto_descriptor_index: optional_index(payload[5]),
})
}
}
fn optional_index(value: u8) -> Option<u8> {
(value != 0xff).then_some(value)
}