Skip to main content

chess_tui/
event.rs

1use crate::app::AppResult;
2use ratatui::crossterm::event::{self, Event as CrosstermEvent, KeyEvent, MouseEvent};
3use std::sync::mpsc;
4use std::thread;
5use std::time::{Duration, Instant};
6
7/// Terminal events.
8#[derive(Clone, Copy, Debug)]
9pub enum Event {
10    /// Terminal tick.
11    Tick,
12    /// Key press.
13    Key(KeyEvent),
14    /// Mouse click/scroll.
15    Mouse(MouseEvent),
16    /// Terminal resize.
17    Resize(u16, u16),
18}
19
20/// Terminal event handler.
21#[allow(dead_code)]
22#[derive(Debug)]
23pub struct EventHandler {
24    /// Event sender channel.
25    sender: mpsc::Sender<Event>,
26    /// Event receiver channel.
27    receiver: mpsc::Receiver<Event>,
28    /// Event handler thread.
29    handler: thread::JoinHandle<()>,
30}
31
32impl EventHandler {
33    /// Constructs a new instance of [`EventHandler`].
34    pub fn new(tick_rate: u64) -> Self {
35        let tick_rate = Duration::from_millis(tick_rate);
36        let (sender, receiver) = mpsc::channel();
37        let handler = {
38            let sender = sender.clone();
39            thread::spawn(move || {
40                let mut last_tick = Instant::now();
41                loop {
42                    let timeout = tick_rate
43                        .checked_sub(last_tick.elapsed())
44                        .unwrap_or(tick_rate);
45
46                    if let Ok(poll_result) = event::poll(timeout) {
47                        if poll_result {
48                            if let Ok(event) = event::read() {
49                                let send_result = match event {
50                                    CrosstermEvent::Key(e) => sender.send(Event::Key(e)),
51                                    CrosstermEvent::Mouse(e) => sender.send(Event::Mouse(e)),
52                                    CrosstermEvent::Resize(w, h) => {
53                                        sender.send(Event::Resize(w, h))
54                                    }
55                                    _ => Ok(()),
56                                };
57                                if send_result.is_err() {
58                                    break;
59                                }
60                            }
61                        }
62                    }
63
64                    if last_tick.elapsed() >= tick_rate {
65                        if sender.send(Event::Tick).is_err() {
66                            break;
67                        }
68                        last_tick = Instant::now();
69                    }
70                }
71            })
72        };
73        Self {
74            sender,
75            receiver,
76            handler,
77        }
78    }
79
80    /// Receive the next event from the handler thread.
81    ///
82    /// This function will always block the current thread if
83    /// there is no data available and it's possible for more data to be sent.
84    pub fn next(&self) -> AppResult<Event> {
85        Ok(self.receiver.recv()?)
86    }
87}