diff options
Diffstat (limited to 'src/event_handler/event.rs')
| -rw-r--r-- | src/event_handler/event.rs | 79 |
1 files changed, 79 insertions, 0 deletions
diff --git a/src/event_handler/event.rs b/src/event_handler/event.rs new file mode 100644 index 0000000..439c31b --- /dev/null +++ b/src/event_handler/event.rs @@ -0,0 +1,79 @@ +use crate::app::AppResult; +use crossterm::event::{self, Event as CrosstermEvent, KeyEvent, MouseEvent}; +use std::sync::mpsc; +use std::thread; +use std::time::{Duration, Instant}; + +/// Terminal events. +#[derive(Clone, Copy, Debug)] +pub enum Event { + /// Terminal tick. + Tick, + /// Key press. + Key(KeyEvent), + /// Mouse click/scroll. + Mouse(MouseEvent), + /// Terminal resize. + Resize(u16, u16), +} + +/// Terminal event handler. +#[allow(dead_code)] +#[derive(Debug)] +pub struct EventHandler { + /// Event sender channel. + sender: mpsc::Sender<Event>, + /// Event receiver channel. + receiver: mpsc::Receiver<Event>, + /// Event handler thread. + handler: thread::JoinHandle<()>, +} + +impl EventHandler { + /// Constructs a new instance of [`EventHandler`]. + pub fn new(tick_rate: u64) -> Self { + let tick_rate = Duration::from_millis(tick_rate); + let (sender, receiver) = mpsc::channel(); + let handler = { + let sender = sender.clone(); + thread::spawn(move || { + let mut last_tick = Instant::now(); + loop { + let timeout = tick_rate + .checked_sub(last_tick.elapsed()) + .unwrap_or(tick_rate); + + if event::poll(timeout).expect("failed to poll new events") { + match event::read().expect("unable to read event") { + CrosstermEvent::Key(e) => sender.send(Event::Key(e)), + CrosstermEvent::Mouse(e) => sender.send(Event::Mouse(e)), + CrosstermEvent::Resize(w, h) => sender.send(Event::Resize(w, h)), + CrosstermEvent::FocusGained => Ok(()), + CrosstermEvent::FocusLost => Ok(()), + CrosstermEvent::Paste(_) => unimplemented!(), + } + .expect("failed to send terminal event") + } + + if last_tick.elapsed() >= tick_rate { + sender.send(Event::Tick).expect("failed to send tick event"); + last_tick = Instant::now(); + } + } + }) + }; + Self { + sender, + receiver, + handler, + } + } + + /// Receive the next event from the handler thread. + /// + /// This function will always block the current thread if + /// there is no data available and it's possible for more data to be sent. + pub fn next(&self) -> AppResult<Event> { + Ok(self.receiver.recv()?) + } +} |
