#[cfg(test)]
mod tests;
use std::sync::Arc;
use num_enum::{IntoPrimitive, TryFromPrimitive};
use crate::{
channel::HidppChannel,
feature::{
CreatableFeature, Feature, FeatureEndpoint, hosts_info::HostIndex,
reprog_controls::ControlId,
},
protocol::v20::Hidpp20Error,
};
bitflags::bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct RemappableCapabilities: u16 {
const KEYBOARD_REPORT = 1 << 0;
const MOUSE_BUTTONS = 1 << 1;
const X_DISPLACEMENT = 1 << 2;
const Y_DISPLACEMENT = 1 << 3;
const VERTICAL_ROLLER = 1 << 4;
const HORIZONTAL_ROLLER = 1 << 5;
const CONSUMER_CONTROL = 1 << 6;
const INTERNAL_FUNCTION = 1 << 7;
const POWER_KEY = 1 << 8;
}
}
bitflags::bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct ModifierMask: u8 {
const LEFT_CTRL = 1 << 0;
const LEFT_SHIFT = 1 << 1;
const LEFT_ALT = 1 << 2;
const LEFT_GUI = 1 << 3;
const RIGHT_CTRL = 1 << 4;
const RIGHT_SHIFT = 1 << 5;
const RIGHT_ALT = 1 << 6;
const RIGHT_GUI = 1 << 7;
}
}
bitflags::bitflags! {
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct HostMask: u8 {
const HOST_1 = 1 << 0;
const HOST_2 = 1 << 1;
const HOST_3 = 1 << 2;
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, IntoPrimitive, TryFromPrimitive)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
#[repr(u8)]
pub enum ActionId {
SendKeyboard = 0x01,
SendMouseButton = 0x02,
SendXDisplacement = 0x03,
SendYDisplacement = 0x04,
SendVerticalRoller = 0x05,
SendHorizontalRoller = 0x06,
SendConsumerControl = 0x07,
ExecuteInternalFunction = 0x08,
SendPowerKey = 0x09,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct RemappableInfo {
pub count: u8,
pub host_count: u8,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct PersistentAction {
pub cid: ControlId,
pub host: HostIndex,
pub action_id: ActionId,
pub value: u16,
pub modifier_mask: ModifierMask,
pub remapped: bool,
}
impl PersistentAction {
fn from_payload(payload: &[u8; 16]) -> Result<Self, Hidpp20Error> {
Ok(Self {
cid: ControlId::from(u16::from_be_bytes([payload[0], payload[1]])),
host: HostIndex::from(payload[2]),
action_id: ActionId::try_from(payload[3])
.map_err(|_| Hidpp20Error::UnsupportedResponse)?,
value: u16::from_be_bytes([payload[4], payload[5]]),
modifier_mask: ModifierMask::from_bits_retain(payload[6]),
remapped: payload[7] & 1 != 0,
})
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct PersistentActionConfig {
pub action_id: ActionId,
pub value: u16,
pub modifier_mask: ModifierMask,
}
#[derive(Clone)]
pub struct PersistentRemappableActionFeature {
endpoint: FeatureEndpoint,
}
impl CreatableFeature for PersistentRemappableActionFeature {
const ID: u16 = 0x1c00;
const STARTING_VERSION: u8 = 0;
fn new(chan: Arc<HidppChannel>, device_index: u8, feature_index: u8) -> Self {
Self {
endpoint: FeatureEndpoint::new(chan, device_index, feature_index),
}
}
}
impl Feature for PersistentRemappableActionFeature {}
impl PersistentRemappableActionFeature {
pub async fn get_feature_info(&self) -> Result<RemappableCapabilities, Hidpp20Error> {
let payload = self.endpoint.call(0, [0; 3]).await?.extend_payload();
Ok(RemappableCapabilities::from_bits_retain(
u16::from_be_bytes([payload[0], payload[1]]),
))
}
pub async fn get_count(&self) -> Result<RemappableInfo, Hidpp20Error> {
let payload = self.endpoint.call(1, [0; 3]).await?.extend_payload();
Ok(RemappableInfo {
count: payload[0],
host_count: payload[1],
})
}
pub async fn get_cid_info(
&self,
index: u8,
host: HostIndex,
) -> Result<ControlId, Hidpp20Error> {
let payload = self
.endpoint
.call(2, [index, u8::from(host), 0])
.await?
.extend_payload();
Ok(ControlId::from(u16::from_be_bytes([
payload[0], payload[1],
])))
}
pub async fn get_persistent_action(
&self,
cid: ControlId,
host: HostIndex,
) -> Result<PersistentAction, Hidpp20Error> {
let [cid_hi, cid_lo] = u16::from(cid).to_be_bytes();
let payload = self
.endpoint
.call(3, [cid_hi, cid_lo, u8::from(host)])
.await?
.extend_payload();
PersistentAction::from_payload(&payload)
}
pub async fn set_persistent_action(
&self,
cid: ControlId,
host: HostIndex,
config: PersistentActionConfig,
) -> Result<(), Hidpp20Error> {
let [cid_hi, cid_lo] = u16::from(cid).to_be_bytes();
let [value_hi, value_lo] = config.value.to_be_bytes();
let mut args = [0; 16];
args[..7].copy_from_slice(&[
cid_hi,
cid_lo,
u8::from(host),
config.action_id.into(),
value_hi,
value_lo,
config.modifier_mask.bits(),
]);
self.endpoint.call_long(4, args).await?;
Ok(())
}
pub async fn reset_persistent_action(
&self,
cid: ControlId,
host: HostIndex,
) -> Result<(), Hidpp20Error> {
let [cid_hi, cid_lo] = u16::from(cid).to_be_bytes();
self.endpoint
.call(5, [cid_hi, cid_lo, u8::from(host)])
.await?;
Ok(())
}
pub async fn reset_to_factory_settings(&self, hosts: HostMask) -> Result<(), Hidpp20Error> {
self.endpoint.call(6, [hosts.bits(), 0, 0]).await?;
Ok(())
}
}