1#[cfg(test)]
10mod tests;
11
12use num_enum::{IntoPrimitive, TryFromPrimitive};
13use openlogi_hidpp_derive::Feature;
14
15use crate::{
16 feature::{FeatureEndpoint, hosts_info::HostIndex, reprog_controls::ControlId},
17 protocol::v20::Hidpp20Error,
18};
19
20bitflags::bitflags! {
21 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
24 #[cfg_attr(feature = "serde", derive(serde::Serialize))]
25 pub struct RemappableCapabilities: u16 {
26 const KEYBOARD_REPORT = 1 << 0;
28 const MOUSE_BUTTONS = 1 << 1;
30 const X_DISPLACEMENT = 1 << 2;
32 const Y_DISPLACEMENT = 1 << 3;
34 const VERTICAL_ROLLER = 1 << 4;
36 const HORIZONTAL_ROLLER = 1 << 5;
38 const CONSUMER_CONTROL = 1 << 6;
40 const INTERNAL_FUNCTION = 1 << 7;
42 const POWER_KEY = 1 << 8;
44 }
45}
46
47bitflags::bitflags! {
48 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
52 #[cfg_attr(feature = "serde", derive(serde::Serialize))]
53 pub struct ModifierMask: u8 {
54 const LEFT_CTRL = 1 << 0;
56 const LEFT_SHIFT = 1 << 1;
58 const LEFT_ALT = 1 << 2;
60 const LEFT_GUI = 1 << 3;
62 const RIGHT_CTRL = 1 << 4;
64 const RIGHT_SHIFT = 1 << 5;
66 const RIGHT_ALT = 1 << 6;
68 const RIGHT_GUI = 1 << 7;
70 }
71}
72
73bitflags::bitflags! {
74 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
77 #[cfg_attr(feature = "serde", derive(serde::Serialize))]
78 pub struct HostMask: u8 {
79 const HOST_1 = 1 << 0;
81 const HOST_2 = 1 << 1;
83 const HOST_3 = 1 << 2;
85 }
86}
87
88#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
90#[cfg_attr(feature = "serde", derive(serde::Serialize))]
91#[non_exhaustive]
92#[repr(u8)]
93pub enum ActionId {
94 SendKeyboard = 0x01,
96 SendMouseButton = 0x02,
98 SendXDisplacement = 0x03,
100 SendYDisplacement = 0x04,
102 SendVerticalRoller = 0x05,
104 SendHorizontalRoller = 0x06,
106 SendConsumerControl = 0x07,
108 ExecuteInternalFunction = 0x08,
110 SendPowerKey = 0x09,
112}
113
114#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
117#[cfg_attr(feature = "serde", derive(serde::Serialize))]
118#[non_exhaustive]
119pub struct RemappableInfo {
120 pub count: u8,
122 pub host_count: u8,
124}
125
126#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
129#[cfg_attr(feature = "serde", derive(serde::Serialize))]
130#[non_exhaustive]
131pub struct PersistentAction {
132 pub cid: ControlId,
134 pub host: HostIndex,
136 pub action_id: ActionId,
138 pub value: u16,
140 pub modifier_mask: ModifierMask,
142 pub remapped: bool,
144}
145
146impl PersistentAction {
147 fn from_payload(payload: &[u8; 16]) -> Result<Self, Hidpp20Error> {
148 Ok(Self {
149 cid: ControlId::from(u16::from_be_bytes([payload[0], payload[1]])),
150 host: HostIndex::from(payload[2]),
151 action_id: ActionId::try_from(payload[3])
152 .map_err(|_| Hidpp20Error::UnsupportedResponse)?,
153 value: u16::from_be_bytes([payload[4], payload[5]]),
154 modifier_mask: ModifierMask::from_bits_retain(payload[6]),
155 remapped: payload[7] & 1 != 0,
156 })
157 }
158}
159
160#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
163#[cfg_attr(feature = "serde", derive(serde::Serialize))]
164pub struct PersistentActionConfig {
165 pub action_id: ActionId,
167 pub value: u16,
169 pub modifier_mask: ModifierMask,
171}
172
173#[derive(Clone, Feature)]
175#[creatable(id = 0x1c00, version = 0)]
176pub struct PersistentRemappableActionFeature {
177 endpoint: FeatureEndpoint,
179}
180
181impl PersistentRemappableActionFeature {
182 pub async fn get_feature_info(&self) -> Result<RemappableCapabilities, Hidpp20Error> {
184 let payload = self.endpoint.call(0, [0; 3]).await?.extend_payload();
185 Ok(RemappableCapabilities::from_bits_retain(
186 u16::from_be_bytes([payload[0], payload[1]]),
187 ))
188 }
189
190 pub async fn get_count(&self) -> Result<RemappableInfo, Hidpp20Error> {
192 let payload = self.endpoint.call(1, [0; 3]).await?.extend_payload();
193 Ok(RemappableInfo {
194 count: payload[0],
195 host_count: payload[1],
196 })
197 }
198
199 pub async fn get_cid_info(
201 &self,
202 index: u8,
203 host: HostIndex,
204 ) -> Result<ControlId, Hidpp20Error> {
205 let payload = self
206 .endpoint
207 .call(2, [index, u8::from(host), 0])
208 .await?
209 .extend_payload();
210 Ok(ControlId::from(u16::from_be_bytes([
211 payload[0], payload[1],
212 ])))
213 }
214
215 pub async fn get_persistent_action(
217 &self,
218 cid: ControlId,
219 host: HostIndex,
220 ) -> Result<PersistentAction, Hidpp20Error> {
221 let [cid_hi, cid_lo] = u16::from(cid).to_be_bytes();
222 let payload = self
223 .endpoint
224 .call(3, [cid_hi, cid_lo, u8::from(host)])
225 .await?
226 .extend_payload();
227 PersistentAction::from_payload(&payload)
228 }
229
230 pub async fn set_persistent_action(
235 &self,
236 cid: ControlId,
237 host: HostIndex,
238 config: PersistentActionConfig,
239 ) -> Result<(), Hidpp20Error> {
240 let [cid_hi, cid_lo] = u16::from(cid).to_be_bytes();
241 let [value_hi, value_lo] = config.value.to_be_bytes();
242 let mut args = [0; 16];
243 args[..7].copy_from_slice(&[
244 cid_hi,
245 cid_lo,
246 u8::from(host),
247 config.action_id.into(),
248 value_hi,
249 value_lo,
250 config.modifier_mask.bits(),
251 ]);
252 self.endpoint.call_long(4, args).await?;
253 Ok(())
254 }
255
256 pub async fn reset_persistent_action(
258 &self,
259 cid: ControlId,
260 host: HostIndex,
261 ) -> Result<(), Hidpp20Error> {
262 let [cid_hi, cid_lo] = u16::from(cid).to_be_bytes();
263 self.endpoint
264 .call(5, [cid_hi, cid_lo, u8::from(host)])
265 .await?;
266 Ok(())
267 }
268
269 pub async fn reset_to_factory_settings(&self, hosts: HostMask) -> Result<(), Hidpp20Error> {
271 self.endpoint.call(6, [hosts.bits(), 0, 0]).await?;
272 Ok(())
273 }
274}