logitech_led_sdk/
sdk.rs

1use crate::sys;
2use crate::ColorPercent;
3use crate::DeviceType;
4use crate::KeyName;
5pub use crate::TargetDevice;
6use crate::SDK_LOCK;
7use std::ffi::CString;
8use std::os::raw::c_int;
9use std::sync::MutexGuard;
10use std::sync::TryLockError;
11use std::time::Duration;
12
13/// Entry to Api.
14///
15/// This serves as proof of initalization and prevents the API from being used by other threads.
16pub struct Sdk(MutexGuard<'static, ()>);
17
18impl Sdk {
19    /// Create a new sdk instance with no name.
20    ///
21    /// # Returns
22    /// Returns None if the sdk could not be initialized.
23    pub fn new() -> Option<Self> {
24        let guard = match SDK_LOCK.try_lock() {
25            Ok(guard) => guard,
26            Err(TryLockError::WouldBlock) => return None,
27            Err(TryLockError::Poisoned(e)) => e.into_inner(),
28        };
29
30        let init = unsafe { sys::LogiLedInit() };
31        if !init {
32            return None;
33        }
34
35        Some(Sdk(guard))
36    }
37
38    /// Create a new sdk instance with a name, where the name is the name of the application using the sdk.
39    ///
40    /// # Panics
41    /// Panics if the name contains interior NULs.
42    ///
43    /// # Returns
44    /// Returns None if the sdk could not be initialized.
45    pub fn new_with_name(name: &str) -> Option<Self> {
46        let guard = match SDK_LOCK.try_lock() {
47            Ok(guard) => guard,
48            Err(TryLockError::WouldBlock) => return None,
49            Err(TryLockError::Poisoned(e)) => e.into_inner(),
50        };
51
52        let name = CString::new(name).expect("name contains interior NUL");
53        let init = unsafe { sys::LogiLedInitWithName(name.as_ptr()) };
54        if !init {
55            return None;
56        }
57
58        Some(Sdk(guard))
59    }
60
61    /// Returns the sdk version.
62    ///
63    /// # Returns
64    /// Returns a tuple of the major, minor and build numbers if successful.
65    /// Returns None if the version could not be found.
66    pub fn get_version(&self) -> Option<(u32, u32, u32)> {
67        let mut major = 0;
68        let mut minor = 0;
69        let mut build = 0;
70
71        let valid = unsafe { sys::LogiLedGetSdkVersion(&mut major, &mut minor, &mut build) };
72        if !valid {
73            return None;
74        }
75
76        // i32 -> u32, transmute
77        Some((major as u32, minor as u32, build as u32))
78    }
79
80    /// Selects the target devices.
81    ///
82    /// # Returns
83    /// Returns true if the target devices were selected.
84    pub fn set_target(&self, target_device: TargetDevice) -> bool {
85        // u32 -> i32, transmute
86        unsafe { sys::LogiLedSetTargetDevice(target_device.bits() as c_int) }
87    }
88
89    /// Sets the lighting.
90    ///
91    /// # Returns
92    /// Returns true if successful.
93    pub fn set_lighting(&self, color: ColorPercent) -> bool {
94        unsafe { sys::LogiLedSetLighting(color.r.into(), color.g.into(), color.b.into()) }
95    }
96
97    /// Set the lighting for a keyboard key by key name.
98    ///
99    /// # Returns
100    /// Returns true if successful.
101    pub fn set_lighting_for_key_with_name(&self, key: KeyName, color: ColorPercent) -> bool {
102        unsafe {
103            sys::LogiLedSetLightingForKeyWithKeyName(
104                key,
105                color.r.into(),
106                color.g.into(),
107                color.b.into(),
108            )
109        }
110    }
111
112    /// Sets the lighting for a keyboard key by scan code.
113    ///
114    /// # Returns
115    /// Returns true if successful.
116    pub fn set_lighting_for_key_with_scan_code(&self, scan_code: u32, color: ColorPercent) -> bool {
117        unsafe {
118            sys::LogiLedSetLightingForKeyWithScanCode(
119                scan_code as c_int,
120                color.r.into(),
121                color.g.into(),
122                color.b.into(),
123            )
124        }
125    }
126
127    /// Sets the lighting for a keyboard key by HID code.
128    ///
129    /// # Returns
130    /// Returns true if successful.
131    pub fn set_lighting_for_key_with_hid_code(&self, hid_code: u32, color: ColorPercent) -> bool {
132        unsafe {
133            sys::LogiLedSetLightingForKeyWithHidCode(
134                hid_code as c_int,
135                color.r.into(),
136                color.g.into(),
137                color.b.into(),
138            )
139        }
140    }
141
142    /// Sets the lighting for a specific device's target zone.
143    ///
144    /// A zone number is generally different per device, read the offical SDK docs for more info.
145    ///
146    /// # Returns
147    /// Returns true if successful.
148    pub fn set_lighting_for_target_zone(
149        &self,
150        device: DeviceType,
151        zone: u32,
152        color: ColorPercent,
153    ) -> bool {
154        unsafe {
155            sys::LogiLedSetLightingForTargetZone(
156                device,
157                zone as c_int,
158                color.r.into(),
159                color.g.into(),
160                color.b.into(),
161            )
162        }
163    }
164
165    /// Save the current lighting, play the effect, and restore the lighting.
166    ///
167    /// duration controls how long the flashes occur overall.
168    /// If omitted, it will run until manually stopped.
169    /// The interval is the time between flashes.
170    ///
171    /// # Returns
172    /// Returns false if the call fails or any of the time values are too large.
173    pub fn flash_lighting(
174        &self,
175        color: ColorPercent,
176        duration: Option<Duration>,
177        interval: Duration,
178    ) -> bool {
179        let duration = match duration.map(|duration| c_int::try_from(duration.as_millis())) {
180            Some(Ok(duration)) if duration != sys::LOGI_LED_DURATION_INFINITE as c_int => duration,
181            Some(Err(_)) | Some(Ok(_)) => return false,
182            None => sys::LOGI_LED_DURATION_INFINITE as c_int,
183        };
184        let interval = match c_int::try_from(interval.as_millis()) {
185            Ok(interval) => interval,
186            Err(_) => return false,
187        };
188
189        unsafe {
190            sys::LogiLedFlashLighting(
191                color.r.into(),
192                color.g.into(),
193                color.b.into(),
194                duration,
195                interval,
196            )
197        }
198    }
199
200    /// Start a flashing effect on the given key.
201    ///
202    /// duration controls how long the flashes occur overall.
203    /// If omitted, it will run until manually stopped.
204    /// The interval is the time between flashes.
205    ///
206    /// # Returns
207    /// Returns false if the call fails or any of the time values are too large.
208    pub fn flash_single_key(
209        &self,
210        key: KeyName,
211        color: ColorPercent,
212        duration: Option<Duration>,
213        interval: Duration,
214    ) -> bool {
215        let duration = match duration.map(|duration| c_int::try_from(duration.as_millis())) {
216            Some(Ok(duration)) if duration != sys::LOGI_LED_DURATION_INFINITE as c_int => duration,
217            Some(Err(_)) | Some(Ok(_)) => return false,
218            None => sys::LOGI_LED_DURATION_INFINITE as c_int,
219        };
220        let interval = match c_int::try_from(interval.as_millis()) {
221            Ok(interval) => interval,
222            Err(_) => return false,
223        };
224
225        unsafe {
226            sys::LogiLedFlashSingleKey(
227                key,
228                color.r.into(),
229                color.g.into(),
230                color.b.into(),
231                duration,
232                interval,
233            )
234        }
235    }
236
237    /// Stops all current LED effects.
238    ///
239    /// # Returns
240    /// Returns false if the call fails.
241    pub fn stop_effects(&self) -> bool {
242        unsafe { sys::LogiLedStopEffects() }
243    }
244
245    /// Stops all LED effects on one key.
246    ///
247    /// # Returns
248    /// Returns false if the call fails.
249    pub fn stop_effects_on_key(&self, key: KeyName) -> bool {
250        unsafe { sys::LogiLedStopEffectsOnKey(key) }
251    }
252
253    /// Save the current lighting, pulse the lighting, then restore the lighting.
254    ///
255    /// duration controls how long the pulses occur overall.
256    /// If omitted, it will run until manually stopped.
257    /// The interval is the time between pulses.
258    ///
259    /// # Returns
260    /// Returns false if the call fails or any of the time values are too large.
261    pub fn pulse_lighting(
262        &self,
263        color: ColorPercent,
264        duration: Option<Duration>,
265        interval: Duration,
266    ) -> bool {
267        let duration = match duration.map(|duration| c_int::try_from(duration.as_millis())) {
268            Some(Ok(duration)) if duration != sys::LOGI_LED_DURATION_INFINITE as c_int => duration,
269            Some(Err(_)) | Some(Ok(_)) => return false,
270            None => sys::LOGI_LED_DURATION_INFINITE as c_int,
271        };
272        let interval = match c_int::try_from(interval.as_millis()) {
273            Ok(interval) => interval,
274            Err(_) => return false,
275        };
276
277        unsafe {
278            sys::LogiLedPulseLighting(
279                color.r.into(),
280                color.g.into(),
281                color.b.into(),
282                duration,
283                interval,
284            )
285        }
286    }
287
288    /// Start a pulsing effect on the given key.
289    ///
290    /// duration controls how long the pulses occur overall.
291    ///
292    /// # Returns
293    /// Returns false if the call fails or any of the time values are too large.
294    pub fn pulse_single_key(
295        &self,
296        key: KeyName,
297        start_color: ColorPercent,
298        end_color: ColorPercent,
299        duration: Duration,
300        is_infinite: bool,
301    ) -> bool {
302        let duration = match c_int::try_from(duration.as_millis()) {
303            Ok(duration) => duration,
304            Err(_) => return false,
305        };
306
307        unsafe {
308            sys::LogiLedPulseSingleKey(
309                key,
310                start_color.r.into(),
311                start_color.g.into(),
312                start_color.b.into(),
313                end_color.r.into(),
314                end_color.g.into(),
315                end_color.b.into(),
316                duration,
317                is_infinite,
318            )
319        }
320    }
321
322    /// Saves the current lighting config for the given key.
323    ///
324    /// # Returns
325    /// Returns false if the call fails.
326    pub fn save_lighting_for_key(&self, key: KeyName) -> bool {
327        unsafe { sys::LogiLedSaveLightingForKey(key) }
328    }
329
330    /// Restores the current lighting config for the given key.
331    ///
332    /// # Returns
333    /// Returns false if the call fails.
334    pub fn restore_lighting_for_key(&self, key: KeyName) -> bool {
335        unsafe { sys::LogiLedRestoreLightingForKey(key) }
336    }
337}
338
339impl Drop for Sdk {
340    fn drop(&mut self) {
341        unsafe {
342            sys::LogiLedShutdown();
343        }
344    }
345}