cue_sdk/led/
mod.rs

1//! Contains the `CueLed` struct, and all of it's associated functionality and errors.
2use cue_sdk_sys as ffi;
3
4mod id;
5
6use crate::device::DeviceIndex;
7use crate::errors::{get_last_error, CueSdkError, CueSdkErrorResult};
8use crate::internal::CuePropertyValueHolder;
9use crate::led::CheckColorError::{LedColorError, SdkError};
10use failure::_core::ops::Range;
11
12pub use id::{
13    CommanderLedId, DeviceLedId, GraphicsCardLedId, HeadsetLedId, HeadsetStandLedId,
14    IntegratedLedId, KeyboardLedId, LedId, MotherboardLedId, MouseMatLedId,
15};
16
17/// This struct contains static information about the led (id, position, device index) as well
18/// as the last color we wrote to the color buffer, and the last color that we manually checked
19/// (confirmed read from device).
20#[derive(Debug, Clone, PartialEq)]
21pub struct CueLed {
22    pub id: LedId,
23    pub position: LedPosition,
24    pub(crate) device_index: DeviceIndex,
25    pub last_checked_color: Option<LedColor>,
26    pub last_buffed_color: Option<LedColor>,
27}
28
29/// The two-variant enum of errors that can occur when checking the color of a `CueLed`.
30#[derive(Debug, Clone, Fail, PartialEq)]
31pub enum CheckColorError {
32    #[fail(display = "Failed to check the color, error: {:?}.", _0)]
33    SdkError(Option<CueSdkError>),
34    #[fail(display = "Error creating valid LedColor, error: {:?}", _0)]
35    LedColorError(LedColorFromFfiError),
36}
37
38impl CueLed {
39    pub(crate) fn with_device_data_from_ffi(
40        device_index: DeviceIndex,
41        led: &ffi::CorsairLedPosition,
42    ) -> Self {
43        let id: LedId = led.ledId.into();
44        let position = LedPosition {
45            height: led.height,
46            left: led.left,
47            top: led.top,
48            width: led.width,
49        };
50        CueLed {
51            device_index,
52            id,
53            position,
54            last_checked_color: None,
55            last_buffed_color: None,
56        }
57    }
58
59    /// Checks the color of a `CueLed`, updating the self struct instance with
60    /// the new value upon success.
61    pub fn check_color(&mut self) -> Result<(), CheckColorError> {
62        let mut vh = CuePropertyValueHolder::new_with_initial_value(ffi::CorsairLedColor {
63            ledId: self.id.into(),
64            r: -1,
65            g: -1,
66            b: -1,
67        });
68        let was_successful = unsafe {
69            ffi::CorsairGetLedsColorsByDeviceIndex(self.device_index as i32, 1, vh.mut_ptr())
70        };
71        if was_successful {
72            let color = LedColor::from_ffi(&vh.value()).map_err(|e| LedColorError(e))?;
73            self.last_checked_color = Some(color);
74            Ok(())
75        } else {
76            self.last_checked_color = None;
77            Err(SdkError(get_last_error()))
78        }
79    }
80
81    /// Update the iCUE SDK color buffer with a new color, and modify our `last_buffed_color`
82    /// field if that color buffer update operation was successful.
83    pub fn update_color_buffer(&mut self, new_color: LedColor) -> CueSdkErrorResult {
84        let mut ffi_color = ffi::CorsairLedColor {
85            ledId: self.id.into(),
86            r: new_color.red as i32,
87            g: new_color.green as i32,
88            b: new_color.blue as i32,
89        };
90
91        let was_successful = unsafe {
92            ffi::CorsairSetLedsColorsBufferByDeviceIndex(
93                self.device_index as i32,
94                1,
95                &mut ffi_color as *mut ffi::CorsairLedColor,
96            )
97        };
98
99        if was_successful {
100            self.last_buffed_color = Some(new_color);
101            Ok(())
102        } else {
103            Err(get_last_error())
104        }
105    }
106}
107
108/// A basic RGB struct for describing an LED color.
109///
110/// It's recommended to reuse these if it makes sense with your use-case, as nothing in the crate
111/// ever requires more than a reference (no mutable references,
112/// or pass ownership with a value ever required).
113#[derive(Debug, Clone, Copy, PartialEq)]
114pub struct LedColor {
115    pub red: u8,
116    pub blue: u8,
117    pub green: u8,
118}
119
120/// An error for if the iCUE SDK returns invalid color values that aren't between 0-255.
121#[derive(Debug, Clone, PartialEq, Fail)]
122#[fail(
123    display = "Received invalid color values, should all be between 0-255, instead received red: {}, green: {}, blue:{}",
124    _0, _1, _2
125)]
126pub struct LedColorFromFfiError(i32, i32, i32);
127
128const VALUE_RGB_RANGE: &Range<i32> = &(0..256);
129
130impl LedColor {
131    pub(crate) fn from_ffi(color: &ffi::CorsairLedColor) -> Result<Self, LedColorFromFfiError> {
132        if !VALUE_RGB_RANGE.contains(&color.r)
133            || !VALUE_RGB_RANGE.contains(&color.b)
134            || !VALUE_RGB_RANGE.contains(&color.g)
135        {
136            Err(LedColorFromFfiError(color.r, color.b, color.g))
137        } else {
138            Ok(LedColor {
139                red: color.r as u8,
140                blue: color.b as u8,
141                green: color.g as u8,
142            })
143        }
144    }
145}
146
147/// The position of a given `CueLed`.
148///
149/// This positions are in physical units for keyboards (inches/cm/mm) and otherwise
150/// are "grid" based and not unitized in any "physical" way.
151#[derive(Debug, Clone, Copy, PartialEq)]
152pub struct LedPosition {
153    pub top: f64,
154    pub left: f64,
155    pub height: f64,
156    pub width: f64,
157}