logitech_lcd_sys/
lib.rs

1#![allow(non_camel_case_types, non_snake_case)]
2
3//! FFI bindings and loader for the Logitech LCD SDK
4//!
5//! [LogitechLcd](struct.LogitechLcd.html) will try to locate and load
6//! `LogitechLcd.dll` at Runtime for dynamic linking.
7//!
8
9#[macro_use]
10extern crate bitflags;
11
12use std::os::raw::{c_int, c_uint};
13
14/// Monochrome screen width in pixels.
15pub const MONO_WIDTH:  usize = 160;
16
17/// Monochrome screen hight in pixels.
18pub const MONO_HEIGHT: usize = 43;
19
20/// Color screen witdh in pixels.
21pub const COLOR_WIDTH:  usize = 320;
22
23/// Color screen hight in pixels.
24pub const COLOR_HEIGHT: usize = 240;
25
26bitflags! {
27    /// Targeted lcd type.
28    ///
29    /// This library allows you to target either Mono or Color devices
30    pub struct LcdType: u32 {
31        /// Mono color.
32        const MONO =  0x00000001;
33        /// 32bit RGBA color.
34        const COLOR = 0b00000010;
35        /// Either mono or color.
36        const EITHER = Self::MONO.bits | Self::COLOR.bits;
37    }
38}
39
40bitflags! {
41    /// Lcd Button bitmap.
42    pub struct LcdButton: u32 {
43        const MONO_BUTTON_0 = 0x00000001;
44        const MONO_BUTTON_1 = 0x00000002;
45        const MONO_BUTTON_2 = 0x00000004;
46        const MONO_BUTTON_3 = 0x00000008;
47        const MONO_BUTTON = Self::MONO_BUTTON_0.bits |
48                            Self::MONO_BUTTON_1.bits |
49                            Self::MONO_BUTTON_2.bits |
50                            Self::MONO_BUTTON_3.bits;
51
52        const COLOR_BUTTON_LEFT   = 0x00000100;
53        const COLOR_BUTTON_RIGHT  = 0x00000200;
54        const COLOR_BUTTON_OK     = 0x00000400;
55        const COLOR_BUTTON_CANCEL = 0x00000800;
56        const COLOR_BUTTON_UP     = 0x00001000;
57        const COLOR_BUTTON_DOWN   = 0x00002000;
58        const COLOR_BUTTON_MENU   = 0x00004000;
59        const COLOR_BUTTON = Self::COLOR_BUTTON_LEFT.bits |
60                             Self::COLOR_BUTTON_RIGHT.bits |
61                             Self::COLOR_BUTTON_OK.bits |
62                             Self::COLOR_BUTTON_CANCEL.bits |
63                             Self::COLOR_BUTTON_UP.bits |
64                             Self::COLOR_BUTTON_DOWN.bits |
65                             Self::COLOR_BUTTON_MENU.bits;
66    }
67}
68
69/// LogitechLcd library.
70///
71/// Contains library symbols/functions as fields. Will unload library when dropped.
72pub struct LogitechLcd {
73    // Main functions
74    pub LogiLcdInit: unsafe extern "C" fn(friendlyName: *const u16, lcdType: c_uint) -> bool,
75    pub LogiLcdIsConnected: unsafe extern "C" fn(lcdType: c_uint) -> bool,
76    pub LogiLcdIsButtonPressed: unsafe extern "C" fn(button: c_uint) -> bool,
77    pub LogiLcdUpdate: unsafe extern "C" fn(),
78    pub LogiLcdShutdown: unsafe extern "C" fn(),
79
80    // Monochrome LCD functions
81    pub LogiLcdMonoSetBackground: unsafe extern "C" fn(monoBitmap: *const u8) -> bool,
82    pub LogiLcdMonoSetText: unsafe extern "C" fn(lineNumber: c_int, text: *const u16) -> bool,
83
84    // Color LCD functions
85    pub LogiLcdColorSetBackground: unsafe extern "C" fn(colorBitmap: *const u8) -> bool,
86    pub LogiLcdColorSetTitle: unsafe extern "C" fn(text: *const u16, red: c_int, green: c_int,
87        blue: c_int) -> bool,
88    pub LogiLcdColorSetText: unsafe extern "C" fn(lineNumber: c_int, text: *const u16, red: c_int,
89        green: c_int, blue: c_int) -> bool,
90
91    // UDK functions, use this only if working with UDK
92    pub LogiLcdColorSetBackgroundUDK: unsafe extern "C" fn(partialBitmap: *const u8,
93        arraySize: c_int) -> c_int,
94    pub LogiLcdColorResetBackgroundUDK: unsafe extern "C" fn() -> c_int,
95    pub LogiLcdMonoSetBackgroundUDK: unsafe extern "C" fn(partialBitmap: *const u8,
96        arraySize: c_int) -> c_int,
97    pub LogiLcdMonoResetBackgroundUDK: unsafe extern "C" fn() -> c_int,
98
99    /// Library handle, will be freed on drop
100    _library: platform::Library,
101}
102
103unsafe impl std::marker::Send for LogitechLcd {}
104
105#[cfg(not(target_os = "windows"))]
106mod platform {
107    use super::LogitechLcd;
108    use std::io::Error;
109
110    pub struct Library;
111
112    impl LogitechLcd {
113        pub fn load() -> Result<LogitechLcd, Error> {
114            unimplemented!();
115        }
116    }
117}
118
119#[cfg(target_os = "windows")]
120mod platform {
121    extern crate winapi;
122    extern crate kernel32;
123    extern crate winreg;
124
125    use super::LogitechLcd;
126
127    use self::winreg::RegKey;
128    use self::winreg::enums::{HKEY_LOCAL_MACHINE, HKEY_CLASSES_ROOT, KEY_READ};
129    use self::winapi::minwindef::{HMODULE, FARPROC};
130
131    use std::os::windows::ffi::OsStrExt;
132    use std::ffi::OsStr;
133    use std::io::Error;
134
135    pub struct Library(HMODULE);
136
137    const ERROR_MOD_NOT_FOUND: i32 = winapi::winerror::ERROR_MOD_NOT_FOUND as i32;
138
139    /// Find `LogitechLcd.dll` in Windows registry using its CLSID
140    fn dll_path_clsid() -> Result<Vec<u16>, Error> {
141        let hkcl = RegKey::predef(HKEY_CLASSES_ROOT);
142        let hklm = RegKey::predef(HKEY_LOCAL_MACHINE);
143
144        let mut dll_path = None;
145
146        #[cfg(target_arch = "x86_64")]
147        {
148            match hkcl.open_subkey_with_flags(
149                "CLSID\\{d0e790a5-01a7-49ae-ae0b-e986bdd0c21b}\\ServerBinary", KEY_READ)
150            {
151                Ok(key) => dll_path = key.get_value::<String, &str>("").ok(),
152                Err(_) => {},
153            }
154
155            match hklm.open_subkey_with_flags(
156                "SOFTWARE\\Classes\\CLSID\\{d0e790a5-01a7-49ae-ae0b-e986bdd0c21b}\\ServerBinary",
157                KEY_READ)
158            {
159                Ok(key) => dll_path = key.get_value::<String, &str>("").ok(),
160                Err(_) => {},
161            }
162        }
163
164        #[cfg(target_arch = "x86")]
165        {
166            match hkcl.open_subkey_with_flags(
167                "Wow6432Node\\CLSID\\{d0e790a5-01a7-49ae-ae0b-e986bdd0c21b}\\ServerBinary", KEY_READ)
168            {
169                Ok(key) => dll_path = key.get_value::<String, &str>("").ok(),
170                Err(_) => {},
171            }
172
173            match hklm.open_subkey_with_flags(
174                "SOFTWARE\\Classes\\Wow6432Node\\CLSID\\{d0e790a5-01a7-49ae-ae0b-e986bdd0c21b}\\ServerBinary",
175                KEY_READ)
176            {
177                Ok(key) => dll_path = key.get_value::<String, &str>("").ok(),
178                Err(_) => {},
179            }
180
181            match hklm.open_subkey_with_flags(
182                "SOFTWARE\\Wow6432Node\\Classes\\CLSID\\{d0e790a5-01a7-49ae-ae0b-e986bdd0c21b}\\ServerBinary",
183                KEY_READ)
184            {
185                Ok(key) => dll_path = key.get_value::<String, &str>("").ok(),
186                Err(_) => {},
187            }
188        }
189
190        match dll_path {
191            // Convert to widestring and terminate with \0\0.
192            Some(p) => Ok(OsStr::new(&p[..]).encode_wide().chain(Some(0)).collect::<Vec<u16>>()),
193            None => Err(Error::from_raw_os_error(ERROR_MOD_NOT_FOUND)),
194        }
195    }
196
197    unsafe fn load_lib() -> Result<HMODULE, Error> {
198        match dll_path_clsid() {
199            Ok(wide_path) => {
200                let handle = kernel32::LoadLibraryW(wide_path.as_ptr());
201                if handle.is_null() {
202                    let error = Error::last_os_error();
203                    let ecode = error.raw_os_error().unwrap();
204                    // Fallthrough on ERROR_MOD_NOT_FOUND
205                    if ecode != ERROR_MOD_NOT_FOUND {
206                        return Err(error);
207                    }
208                } else {
209                    return Ok(handle);
210                }
211            },
212            Err(e) => {
213                match e.raw_os_error() {
214                    Some(ERROR_MOD_NOT_FOUND) => {},
215                    _ => return Err(e),
216                }
217            },
218        }
219
220        // Convert to widestring and terminate with \0\0.
221        let wide_name = OsStr::new("LogitechLcd.dll").encode_wide().chain(Some(0)).collect::<Vec<u16>>();
222        let handle = kernel32::LoadLibraryW(wide_name.as_ptr());
223        if handle.is_null() {
224            Err(Error::last_os_error())
225        } else {
226            Ok(handle)
227        }
228    }
229
230    impl LogitechLcd {
231        /// Try to locate and load 'LogitechLcd.dll'.
232        pub fn load() -> Result<LogitechLcd, Error> {
233            use std::mem;
234
235            unsafe {
236                let handle = load_lib()?;
237
238                let mut symbols = [
239                    ("LogiLcdInit\0",                    0 as FARPROC),
240                    ("LogiLcdIsConnected\0",             0 as FARPROC),
241                    ("LogiLcdIsButtonPressed\0",         0 as FARPROC),
242                    ("LogiLcdUpdate\0",                  0 as FARPROC),
243                    ("LogiLcdShutdown\0",                0 as FARPROC),
244                    ("LogiLcdMonoSetBackground\0",       0 as FARPROC),
245                    ("LogiLcdMonoSetText\0",             0 as FARPROC),
246                    ("LogiLcdColorSetBackground\0",      0 as FARPROC),
247                    ("LogiLcdColorSetTitle\0",           0 as FARPROC),
248                    ("LogiLcdColorSetText\0",            0 as FARPROC),
249                    ("LogiLcdColorSetBackgroundUDK\0",   0 as FARPROC),
250                    ("LogiLcdColorResetBackgroundUDK\0", 0 as FARPROC),
251                    ("LogiLcdMonoSetBackgroundUDK\0",    0 as FARPROC),
252                    ("LogiLcdMonoResetBackgroundUDK\0",  0 as FARPROC),
253                ];
254
255                for i in symbols.iter_mut() {
256                    i.1 = kernel32::GetProcAddress(handle, i.0.as_ptr() as *const i8);
257                    if i.1.is_null() {
258                        let error = Error::last_os_error();
259                        kernel32::FreeLibrary(handle);
260                        return Err(error);
261                    }
262                }
263
264                Ok(LogitechLcd {
265                    LogiLcdInit:                    mem::transmute(symbols[0].1),
266                    LogiLcdIsConnected:             mem::transmute(symbols[1].1),
267                    LogiLcdIsButtonPressed:         mem::transmute(symbols[2].1),
268                    LogiLcdUpdate:                  mem::transmute(symbols[3].1),
269                    LogiLcdShutdown:                mem::transmute(symbols[4].1),
270                    LogiLcdMonoSetBackground:       mem::transmute(symbols[5].1),
271                    LogiLcdMonoSetText:             mem::transmute(symbols[6].1),
272                    LogiLcdColorSetBackground:      mem::transmute(symbols[7].1),
273                    LogiLcdColorSetTitle:           mem::transmute(symbols[8].1),
274                    LogiLcdColorSetText:            mem::transmute(symbols[9].1),
275                    LogiLcdColorSetBackgroundUDK:   mem::transmute(symbols[10].1),
276                    LogiLcdColorResetBackgroundUDK: mem::transmute(symbols[11].1),
277                    LogiLcdMonoSetBackgroundUDK:    mem::transmute(symbols[12].1),
278                    LogiLcdMonoResetBackgroundUDK:  mem::transmute(symbols[13].1),
279                    _library: Library(handle),
280                })
281            }
282        }
283    }
284
285    impl Drop for Library {
286        fn drop(&mut self) {
287            unsafe {
288                kernel32::FreeLibrary(self.0);
289            }
290        }
291    }
292}