use std::io; use std::sync::mpsc; use std::sync::{ atomic::{AtomicBool, Ordering}, Arc, }; use std::thread; use std::time::Duration; use termion::event::Key; use termion::input::TermRead; pub enum Event { Input(I), Tick, } /// A small event handler that wrap termion input and tick events. Each event /// type is handled in its own thread and returned to a common `Receiver` pub struct Events { rx: mpsc::Receiver>, input_handle: thread::JoinHandle<()>, ignore_exit_key: Arc, tick_handle: thread::JoinHandle<()>, } #[derive(Debug, Clone, Copy)] pub struct Config { pub exit_key: Key, pub tick_rate: Duration, } impl Default for Config { fn default() -> Config { Config { exit_key: Key::Char('q'), tick_rate: Duration::from_millis(250), } } } impl Events { pub fn new() -> Events { Events::with_config(Config::default()) } pub fn with_config(config: Config) -> Events { let (tx, rx) = mpsc::channel(); let ignore_exit_key = Arc::new(AtomicBool::new(false)); let input_handle = { let tx = tx.clone(); let ignore_exit_key = ignore_exit_key.clone(); thread::spawn(move || { let stdin = io::stdin(); for evt in stdin.keys() { if let Ok(key) = evt { if let Err(err) = tx.send(Event::Input(key)) { eprintln!("{}", err); return; } if !ignore_exit_key.load(Ordering::Relaxed) && key == config.exit_key { return; } } } }) }; let tick_handle = { thread::spawn(move || loop { if tx.send(Event::Tick).is_err() { break; } thread::sleep(config.tick_rate); }) }; Events { rx, ignore_exit_key, input_handle, tick_handle, } } pub fn next(&self) -> Result, mpsc::RecvError> { self.rx.recv() } pub fn disable_exit_key(&mut self) { self.ignore_exit_key.store(true, Ordering::Relaxed); } pub fn enable_exit_key(&mut self) { self.ignore_exit_key.store(false, Ordering::Relaxed); } }