#![warn(missing_docs)]
extern crate logitech_lcd_sys as sys;
pub use sys::{
LcdButton, MONO_WIDTH, MONO_HEIGHT, COLOR_WIDTH, COLOR_HEIGHT,
};
use std::sync::atomic::{AtomicBool, Ordering, ATOMIC_BOOL_INIT};
use std::os::raw::c_int;
static INITIALIZED: AtomicBool = ATOMIC_BOOL_INIT;
pub struct Lcd {
type_flags: sys::LcdType,
lib: sys::LogitechLcd,
}
#[derive(Debug)]
pub enum Error {
NotConnected,
Initialization,
MonoBackground,
MonoText,
ColorBackground,
ColorTitle,
ColorText,
NullCharacter,
LoadLibrary(std::io::Error),
}
impl std::error::Error for Error {
fn description(&self) -> &str {
match *self {
Error::NotConnected => "A logitech LCD is not connected to the system.",
Error::Initialization => "FFI call to LogiLcdInit() in LogitechLcd.dll has failed.",
Error::MonoBackground => "FFI call to LogiLcdMonoSetBackground() in LogitechLcd.dll has failed.",
Error::MonoText => "FFI call to LogiLcdMonoSetText() in LogitechLcd.dll has failed.",
Error::ColorBackground => "FFI call to LogiLcdColorSetTitle() in LogitechLcd.dll has failed.",
Error::ColorTitle => "FFI call to LogiLcdColorSetTitle() in LogitechLcd.dll has failed.",
Error::ColorText => "FFI call to LogiLcdColorSetText() in LogitechLcd.dll has failed.",
Error::NullCharacter => "Unexpected NULL character.",
Error::LoadLibrary(_) => "Failed to load LogitechLcd.dll",
}
}
fn cause(&self) -> Option<&std::error::Error> {
match *self {
Error::LoadLibrary(ref e) => Some(e),
_ => None,
}
}
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
use std::error::Error;
match self.cause() {
Some(c) => write!(f, "LcdError: {}, Cause: {}", self.description(), c.description()),
None => write!(f, "LcdError: {}", self.description()),
}
}
}
#[cfg(target_os = "windows")]
fn str_to_wchar_checked(s: &str) -> Result<Vec<u16>, Error> {
use std::os::windows::ffi::OsStrExt;
use std::ffi::OsStr;
if s.chars().any(|c| c as u32 == 0) {
return Err(Error::NullCharacter);
}
Ok(OsStr::new(s).encode_wide().chain(Some(0)).collect::<Vec<u16>>())
}
#[cfg(not(target_os = "windows"))]
fn str_to_wchar_checked(_: &str) -> Result<Vec<u16>, Error> {
unimplemented!();
}
impl Lcd {
fn init(app_name: &str, type_flags: sys::LcdType) -> Result<Lcd, Error> {
assert_eq!(INITIALIZED.swap(true, Ordering::SeqCst), false);
let lib = sys::LogitechLcd::load().map_err(|e| Error::LoadLibrary(e))?;
let ws = str_to_wchar_checked(app_name)?;
let ret = unsafe {
match (lib.LogiLcdInit)(ws.as_ptr(), type_flags.bits()) {
true => {
match (lib.LogiLcdIsConnected)(type_flags.bits()) {
true => Ok(Lcd {
type_flags: type_flags,
lib: lib,
}),
false => Err(Error::NotConnected),
}
},
false => Err(Error::Initialization),
}
};
if ret.is_err() {
INITIALIZED.store(false, Ordering::SeqCst);
}
ret
}
pub fn init_mono(app_name: &str) -> Result<Lcd, Error> {
Self::init(app_name, sys::LcdType::MONO)
}
pub fn init_color(app_name: &str) -> Result<Lcd, Error> {
Self::init(app_name, sys::LcdType::COLOR)
}
pub fn init_either(app_name: &str) -> Result<Lcd, Error> {
Self::init(app_name, sys::LcdType::EITHER)
}
pub fn is_connected(&self) -> bool {
unsafe {
(self.lib.LogiLcdIsConnected)(self.type_flags.bits())
}
}
pub fn update(&mut self) {
unsafe {
(self.lib.LogiLcdUpdate)();
}
}
pub fn is_button_pressed(&self, buttons: LcdButton) -> bool {
unsafe {
(self.lib.LogiLcdIsButtonPressed)(buttons.bits())
}
}
pub fn set_mono_background(&mut self, mono_bitmap: &[u8]) -> Result<(), Error> {
assert!(!(self.type_flags | sys::LcdType::MONO).is_empty());
assert_eq!(mono_bitmap.len(), MONO_WIDTH * MONO_HEIGHT);
unsafe {
match (self.lib.LogiLcdMonoSetBackground)(mono_bitmap.as_ptr()) {
true => Ok(()),
false => Err(Error::MonoBackground),
}
}
}
pub fn set_mono_text(&mut self, line_number: usize, text: &str) -> Result<(), Error> {
assert!(!(self.type_flags | sys::LcdType::MONO).is_empty());
let ws = str_to_wchar_checked(text)?;
assert!(line_number < 4);
unsafe {
match (self.lib.LogiLcdMonoSetText)(line_number as c_int, ws.as_ptr()) {
true => Ok(()),
false => Err(Error::MonoText),
}
}
}
pub fn set_color_background(&mut self, color_bitmap: &[u8]) -> Result<(), Error> {
assert!(!(self.type_flags | sys::LcdType::COLOR).is_empty());
assert_eq!(color_bitmap.len(), COLOR_WIDTH * COLOR_HEIGHT * 4);
unsafe {
match (self.lib.LogiLcdColorSetBackground)(color_bitmap.as_ptr()) {
true => Ok(()),
false => Err(Error::ColorBackground),
}
}
}
pub fn set_color_title(&mut self, text: &str, red: u8, green: u8, blue: u8)
-> Result<(), Error>
{
assert!(!(self.type_flags | sys::LcdType::COLOR).is_empty());
let ws = str_to_wchar_checked(text)?;
unsafe {
match (self.lib.LogiLcdColorSetTitle)(ws.as_ptr(), red as c_int,
green as c_int, blue as c_int)
{
true => Ok(()),
false => Err(Error::ColorTitle),
}
}
}
pub fn set_color_text(&mut self, line_number: usize, text: &str,
red: u8, green: u8, blue: u8) -> Result<(), Error>
{
assert!(!(self.type_flags | sys::LcdType::COLOR).is_empty());
let ws = str_to_wchar_checked(text)?;
assert!(line_number < 8);
unsafe {
match (self.lib.LogiLcdColorSetText)(line_number as c_int,
ws.as_ptr(), red as c_int, green as c_int, blue as c_int)
{
true => Ok(()),
false => Err(Error::ColorText),
}
}
}
}
impl Drop for Lcd {
fn drop(&mut self) {
unsafe {
(self.lib.LogiLcdShutdown)();
}
INITIALIZED.store(false, Ordering::SeqCst);
}
}