| mod events; |
| |
| use events::{EventReader, EventType, InputEvent, KeyboardCode, KeyboardValue}; |
| use std::path::Path; |
| use std::sync::mpsc; |
| |
| /// Wait for the given key to be pressed on the given `events` interator. |
| /// |
| /// Returns `true` if the keystroke was detected before the iterator finished. |
| fn wait_for_key_press<I>(code: KeyboardCode, events: &mut I) -> Result<(), String> |
| where |
| I: Iterator<Item = InputEvent>, |
| { |
| for e in events { |
| if { e.type_ } == EventType::Key |
| && e.code == (code as u16) |
| && e.value == (KeyboardValue::KeyDown as i32) |
| { |
| return Ok(()); |
| } |
| } |
| Err("Device closed while waiting for key press.".to_string()) |
| } |
| |
| fn run_keyboard_test(input_devices: &[&Path]) -> Result<(), String> { |
| // Open the set of character devices. |
| let files = input_devices |
| .iter() |
| .map(|x| { |
| EventReader::new_from_path(Path::new(x)) |
| .map_err(|e| format!("Failed to open file '{}': {}", x.display(), e)) |
| }) |
| .collect::<Result<Vec<EventReader>, String>>()?; |
| |
| // Create a EventReader object for each input file, all sending to the channel `tx`. |
| let (tx, rx) = mpsc::sync_channel(0); |
| let _threads = files |
| .into_iter() |
| .map(|mut r| { |
| let tx = tx.clone(); |
| std::thread::spawn(move || { |
| while let Ok(event) = r.read() { |
| tx.send(event).ok(); |
| } |
| }) |
| }) |
| .collect::<Vec<_>>(); |
| |
| // Wait for key strokes. |
| println!("Type 'abc<shift>' to pass test."); |
| let mut iter = rx.iter(); |
| let codes = [ |
| KeyboardCode::A, |
| KeyboardCode::B, |
| KeyboardCode::C, |
| KeyboardCode::LeftShift, |
| ]; |
| for code in &codes { |
| println!(" waiting for {:?} ...", code); |
| if let error @ Err(_) = wait_for_key_press(*code, &mut iter) { |
| return error; |
| } |
| } |
| println!("PASS"); |
| Ok(()) |
| } |
| |
| fn main() -> Result<(), String> { |
| // Parse command line arguments. |
| let matches = clap::App::new("VirtIO Input Tester") |
| .subcommand( |
| clap::SubCommand::with_name("keyboard") |
| .about("runs a keyboard test") |
| .arg(clap::Arg::with_name("files").required(true).min_values(1)), |
| ) |
| .get_matches(); |
| |
| // Run the user-specified command |
| match matches.subcommand() { |
| ("keyboard", Some(keyboard_matches)) => { |
| let files = keyboard_matches |
| .values_of("files") |
| .unwrap() |
| .map(Path::new) |
| .collect::<Vec<&Path>>(); |
| run_keyboard_test(&files) |
| } |
| _ => Err("Must provide a subcommand indicating which test to run.".to_string()), |
| } |
| } |