Skip to main content

cue_sdk/
device.rs

1use core::ffi::c_char;
2use std::fmt;
3
4use bitflags::bitflags;
5use cue_sdk_sys as ffi;
6
7// ---------------------------------------------------------------------------
8// DeviceId
9// ---------------------------------------------------------------------------
10
11/// An opaque device identifier returned by the SDK.
12///
13/// This is a fixed-size byte array that can be cheaply round-tripped through
14/// FFI without allocation.
15#[derive(Clone, Copy, PartialEq, Eq, Hash)]
16pub struct DeviceId(pub(crate) ffi::CorsairDeviceId);
17
18impl DeviceId {
19    /// View the raw bytes as a C string pointer.
20    pub(crate) fn as_ptr(&self) -> *const c_char {
21        self.0.as_ptr()
22    }
23
24    /// Create from the FFI type (copy).
25    pub(crate) fn from_ffi(raw: ffi::CorsairDeviceId) -> Self {
26        Self(raw)
27    }
28}
29
30impl fmt::Debug for DeviceId {
31    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32        write!(f, "DeviceId(\"{}\")", self)
33    }
34}
35
36impl fmt::Display for DeviceId {
37    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38        let bytes = self.0.map(|c| c as u8);
39        let len = bytes.iter().position(|&b| b == 0).unwrap_or(bytes.len());
40        let s = std::str::from_utf8(&bytes[..len]).unwrap_or("<invalid utf8>");
41        f.write_str(s)
42    }
43}
44
45// ---------------------------------------------------------------------------
46// DeviceType (bitflags)
47// ---------------------------------------------------------------------------
48
49bitflags! {
50    /// Bitmask of device types used for filtering in [`Session::get_devices`].
51    #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
52    pub struct DeviceType: u32 {
53        const UNKNOWN           = ffi::CorsairDeviceType_CDT_Unknown;
54        const KEYBOARD          = ffi::CorsairDeviceType_CDT_Keyboard;
55        const MOUSE             = ffi::CorsairDeviceType_CDT_Mouse;
56        const MOUSEMAT          = ffi::CorsairDeviceType_CDT_Mousemat;
57        const HEADSET           = ffi::CorsairDeviceType_CDT_Headset;
58        const HEADSET_STAND     = ffi::CorsairDeviceType_CDT_HeadsetStand;
59        const FAN_LED_CONTROLLER = ffi::CorsairDeviceType_CDT_FanLedController;
60        const LED_CONTROLLER    = ffi::CorsairDeviceType_CDT_LedController;
61        const MEMORY_MODULE     = ffi::CorsairDeviceType_CDT_MemoryModule;
62        const COOLER            = ffi::CorsairDeviceType_CDT_Cooler;
63        const MOTHERBOARD       = ffi::CorsairDeviceType_CDT_Motherboard;
64        const GRAPHICS_CARD     = ffi::CorsairDeviceType_CDT_GraphicsCard;
65        const TOUCHBAR          = ffi::CorsairDeviceType_CDT_Touchbar;
66        const GAME_CONTROLLER   = ffi::CorsairDeviceType_CDT_GameController;
67        const ALL               = ffi::CorsairDeviceType_CDT_All;
68    }
69}
70
71// ---------------------------------------------------------------------------
72// DeviceInfo
73// ---------------------------------------------------------------------------
74
75/// Information about a connected Corsair device.
76#[derive(Debug, Clone)]
77pub struct DeviceInfo {
78    /// The device type bitmask.
79    pub device_type: DeviceType,
80    /// Unique device identifier.
81    pub id: DeviceId,
82    /// Serial number string.
83    pub serial: String,
84    /// Model name string.
85    pub model: String,
86    /// Number of LEDs on this device.
87    pub led_count: i32,
88    /// Number of channels (for DIY devices).
89    pub channel_count: i32,
90}
91
92impl DeviceInfo {
93    /// Convert from the FFI struct.
94    pub(crate) fn from_ffi(raw: &ffi::CorsairDeviceInfo) -> Self {
95        Self {
96            device_type: DeviceType::from_bits_truncate(raw.type_),
97            id: DeviceId::from_ffi(raw.id),
98            serial: c_char_array_to_string(&raw.serial),
99            model: c_char_array_to_string(&raw.model),
100            led_count: raw.ledCount,
101            channel_count: raw.channelCount,
102        }
103    }
104}
105
106/// Convert a fixed-size `c_char` array to an owned `String`, stopping at the
107/// first null byte.
108fn c_char_array_to_string(arr: &[c_char]) -> String {
109    let bytes: Vec<u8> = arr
110        .iter()
111        .map(|&c| c as u8)
112        .take_while(|&b| b != 0)
113        .collect();
114    String::from_utf8_lossy(&bytes).into_owned()
115}