use bytes::{Bytes, BytesMut, BufMut};
use hid;
#[macro_use] extern crate serde_derive;
use serde_json;
use std::{error, fmt, str, time};
const LOOKING_GLASS_VID:u16 = 0x04D8;
const LOOKING_GLASS_PID:u16 = 0xEf7E;
pub struct LookingGlass {
pub serial: String,
pub pitch: f32,
pub slope: f32,
pub center: f32,
pub dpi: f32,
pub screen_w: u32,
pub screen_h: u32,
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum Error {
HIDError(String),
ParseError(String)
}
pub type Result<T> = ::std::result::Result<T, Error>;
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(error::Error::description(self))
}
}
impl error::Error for Error {
fn description(&self) -> &str {
match *self {
Error::HIDError(ref err) => err,
Error::ParseError(ref err) => err,
}
}
}
impl LookingGlass {
pub fn findall() -> Vec<Result<LookingGlass>> {
let mut glasses = Vec::new();
let hid_manager = match hid::init() {
Ok(manager) => manager,
Err(error) => {
glasses.push(Err(Error::HIDError(error.to_string())));
return glasses;
}
};
for candidate in hid_manager.find(Some(LOOKING_GLASS_VID), Some(LOOKING_GLASS_PID)) {
glasses.push(
if let Some(_hid_serial) = candidate.serial_number() {
match get_json_string(candidate) {
Ok(string) => json_to_glass(string),
Err(error) => Err(Error::HIDError(error.to_string()))
}
} else {
Err(Error::HIDError("Error reading - may lack permissions?".to_string()))
}
);
}
glasses
}
}
fn json_to_glass(json_string: String) -> Result<LookingGlass>
{
#[derive(Serialize, Deserialize)]
struct JSONValueMap {
value: f32
}
#[derive(Serialize, Deserialize)]
#[allow(non_snake_case)]
struct ConfigJSON {
configVersion: String,
serial: String,
pitch: JSONValueMap,
slope: JSONValueMap,
center: JSONValueMap,
viewCone: JSONValueMap,
invView: JSONValueMap,
verticalAngle: JSONValueMap,
DPI: JSONValueMap,
screenW: JSONValueMap,
screenH: JSONValueMap,
flipImageX: JSONValueMap,
flipImageY: JSONValueMap,
flipSubp: JSONValueMap,
}
match serde_json::from_str::<ConfigJSON>(&json_string) {
Ok(config) => {
if config.configVersion == "1.0" {
Ok(LookingGlass {
serial: config.serial,
pitch: config.pitch.value,
slope: config.slope.value,
center: config.center.value,
dpi: config.DPI.value,
screen_w: config.screenW.value as u32,
screen_h: config.screenH.value as u32,
})
} else {
Err(Error::ParseError(format!("Don't know how to read config version {}...",
config.configVersion)))
}
},
Err(error) => {
Err(Error::ParseError(format!("Error parsing JSON: {}", error.to_string())))
}
}
}
fn hid_multiread(handle: &mut hid::Handle) -> hid::Result<BytesMut> {
let mut ret_buf = BytesMut::with_capacity(0);
loop {
let mut this_read = BytesMut::with_capacity(128);
this_read.resize(128, 0);
match handle.data().read(&mut this_read, time::Duration::from_millis(10))? {
Some(count) => {
ret_buf.extend_from_slice(&this_read[..count]);
},
None => {
break;
},
}
}
Ok(ret_buf)
}
fn hid_query(handle: &mut hid::Handle, addr:u16) -> hid::Result<BytesMut> {
let mut buf = BytesMut::with_capacity(512);
hid_multiread(handle)?;
buf.put_u16_be(0);
buf.put_u16_be(addr);
buf.resize(68, 0);
let count = handle.feature().send(&buf)?;
if count != buf.len() {
return Err(hid::Error::Write);
}
buf = hid_multiread(handle)?;
let confirm = [0, 0, (addr>>8 & 0xff) as u8, (addr & 0xFF) as u8];
if buf.len() <= 4 ||
buf.split_to(4) != Bytes::from(&confirm[..]) {
println!("Confirm failed!");
return Err(hid::Error::Read);
}
Ok(buf)
}
fn get_json_string(candidate: hid::Device) -> hid::Result<String> {
let mut handle = candidate.open()?;
handle.blocking(true)?;
let json_size_raw = hid_query(&mut handle, 0)?;
let mut json_size = 0usize;
for i in 0..4 {
json_size <<= 8;
json_size += json_size_raw[i] as usize;
}
let mut json = Vec::from(&json_size_raw[4..]);
while json.len() < json_size {
let last_page = (json.len() / 64) as u16;
let this_read = hid_query(&mut handle, last_page + 1)?;
json.extend_from_slice(&this_read);
}
json.truncate(json_size);
match str::from_utf8(&json) {
Ok(yay) => Ok(yay.to_string()),
Err(error) => Err(hid::Error::String(error.to_string()))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn can_find() {
let glasses = LookingGlass::findall();
if glasses.is_empty() {
println!("Didn't find any Looking Glasses");
} else {
println!("Looking Glasses found:");
}
let mut index = 0;
for glass in &glasses {
index += 1;
println!("{} of {}", index, glasses.len());
match glass {
Ok(glass) => {
println!("\t Serial: {}", glass.serial);
println!("\t {}x{} pixels, {} DPI", glass.screen_w, glass.screen_h, glass.dpi);
println!("\t pitch: {}", glass.pitch);
println!("\t slope: {}", glass.slope);
println!("\t center: {}", glass.center);
},
Err(error) => {
println!("\t Error: {}", error.to_string());
assert!(false);
}
}
}
}
#[test]
fn test_json_to_glass_v1() {
let json = concat!(
r#"{"configVersion":"1.0","serial":"00297","pitch":{"value":49.81804275512695},"#,
r#""slope":{"value":5.044347763061523},"center":{"value":0.176902174949646},"#,
r#""viewCone":{"value":40.0},"invView":{"value":1.0},"verticalAngle":{"value":0.0},"#,
r#""DPI":{"value":338.0},"screenW":{"value":2560.0},"screenH":{"value":1600.0},"#,
r#""flipImageX":{"value":0.0},"flipImageY":{"value":0.0},"flipSubp":{"value":0.0}}"#
).to_string();
match json_to_glass(json) {
Ok(glass) => {
assert_eq!(glass.serial, "00297");
assert_eq!(glass.pitch, 49.81804275512695);
assert_eq!(glass.slope, 5.044347763061523);
assert_eq!(glass.center, 0.176902174949646);
assert_eq!(glass.dpi, 338.0);
assert_eq!(glass.screen_w, 2560);
assert_eq!(glass.screen_h, 1600);
},
Err(..) => {
assert!(false);
}
}
}
}