hidpp/feature/illumination/
types.rs1use num_enum::{IntoPrimitive, TryFromPrimitive};
4
5use crate::protocol::v20::{ErrorType, Hidpp20Error};
6
7pub(super) fn be16(payload: &[u8; 16], offset: usize) -> u16 {
9 u16::from_be_bytes([payload[offset], payload[offset + 1]])
10}
11
12bitflags::bitflags! {
13 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
16 #[cfg_attr(feature = "serde", derive(serde::Serialize))]
17 pub struct ControlCapabilities: u8 {
18 const HAS_EVENTS = 1 << 0;
20 const HAS_LINEAR_LEVELS = 1 << 1;
22 const HAS_NON_LINEAR_LEVELS = 1 << 2;
24 const HAS_DYNAMIC_MAXIMUM = 1 << 3;
26 }
27}
28
29#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
33#[cfg_attr(feature = "serde", derive(serde::Serialize))]
34#[non_exhaustive]
35pub struct ControlInfo {
36 pub capabilities: ControlCapabilities,
38 pub min: u16,
41 pub max: u16,
43 pub resolution: u16,
45 pub max_levels: u8,
48}
49
50impl ControlInfo {
51 pub(super) fn from_payload(payload: &[u8; 16]) -> Self {
52 Self {
53 capabilities: ControlCapabilities::from_bits_retain(payload[0]),
54 min: be16(payload, 1),
55 max: be16(payload, 3),
56 resolution: be16(payload, 5),
57 max_levels: payload[7] & 0x0f,
58 }
59 }
60}
61
62#[derive(Clone, Debug, PartialEq, Eq, Hash)]
67#[cfg_attr(feature = "serde", derive(serde::Serialize))]
68pub enum LevelConfig {
69 Linear {
71 min: u16,
73 max: u16,
75 step: u16,
77 },
78 NonLinear {
80 start_index: u8,
82 level_count: u8,
84 values: Vec<u16>,
86 },
87}
88
89impl LevelConfig {
90 pub(super) fn from_payload(payload: &[u8; 16]) -> Self {
91 let flags = payload[0];
92 if flags & 1 != 0 {
93 LevelConfig::Linear {
94 min: be16(payload, 2),
95 max: be16(payload, 4),
96 step: be16(payload, 6),
97 }
98 } else {
99 let valid_count = usize::from((flags >> 5) & 0x07);
100 let values = (0..valid_count).map(|i| be16(payload, 2 + 2 * i)).collect();
101 LevelConfig::NonLinear {
102 start_index: payload[1] >> 4,
103 level_count: payload[1] & 0x0f,
104 values,
105 }
106 }
107 }
108}
109
110#[derive(Clone, Debug, PartialEq, Eq, Hash)]
113#[cfg_attr(feature = "serde", derive(serde::Serialize))]
114pub enum SetLevels {
115 Reset,
117 Linear {
119 min: u16,
121 max: u16,
123 step: u16,
125 },
126 NonLinear {
128 start_index: u8,
130 level_count: u8,
133 values: Vec<u16>,
135 },
136}
137
138impl SetLevels {
139 pub(super) fn to_payload(&self) -> Result<[u8; 16], Hidpp20Error> {
141 let mut args = [0u8; 16];
142 match self {
143 SetLevels::Reset => {
144 args[0] = 1 << 1;
146 }
147 SetLevels::Linear { min, max, step } => {
148 args[0] = 1; args[2..4].copy_from_slice(&min.to_be_bytes());
150 args[4..6].copy_from_slice(&max.to_be_bytes());
151 args[6..8].copy_from_slice(&step.to_be_bytes());
152 }
153 SetLevels::NonLinear {
154 start_index,
155 level_count,
156 values,
157 } => {
158 if !(1..=7).contains(&values.len()) || *start_index > 0x0f || *level_count > 0x0f {
159 return Err(Hidpp20Error::Feature(ErrorType::InvalidArgument));
160 }
161 let valid_count = (values.len() as u8) & 0x07;
162 args[0] = valid_count << 5; args[1] = (start_index << 4) | (level_count & 0x0f);
164 for (i, value) in values.iter().take(7).enumerate() {
165 args[2 + 2 * i..4 + 2 * i].copy_from_slice(&value.to_be_bytes());
166 }
167 }
168 }
169 Ok(args)
170 }
171}
172
173#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
175#[cfg_attr(feature = "serde", derive(serde::Serialize))]
176#[non_exhaustive]
177#[repr(u8)]
178pub enum IlluminationState {
179 Off = 0,
181 On = 1,
183}
184
185impl From<bool> for IlluminationState {
186 fn from(value: bool) -> Self {
187 if value { Self::On } else { Self::Off }
188 }
189}
190
191#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
193#[cfg_attr(feature = "serde", derive(serde::Serialize))]
194#[non_exhaustive]
195#[repr(u8)]
196pub enum BrightnessClampedSource {
197 Unknown = 0,
199 HidPlusPlus = 1,
201 Button = 2,
203}
204
205pub(super) fn illumination_state(byte: u8) -> Result<IlluminationState, Hidpp20Error> {
207 IlluminationState::try_from(byte & 1).map_err(|_| Hidpp20Error::UnsupportedResponse)
208}