use std::sync::Arc;
use num_enum::{IntoPrimitive, TryFromPrimitive};
use crate::{
channel::HidppChannel,
event::EventEmitter,
protocol::v10::{self, Hidpp10Error},
receiver::{ListenerDropGuard, RECEIVER_DEVICE_INDEX, ReceiverError},
};
pub const VPID_PAIRS: &[(u16, u16)] = &[(0x046d, 0xc52b), (0x046d, 0xc532)];
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, IntoPrimitive, TryFromPrimitive)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
#[repr(u8)]
pub enum Register {
Connections = 0x02,
ReceiverInfo = 0xb5,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, IntoPrimitive, TryFromPrimitive)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
#[repr(u8)]
pub enum InfoSubRegister {
ReceiverInfo = 0x03,
DevicePairingInformation = 0x50,
DeviceCodename = 0x60,
}
#[derive(Clone)]
pub struct Receiver {
chan: Arc<HidppChannel>,
emitter: Arc<EventEmitter<Event>>,
_listener: Arc<ListenerDropGuard>,
}
impl Receiver {
pub fn new(chan: Arc<HidppChannel>) -> Result<Self, ReceiverError> {
if !VPID_PAIRS.contains(&(chan.vendor_id, chan.product_id)) {
return Err(ReceiverError::UnknownReceiver);
}
let emitter = Arc::new(EventEmitter::new());
let hdl = chan.add_msg_listener({
let emitter = Arc::clone(&emitter);
move |raw, matched| {
if matched {
return;
}
let parsed = v10::Message::from(raw);
let header = parsed.header();
let payload = parsed.extend_payload();
if header.sub_id != 0x41 {
return;
}
let Ok(kind) = DeviceKind::try_from(payload[1] & 0x0f) else {
return;
};
emitter.emit(Event::DeviceConnection(DeviceConnection {
index: header.device_index,
kind,
encrypted: payload[1] & (1 << 4) != 0,
online: payload[1] & (1 << 6) == 0,
wpid: u16::from_le_bytes(payload[2..=3].try_into().unwrap()),
}));
}
});
Ok(Receiver {
_listener: Arc::new(ListenerDropGuard {
chan: Arc::clone(&chan),
hdl,
}),
chan,
emitter,
})
}
pub fn listen(&self) -> async_channel::Receiver<Event> {
self.emitter.create_receiver()
}
pub async fn count_pairings(&self) -> Result<u8, ReceiverError> {
let response = self
.chan
.read_register(
RECEIVER_DEVICE_INDEX,
Register::Connections.into(),
[0u8; 3],
)
.await?;
Ok(response[1])
}
pub async fn trigger_device_arrival(&self) -> Result<(), ReceiverError> {
self.chan
.write_register(
RECEIVER_DEVICE_INDEX,
Register::Connections.into(),
[0x02, 0x00, 0x00],
)
.await?;
Ok(())
}
pub async fn get_receiver_info(&self) -> Result<ReceiverInfo, ReceiverError> {
let response = self
.chan
.read_long_register(
RECEIVER_DEVICE_INDEX,
Register::ReceiverInfo.into(),
[InfoSubRegister::ReceiverInfo.into(), 0, 0],
)
.await?;
Ok(ReceiverInfo {
serial_number: hex::encode_upper(&response[1..=4]),
pairing_slots: response[6],
})
}
pub async fn get_device_pairing_information(
&self,
device_index: u8,
) -> Result<DevicePairingInformation, ReceiverError> {
let response = self
.chan
.read_long_register(
RECEIVER_DEVICE_INDEX,
Register::ReceiverInfo.into(),
[
u8::from(InfoSubRegister::DevicePairingInformation) | (device_index & 0x0f),
0x00,
0x00,
],
)
.await?;
Ok(DevicePairingInformation {
wpid: u16::from_le_bytes(response[2..=3].try_into().unwrap()),
kind: DeviceKind::try_from(response[1] & 0x0f)
.map_err(|_| Hidpp10Error::UnsupportedResponse)?,
encrypted: response[1] & (1 << 4) != 0,
online: response[1] & (1 << 6) == 0,
unit_id: response[4..=7].try_into().unwrap(),
})
}
pub async fn get_unique_id(&self) -> Result<String, ReceiverError> {
self.get_receiver_info().await.map(|i| i.serial_number)
}
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct ReceiverInfo {
pub serial_number: String,
pub pairing_slots: u8,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct DevicePairingInformation {
pub wpid: u16,
pub kind: DeviceKind,
pub encrypted: bool,
pub online: bool,
pub unit_id: [u8; 4],
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, IntoPrimitive, TryFromPrimitive)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
#[repr(u8)]
pub enum DeviceKind {
Unknown = 0x00,
Keyboard = 0x01,
Mouse = 0x02,
Numpad = 0x03,
Presenter = 0x04,
Remote = 0x05,
Trackball = 0x06,
Touchpad = 0x07,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub struct DeviceConnection {
pub index: u8,
pub kind: DeviceKind,
pub encrypted: bool,
pub online: bool,
pub wpid: u16,
}
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
#[non_exhaustive]
pub enum Event {
DeviceConnection(DeviceConnection),
}