use std::sync::Arc;
use num_enum::{IntoPrimitive, TryFromPrimitive};
use super::{RECEIVER_DEVICE_INDEX, ReceiverError};
use crate::{
channel::HidppChannel,
event::EventEmitter,
nibble::U4,
protocol::v10::{self, Hidpp10Error},
};
pub const BOLT_VPID_PAIRS: &[(u16, u16)] = &[(0x046d, 0xc548)];
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, IntoPrimitive, TryFromPrimitive)]
#[non_exhaustive]
#[repr(u8)]
pub enum BoltRegister {
Connections = 0x02,
ReceiverInfo = 0xb5,
UniqueId = 0xfb,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, IntoPrimitive, TryFromPrimitive)]
#[non_exhaustive]
#[repr(u8)]
pub enum BoltInfoSubRegister {
DevicePairingInformation = 0x50,
DeviceCodename = 0x60, }
#[derive(Clone)]
pub struct BoltReceiver {
chan: Arc<HidppChannel>,
emitter: Arc<EventEmitter<BoltEvent>>,
msg_listener_hdl: u32,
}
impl BoltReceiver {
pub fn new(chan: Arc<HidppChannel>) -> Result<Self, ReceiverError> {
if !BOLT_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 v10::Message::Short(header, payload) = v10::Message::from(raw) else {
return;
};
match header.sub_id {
0x41 => {
let Ok(kind) = BoltDeviceKind::try_from(payload[1] & 0x0f) else {
return;
};
emitter.emit(BoltEvent::DeviceConnection(BoltDeviceConnection {
index: header.device_index,
kind,
encrypted: payload[1] & (1 << 5) != 0,
online: payload[1] & (1 << 6) == 0,
wpid: u16::from_le_bytes(payload[2..=3].try_into().unwrap()),
}));
},
_ => (),
}
}
});
Ok(BoltReceiver {
chan,
emitter,
msg_listener_hdl: hdl,
})
}
pub fn listen(&self) -> async_channel::Receiver<BoltEvent> {
self.emitter.create_receiver()
}
pub async fn count_pairings(&self) -> Result<u8, ReceiverError> {
let response = self
.chan
.read_register(
RECEIVER_DEVICE_INDEX,
BoltRegister::Connections.into(),
[0u8; 3],
)
.await?;
Ok(response[1])
}
pub async fn trigger_device_arrival(&self) -> Result<(), ReceiverError> {
self.chan
.write_register(RECEIVER_DEVICE_INDEX, BoltRegister::Connections.into(), [
0x02, 0x00, 0x00,
])
.await?;
Ok(())
}
pub async fn get_unique_id(&self) -> Result<String, ReceiverError> {
let response = self
.chan
.read_long_register(
RECEIVER_DEVICE_INDEX,
BoltRegister::UniqueId.into(),
[0u8; 3],
)
.await?;
Ok(core::str::from_utf8(&response)
.map_err(|_| Hidpp10Error::UnsupportedResponse)?
.to_string())
}
pub async fn get_device_pairing_information(
&self,
device_index: U4,
) -> Result<BoltDevicePairingInformation, ReceiverError> {
let response = self
.chan
.read_long_register(RECEIVER_DEVICE_INDEX, BoltRegister::ReceiverInfo.into(), [
u8::from(BoltInfoSubRegister::DevicePairingInformation) + device_index.to_lo(),
0x00,
0x00,
])
.await?;
Ok(BoltDevicePairingInformation {
wpid: u16::from_le_bytes(response[2..=3].try_into().unwrap()),
kind: BoltDeviceKind::try_from(response[1] & 0x0f)
.map_err(|_| Hidpp10Error::UnsupportedResponse)?,
encrypted: response[1] & (1 << 5) != 0,
online: response[1] & (1 << 6) == 0,
unit_id: response[4..=7].try_into().unwrap(),
})
}
pub async fn get_device_codename(&self, device_index: U4) -> Result<String, ReceiverError> {
let response = self
.chan
.read_long_register(RECEIVER_DEVICE_INDEX, BoltRegister::ReceiverInfo.into(), [
u8::from(BoltInfoSubRegister::DeviceCodename) + device_index.to_lo(),
0x01,
0x00,
])
.await?;
let end_idx = response[2] as usize;
Ok(core::str::from_utf8(&response[3..end_idx])
.map_err(|_| Hidpp10Error::UnsupportedResponse)?
.to_string())
}
}
impl Drop for BoltReceiver {
fn drop(&mut self) {
self.chan.remove_msg_listener(self.msg_listener_hdl);
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub struct BoltDevicePairingInformation {
wpid: u16,
pub kind: BoltDeviceKind,
pub encrypted: bool,
pub online: bool,
unit_id: [u8; 4],
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug, IntoPrimitive, TryFromPrimitive)]
#[non_exhaustive]
#[repr(u8)]
pub enum BoltDeviceKind {
Unknown = 0x00,
Keyboard = 0x01,
Mouse = 0x02,
Numpad = 0x03,
Presenter = 0x04,
Remote = 0x07,
Trackball = 0x08,
Touchpad = 0x09,
Tablet = 0x0a,
Gamepad = 0x0b,
Joystick = 0x0c,
Headset = 0x0d,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub enum BoltEvent {
DeviceConnection(BoltDeviceConnection),
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
#[non_exhaustive]
pub struct BoltDeviceConnection {
pub index: u8,
pub kind: BoltDeviceKind,
pub encrypted: bool,
pub online: bool,
pub wpid: u16,
}