hidpp/receiver/mod.rs
1//! Implements the different HID++ wireless receivers.
2//!
3//! Because of the lack of public documentation about the different receivers
4//! and their capabilities, and because I currently only own a single Bolt
5//! receiver, this module is largely incomplete. I would be more than happy for
6//! anyone who owns a different receiver, with Unifying having the highest
7//! priority, and who is willing to actively support its implementation by
8//! providing information and testing.
9//!
10//! Receivers can generally only be differentiated by their USB vendor and
11//! product IDs, so the [`detect`] function does nothing more than matching
12//! those values to the sets of known vendor and product ID pairs of the
13//! different receivers.
14
15use std::sync::Arc;
16
17use thiserror::Error;
18
19use crate::{channel::HidppChannel, protocol::v10::Hidpp10Error};
20
21/// Removes a HID++ message listener when the last receiver clone is dropped.
22///
23/// Storing this inside an `Arc` on the receiver struct prevents the
24/// `#[derive(Clone)]` copy from sharing the raw `u32` handle: cloning a
25/// receiver increments the Arc refcount, and `remove_msg_listener` is called
26/// exactly once — when the last clone's Arc refcount reaches zero.
27pub(super) struct ListenerDropGuard {
28 pub(super) chan: Arc<HidppChannel>,
29 pub(super) hdl: u32,
30}
31
32impl Drop for ListenerDropGuard {
33 fn drop(&mut self) {
34 self.chan.remove_msg_listener(self.hdl);
35 }
36}
37
38pub mod bolt;
39pub mod unifying;
40
41/// The index to use when communicating with the receiver on any HID++ channel.
42pub const RECEIVER_DEVICE_INDEX: u8 = 0xff;
43
44/// Tries to detect the receiver present on a HID++ channel.
45pub fn detect(chan: Arc<HidppChannel>) -> Option<Receiver> {
46 let vpid_pair = &(chan.vendor_id, chan.product_id);
47
48 if bolt::VPID_PAIRS.contains(vpid_pair) {
49 return bolt::Receiver::new(chan).ok().map(Receiver::Bolt);
50 }
51
52 if unifying::VPID_PAIRS.contains(vpid_pair) {
53 return unifying::Receiver::new(chan).ok().map(Receiver::Unifying);
54 }
55 None
56}
57
58/// Represents a HID++ wireless receiver.
59#[derive(Clone)]
60#[non_exhaustive]
61pub enum Receiver {
62 Bolt(bolt::Receiver),
63 Unifying(unifying::Receiver),
64}
65
66impl Receiver {
67 /// Provides a human-readable name for the receiver.
68 pub fn name(&self) -> String {
69 match self {
70 Self::Bolt(_) => "Logi Bolt Receiver",
71 Self::Unifying(_) => "Unifying Receiver",
72 }
73 .to_string()
74 }
75
76 /// Provides a string that uniquely identifies the specific receiver.
77 ///
78 /// This MAY be the serial number, but it may also be any other value that
79 /// is defined as unique.
80 pub async fn get_unique_id(&self) -> Result<String, ReceiverError> {
81 match self {
82 Self::Bolt(bolt) => bolt.get_unique_id().await,
83 Self::Unifying(unifying) => unifying.get_unique_id().await,
84 }
85 }
86}
87
88/// Represents an error returned by a receiver.
89#[derive(Debug, Error)]
90#[non_exhaustive]
91pub enum ReceiverError {
92 /// Indicates that no supported receiver could be identified on a HID++
93 /// channel.
94 #[error("no (supported) receiver could be found")]
95 UnknownReceiver,
96
97 /// Indicates that a HID++1.0 register access resulted in an error.
98 #[error("a HID++1.0 error occurred")]
99 Protocol(#[from] Hidpp10Error),
100}