openseries/
device_manager.rs1use crate::devices::Device;
2use crate::protocol::definitions::DEFINITIONS;
3use crate::protocol::{Endpoint, HidTransport, Identity};
4use crate::{Result, protocol::map_hid_error};
5use hidapi::HidApi;
6use std::sync::Arc;
7use std::time::Duration;
8
9const STEELSERIES_VENDOR_ID: u16 = 0x1038;
10
11#[derive(Clone, Copy, Debug)]
13#[non_exhaustive]
14pub struct DiscoveryOptions {
15 timeout: Duration,
16}
17
18impl Default for DiscoveryOptions {
19 fn default() -> Self {
20 Self {
21 timeout: Duration::from_secs(2),
22 }
23 }
24}
25
26impl DiscoveryOptions {
27 pub fn with_timeout(mut self, timeout: Duration) -> Self {
29 self.timeout = timeout;
30 self
31 }
32
33 pub fn timeout(self) -> Duration {
35 self.timeout
36 }
37}
38
39pub fn discover_devices() -> Result<Vec<Device>> {
41 discover_devices_with_options(DiscoveryOptions::default())
42}
43
44pub fn discover_devices_with_options(options: DiscoveryOptions) -> Result<Vec<Device>> {
46 let timeout_ms = i32::try_from(options.timeout.as_millis()).map_err(|_| {
47 crate::OpenSeriesError::InvalidArgument(
48 "Device timeout must not exceed 2147483647 milliseconds.".into(),
49 )
50 })?;
51 let api = Arc::new(HidApi::new().map_err(map_hid_error)?);
52 let mut devices = Vec::new();
53 for info in api
54 .device_list()
55 .filter(|info| info.vendor_id() == STEELSERIES_VENDOR_ID)
56 {
57 if !DEFINITIONS
58 .iter()
59 .any(|definition| definition.product_ids.contains(&info.product_id()))
60 {
61 continue;
62 }
63 let mut endpoint = Endpoint::inspect(&api, info);
64 if let Some(definition) = DEFINITIONS
65 .iter()
66 .copied()
67 .filter(|definition| definition.product_ids.contains(&info.product_id()))
68 .find(|definition| definition.matches(&endpoint))
69 {
70 let identity = Identity::new(definition.slug, info);
71 let report_sizes = endpoint.report_sizes();
72 let device = endpoint.take_device();
73 let transport = HidTransport::new(
74 Arc::clone(&api),
75 info.path().to_owned(),
76 device,
77 timeout_ms,
78 report_sizes,
79 );
80 devices.push((definition.connect)(identity, transport));
81 }
82 }
83 devices.sort_unstable_by(|left, right| left.id().cmp(right.id()));
84 Ok(devices)
85}