sim_restaurant/tui/
event.rs1use std::sync::mpsc;
4use std::thread;
5use std::time::{Duration, Instant};
6
7use crossterm::event::{self, Event as CrosstermEvent, KeyEvent};
8
9pub type AppResult<T> = Result<T, Box<dyn std::error::Error>>;
10
11#[derive(Clone, Copy, Debug)]
12pub enum Event {
13 Tick,
14 Key(KeyEvent),
15 Resize(u16, u16),
16}
17
18#[derive(Debug)]
19pub struct EventHandler {
20 receiver: mpsc::Receiver<Event>,
21 _handler: thread::JoinHandle<()>,
22}
23
24impl EventHandler {
25 #[must_use]
26 pub fn new(tick_rate_ms: u64) -> Self {
27 let tick_rate = Duration::from_millis(tick_rate_ms);
28 let (sender, receiver) = mpsc::channel();
29 let handler = thread::spawn(move || {
30 let mut last_tick = Instant::now();
31 loop {
32 let timeout = tick_rate
33 .checked_sub(last_tick.elapsed())
34 .unwrap_or(tick_rate);
35
36 if event::poll(timeout).expect("no events available") {
37 match event::read().expect("unable to read event") {
38 CrosstermEvent::Key(key) => sender.send(Event::Key(key)),
39 CrosstermEvent::Resize(width, height) => {
40 sender.send(Event::Resize(width, height))
41 }
42 _ => continue,
43 }
44 .expect("failed to send terminal event");
45 }
46
47 if last_tick.elapsed() >= tick_rate {
48 sender.send(Event::Tick).expect("failed to send tick event");
49 last_tick = Instant::now();
50 }
51 }
52 });
53
54 Self {
55 receiver,
56 _handler: handler,
57 }
58 }
59
60 pub fn next(&self) -> AppResult<Event> {
61 Ok(self.receiver.recv()?)
62 }
63}