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
13pub struct Sdk(MutexGuard<'static, ()>);
17
18impl Sdk {
19 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 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 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 Some((major as u32, minor as u32, build as u32))
78 }
79
80 pub fn set_target(&self, target_device: TargetDevice) -> bool {
85 unsafe { sys::LogiLedSetTargetDevice(target_device.bits() as c_int) }
87 }
88
89 pub fn set_lighting(&self, color: ColorPercent) -> bool {
94 unsafe { sys::LogiLedSetLighting(color.r.into(), color.g.into(), color.b.into()) }
95 }
96
97 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 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 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 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 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 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 pub fn stop_effects(&self) -> bool {
242 unsafe { sys::LogiLedStopEffects() }
243 }
244
245 pub fn stop_effects_on_key(&self, key: KeyName) -> bool {
250 unsafe { sys::LogiLedStopEffectsOnKey(key) }
251 }
252
253 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 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 pub fn save_lighting_for_key(&self, key: KeyName) -> bool {
327 unsafe { sys::LogiLedSaveLightingForKey(key) }
328 }
329
330 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}