rust_sadari_cli/helper/
event.rs

1use std::io;
2use std::sync::mpsc;
3use std::sync::{
4    atomic::{AtomicBool, Ordering},
5    Arc,
6};
7use std::thread;
8use std::time::Duration;
9
10use termion::event::Key;
11use termion::input::TermRead;
12
13pub enum Event<I> {
14    Input(I),
15    Tick,
16}
17
18/// A small event handler that wrap termion input and tick events. Each event
19/// type is handled in its own thread and returned to a common `Receiver`
20pub struct Events {
21    rx: mpsc::Receiver<Event<Key>>,
22    _input_handle: thread::JoinHandle<()>,
23    ignore_exit_key: Arc<AtomicBool>,
24    _tick_handle: thread::JoinHandle<()>,
25}
26
27#[derive(Debug, Clone, Copy)]
28pub struct Config {
29    pub exit_key: Key,
30    pub tick_rate: Duration,
31}
32
33impl Default for Config {
34    fn default() -> Config {
35        Config {
36            exit_key: Key::Char('q'),
37            tick_rate: Duration::from_millis(250),
38        }
39    }
40}
41
42impl Events {
43    #[allow(dead_code)]
44    pub fn new() -> Events {
45        Events::with_config(Config::default())
46    }
47
48    pub fn with_config(config: Config) -> Events {
49        let (tx, rx) = mpsc::channel();
50        let ignore_exit_key = Arc::new(AtomicBool::new(false));
51        let input_handle = {
52            let tx = tx.clone();
53            let ignore_exit_key = ignore_exit_key.clone();
54            thread::spawn(move || {
55                let stdin = io::stdin();
56                for evt in stdin.keys() {
57                    match evt {
58                        Ok(key) => {
59                            if let Err(_) = tx.send(Event::Input(key)) {
60                                return;
61                            }
62                            if !ignore_exit_key.load(Ordering::Relaxed) && key == config.exit_key {
63                                return;
64                            }
65                        }
66                        Err(_) => {}
67                    }
68                }
69            })
70        };
71        let tick_handle = {
72            let tx = tx.clone();
73            thread::spawn(move || {
74                let tx = tx.clone();
75                loop {
76                    tx.send(Event::Tick).unwrap();
77                    thread::sleep(config.tick_rate);
78                }
79            })
80        };
81        Events {
82            rx,
83            ignore_exit_key,
84            _input_handle: input_handle,
85            _tick_handle: tick_handle,
86        }
87    }
88
89    pub fn next(&self) -> Result<Event<Key>, mpsc::RecvError> {
90        self.rx.recv()
91    }
92
93    #[allow(dead_code)]
94    pub fn disable_exit_key(&mut self) {
95        self.ignore_exit_key.store(true, Ordering::Relaxed);
96    }
97
98    #[allow(dead_code)]
99    pub fn enable_exit_key(&mut self) {
100        self.ignore_exit_key.store(false, Ordering::Relaxed);
101    }
102}