Skip to main content

openseries/
device_manager.rs

1use 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/// Configuration used while discovering and communicating with devices.
12#[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    /// Sets the maximum wait for a response to a device command.
28    pub fn with_timeout(mut self, timeout: Duration) -> Self {
29        self.timeout = timeout;
30        self
31    }
32
33    /// Returns the configured device-response timeout.
34    pub fn timeout(self) -> Duration {
35        self.timeout
36    }
37}
38
39/// Discovers supported devices using the default options.
40pub fn discover_devices() -> Result<Vec<Device>> {
41    discover_devices_with_options(DiscoveryOptions::default())
42}
43
44/// Discovers supported devices using the supplied communication options.
45pub 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}