hidpp/feature/persistent_remappable_action/
mod.rs1#[cfg(test)]
10mod tests;
11
12use std::sync::Arc;
13
14use num_enum::{IntoPrimitive, TryFromPrimitive};
15
16use crate::{
17 channel::HidppChannel,
18 feature::{
19 CreatableFeature, Feature, FeatureEndpoint, hosts_info::HostIndex,
20 reprog_controls::ControlId,
21 },
22 protocol::v20::Hidpp20Error,
23};
24
25bitflags::bitflags! {
26 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
29 #[cfg_attr(feature = "serde", derive(serde::Serialize))]
30 pub struct RemappableCapabilities: u16 {
31 const KEYBOARD_REPORT = 1 << 0;
33 const MOUSE_BUTTONS = 1 << 1;
35 const X_DISPLACEMENT = 1 << 2;
37 const Y_DISPLACEMENT = 1 << 3;
39 const VERTICAL_ROLLER = 1 << 4;
41 const HORIZONTAL_ROLLER = 1 << 5;
43 const CONSUMER_CONTROL = 1 << 6;
45 const INTERNAL_FUNCTION = 1 << 7;
47 const POWER_KEY = 1 << 8;
49 }
50}
51
52bitflags::bitflags! {
53 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
57 #[cfg_attr(feature = "serde", derive(serde::Serialize))]
58 pub struct ModifierMask: u8 {
59 const LEFT_CTRL = 1 << 0;
61 const LEFT_SHIFT = 1 << 1;
63 const LEFT_ALT = 1 << 2;
65 const LEFT_GUI = 1 << 3;
67 const RIGHT_CTRL = 1 << 4;
69 const RIGHT_SHIFT = 1 << 5;
71 const RIGHT_ALT = 1 << 6;
73 const RIGHT_GUI = 1 << 7;
75 }
76}
77
78bitflags::bitflags! {
79 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
82 #[cfg_attr(feature = "serde", derive(serde::Serialize))]
83 pub struct HostMask: u8 {
84 const HOST_1 = 1 << 0;
86 const HOST_2 = 1 << 1;
88 const HOST_3 = 1 << 2;
90 }
91}
92
93#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
95#[cfg_attr(feature = "serde", derive(serde::Serialize))]
96#[non_exhaustive]
97#[repr(u8)]
98pub enum ActionId {
99 SendKeyboard = 0x01,
101 SendMouseButton = 0x02,
103 SendXDisplacement = 0x03,
105 SendYDisplacement = 0x04,
107 SendVerticalRoller = 0x05,
109 SendHorizontalRoller = 0x06,
111 SendConsumerControl = 0x07,
113 ExecuteInternalFunction = 0x08,
115 SendPowerKey = 0x09,
117}
118
119#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
122#[cfg_attr(feature = "serde", derive(serde::Serialize))]
123#[non_exhaustive]
124pub struct RemappableInfo {
125 pub count: u8,
127 pub host_count: u8,
129}
130
131#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
134#[cfg_attr(feature = "serde", derive(serde::Serialize))]
135#[non_exhaustive]
136pub struct PersistentAction {
137 pub cid: ControlId,
139 pub host: HostIndex,
141 pub action_id: ActionId,
143 pub value: u16,
145 pub modifier_mask: ModifierMask,
147 pub remapped: bool,
149}
150
151impl PersistentAction {
152 fn from_payload(payload: &[u8; 16]) -> Result<Self, Hidpp20Error> {
153 Ok(Self {
154 cid: ControlId::from(u16::from_be_bytes([payload[0], payload[1]])),
155 host: HostIndex::from(payload[2]),
156 action_id: ActionId::try_from(payload[3])
157 .map_err(|_| Hidpp20Error::UnsupportedResponse)?,
158 value: u16::from_be_bytes([payload[4], payload[5]]),
159 modifier_mask: ModifierMask::from_bits_retain(payload[6]),
160 remapped: payload[7] & 1 != 0,
161 })
162 }
163}
164
165#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
168#[cfg_attr(feature = "serde", derive(serde::Serialize))]
169pub struct PersistentActionConfig {
170 pub action_id: ActionId,
172 pub value: u16,
174 pub modifier_mask: ModifierMask,
176}
177
178#[derive(Clone)]
180pub struct PersistentRemappableActionFeature {
181 endpoint: FeatureEndpoint,
183}
184
185impl CreatableFeature for PersistentRemappableActionFeature {
186 const ID: u16 = 0x1c00;
187 const STARTING_VERSION: u8 = 0;
188
189 fn new(chan: Arc<HidppChannel>, device_index: u8, feature_index: u8) -> Self {
190 Self {
191 endpoint: FeatureEndpoint::new(chan, device_index, feature_index),
192 }
193 }
194}
195
196impl Feature for PersistentRemappableActionFeature {}
197
198impl PersistentRemappableActionFeature {
199 pub async fn get_feature_info(&self) -> Result<RemappableCapabilities, Hidpp20Error> {
201 let payload = self.endpoint.call(0, [0; 3]).await?.extend_payload();
202 Ok(RemappableCapabilities::from_bits_retain(
203 u16::from_be_bytes([payload[0], payload[1]]),
204 ))
205 }
206
207 pub async fn get_count(&self) -> Result<RemappableInfo, Hidpp20Error> {
209 let payload = self.endpoint.call(1, [0; 3]).await?.extend_payload();
210 Ok(RemappableInfo {
211 count: payload[0],
212 host_count: payload[1],
213 })
214 }
215
216 pub async fn get_cid_info(
218 &self,
219 index: u8,
220 host: HostIndex,
221 ) -> Result<ControlId, Hidpp20Error> {
222 let payload = self
223 .endpoint
224 .call(2, [index, u8::from(host), 0])
225 .await?
226 .extend_payload();
227 Ok(ControlId::from(u16::from_be_bytes([
228 payload[0], payload[1],
229 ])))
230 }
231
232 pub async fn get_persistent_action(
234 &self,
235 cid: ControlId,
236 host: HostIndex,
237 ) -> Result<PersistentAction, Hidpp20Error> {
238 let [cid_hi, cid_lo] = u16::from(cid).to_be_bytes();
239 let payload = self
240 .endpoint
241 .call(3, [cid_hi, cid_lo, u8::from(host)])
242 .await?
243 .extend_payload();
244 PersistentAction::from_payload(&payload)
245 }
246
247 pub async fn set_persistent_action(
252 &self,
253 cid: ControlId,
254 host: HostIndex,
255 config: PersistentActionConfig,
256 ) -> Result<(), Hidpp20Error> {
257 let [cid_hi, cid_lo] = u16::from(cid).to_be_bytes();
258 let [value_hi, value_lo] = config.value.to_be_bytes();
259 let mut args = [0; 16];
260 args[..7].copy_from_slice(&[
261 cid_hi,
262 cid_lo,
263 u8::from(host),
264 config.action_id.into(),
265 value_hi,
266 value_lo,
267 config.modifier_mask.bits(),
268 ]);
269 self.endpoint.call_long(4, args).await?;
270 Ok(())
271 }
272
273 pub async fn reset_persistent_action(
275 &self,
276 cid: ControlId,
277 host: HostIndex,
278 ) -> Result<(), Hidpp20Error> {
279 let [cid_hi, cid_lo] = u16::from(cid).to_be_bytes();
280 self.endpoint
281 .call(5, [cid_hi, cid_lo, u8::from(host)])
282 .await?;
283 Ok(())
284 }
285
286 pub async fn reset_to_factory_settings(&self, hosts: HostMask) -> Result<(), Hidpp20Error> {
288 self.endpoint.call(6, [hosts.bits(), 0, 0]).await?;
289 Ok(())
290 }
291}