use num_enum::{IntoPrimitive, TryFromPrimitive};
use crate::protocol::v20::{ErrorType, Hidpp20Error};
pub(super) fn be16(payload: &[u8; 16], offset: usize) -> u16 {
u16::from_be_bytes([payload[offset], payload[offset + 1]])
}
bitflags::bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct ControlCapabilities: u8 {
const HAS_EVENTS = 1 << 0;
const HAS_LINEAR_LEVELS = 1 << 1;
const HAS_NON_LINEAR_LEVELS = 1 << 2;
const HAS_DYNAMIC_MAXIMUM = 1 << 3;
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct ControlInfo {
pub capabilities: ControlCapabilities,
pub min: u16,
pub max: u16,
pub resolution: u16,
pub max_levels: u8,
}
impl ControlInfo {
pub(super) fn from_payload(payload: &[u8; 16]) -> Self {
Self {
capabilities: ControlCapabilities::from_bits_retain(payload[0]),
min: be16(payload, 1),
max: be16(payload, 3),
resolution: be16(payload, 5),
max_levels: payload[7] & 0x0f,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub enum LevelConfig {
Linear {
min: u16,
max: u16,
step: u16,
},
NonLinear {
start_index: u8,
level_count: u8,
values: Vec<u16>,
},
}
impl LevelConfig {
pub(super) fn from_payload(payload: &[u8; 16]) -> Self {
let flags = payload[0];
if flags & 1 != 0 {
LevelConfig::Linear {
min: be16(payload, 2),
max: be16(payload, 4),
step: be16(payload, 6),
}
} else {
let valid_count = usize::from((flags >> 5) & 0x07);
let values = (0..valid_count).map(|i| be16(payload, 2 + 2 * i)).collect();
LevelConfig::NonLinear {
start_index: payload[1] >> 4,
level_count: payload[1] & 0x0f,
values,
}
}
}
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub enum SetLevels {
Reset,
Linear {
min: u16,
max: u16,
step: u16,
},
NonLinear {
start_index: u8,
level_count: u8,
values: Vec<u16>,
},
}
impl SetLevels {
pub(super) fn to_payload(&self) -> Result<[u8; 16], Hidpp20Error> {
let mut args = [0u8; 16];
match self {
SetLevels::Reset => {
args[0] = 1 << 1;
}
SetLevels::Linear { min, max, step } => {
args[0] = 1; args[2..4].copy_from_slice(&min.to_be_bytes());
args[4..6].copy_from_slice(&max.to_be_bytes());
args[6..8].copy_from_slice(&step.to_be_bytes());
}
SetLevels::NonLinear {
start_index,
level_count,
values,
} => {
if !(1..=7).contains(&values.len()) || *start_index > 0x0f || *level_count > 0x0f {
return Err(Hidpp20Error::Feature(ErrorType::InvalidArgument));
}
let valid_count = (values.len() as u8) & 0x07;
args[0] = valid_count << 5; args[1] = (start_index << 4) | (level_count & 0x0f);
for (i, value) in values.iter().take(7).enumerate() {
args[2 + 2 * i..4 + 2 * i].copy_from_slice(&value.to_be_bytes());
}
}
}
Ok(args)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
#[repr(u8)]
pub enum IlluminationState {
Off = 0,
On = 1,
}
impl From<bool> for IlluminationState {
fn from(value: bool) -> Self {
if value { Self::On } else { Self::Off }
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
#[repr(u8)]
pub enum BrightnessClampedSource {
Unknown = 0,
HidPlusPlus = 1,
Button = 2,
}
pub(super) fn illumination_state(byte: u8) -> Result<IlluminationState, Hidpp20Error> {
IlluminationState::try_from(byte & 1).map_err(|_| Hidpp20Error::UnsupportedResponse)
}