diff options
| author | lukeflo | 2024-09-20 22:31:33 +0200 |
|---|---|---|
| committer | lukeflo | 2024-09-20 22:31:33 +0200 |
| commit | dc45b960a4eda299058e597f6867e4d4be109b1b (patch) | |
| tree | 30501aa9f1f26413f959d751302fa189508eacc1 /src/frontend/event.rs | |
| download | bibiman-dc45b960a4eda299058e597f6867e4d4be109b1b.tar.gz bibiman-dc45b960a4eda299058e597f6867e4d4be109b1b.zip | |
initial commit
Diffstat (limited to 'src/frontend/event.rs')
| -rw-r--r-- | src/frontend/event.rs | 97 |
1 files changed, 97 insertions, 0 deletions
diff --git a/src/frontend/event.rs b/src/frontend/event.rs new file mode 100644 index 0000000..f83dfea --- /dev/null +++ b/src/frontend/event.rs @@ -0,0 +1,97 @@ +use std::time::Duration; + +use crossterm::event::{Event as CrosstermEvent, KeyEvent, MouseEvent}; +use futures::{FutureExt, StreamExt}; +use tokio::sync::mpsc; + +use crate::frontend::app::AppResult; + +/// 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::UnboundedSender<Event>, + /// Event receiver channel. + receiver: mpsc::UnboundedReceiver<Event>, + /// Event handler thread. + handler: tokio::task::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::unbounded_channel(); + let _sender = sender.clone(); + let handler = tokio::spawn(async move { + let mut reader = crossterm::event::EventStream::new(); + let mut tick = tokio::time::interval(tick_rate); + loop { + let tick_delay = tick.tick(); + let crossterm_event = reader.next().fuse(); + tokio::select! { + _ = _sender.closed() => { + break; + } + _ = tick_delay => { + _sender.send(Event::Tick).unwrap(); + } + Some(Ok(evt)) = crossterm_event => { + match evt { + CrosstermEvent::Key(key) => { + if key.kind == crossterm::event::KeyEventKind::Press { + _sender.send(Event::Key(key)).unwrap(); + } + }, + CrosstermEvent::Mouse(mouse) => { + _sender.send(Event::Mouse(mouse)).unwrap(); + }, + CrosstermEvent::Resize(x, y) => { + _sender.send(Event::Resize(x, y)).unwrap(); + }, + CrosstermEvent::FocusLost => { + }, + CrosstermEvent::FocusGained => { + }, + CrosstermEvent::Paste(_) => { + }, + } + } + }; + } + }); + 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 async fn next(&mut self) -> AppResult<Event> { + self.receiver + .recv() + .await + .ok_or(Box::new(std::io::Error::new( + std::io::ErrorKind::Other, + "This is an IO error", + ))) + } +} |
