Skip to main content

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}