use std::sync::Arc;
use openlogi_device_registry::receiver::{ReceiverProtocol, find_receiver};
use thiserror::Error;
use crate::{channel::HidppChannel, protocol::v10::Hidpp10Error};
pub mod bolt;
pub mod unifying;
pub const RECEIVER_DEVICE_INDEX: u8 = 0xff;
const NOTIFICATIONS_REGISTER: u8 = 0x00;
const BOLT_PROTOCOL: u8 = 0x10;
pub(crate) const fn connection_encrypted(protocol: u8, device_info: u8) -> bool {
device_info & (1 << 5) != 0 || protocol == BOLT_PROTOCOL
}
bitflags::bitflags! {
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct NotificationFlags: u32 {
const WIRELESS = 0x00_0100;
const SOFTWARE_PRESENT = 0x00_0800;
}
}
impl NotificationFlags {
fn from_register(value: [u8; 3]) -> Self {
Self::from_bits_retain(u32::from_be_bytes([0, value[0], value[1], value[2]]))
}
fn to_register(self) -> [u8; 3] {
let [_, high, mid, low] = self.bits().to_be_bytes();
[high, mid, low]
}
}
pub async fn read_notification_flags(
chan: &HidppChannel,
) -> Result<NotificationFlags, Hidpp10Error> {
chan.read_register(RECEIVER_DEVICE_INDEX, NOTIFICATIONS_REGISTER, [0; 3])
.await
.map(NotificationFlags::from_register)
}
pub async fn write_notification_flags(
chan: &HidppChannel,
flags: NotificationFlags,
) -> Result<(), Hidpp10Error> {
chan.write_register(
RECEIVER_DEVICE_INDEX,
NOTIFICATIONS_REGISTER,
flags.to_register(),
)
.await
}
pub fn detect(chan: Arc<HidppChannel>) -> Option<Receiver> {
match find_receiver(chan.vendor_id, chan.product_id)?.protocol {
ReceiverProtocol::Bolt => bolt::Receiver::new(chan).ok().map(Receiver::Bolt),
ReceiverProtocol::Unifying => unifying::Receiver::new(chan).ok().map(Receiver::Unifying),
}
}
#[derive(Clone)]
#[non_exhaustive]
pub enum Receiver {
Bolt(bolt::Receiver),
Unifying(unifying::Receiver),
}
impl Receiver {
#[must_use]
pub fn name(&self) -> String {
match self {
Self::Bolt(_) => "Logi Bolt Receiver",
Self::Unifying(_) => "Unifying Receiver",
}
.to_string()
}
pub async fn get_unique_id(&self) -> Result<String, ReceiverError> {
match self {
Self::Bolt(bolt) => bolt.get_unique_id().await,
Self::Unifying(unifying) => unifying.get_unique_id().await,
}
}
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum ReceiverError {
#[error("no (supported) receiver could be found")]
UnknownReceiver,
#[error("a HID++1.0 error occurred")]
Protocol(#[from] Hidpp10Error),
}
#[cfg(test)]
mod tests {
use super::NotificationFlags;
#[test]
fn notification_flags_round_trip_unknown_bits() {
let flags = NotificationFlags::from_register([0x10, 0x01, 0x55]);
assert!(flags.contains(NotificationFlags::WIRELESS));
assert!(!flags.contains(NotificationFlags::SOFTWARE_PRESENT));
assert_eq!(flags.to_register(), [0x10, 0x01, 0x55]);
assert_eq!(
(flags | NotificationFlags::SOFTWARE_PRESENT).to_register(),
[0x10, 0x09, 0x55]
);
assert_eq!(
(flags - NotificationFlags::WIRELESS).to_register(),
[0x10, 0x00, 0x55]
);
}
}