use std::time::Duration;
use color_eyre::eyre::eyre;
use crossterm::event::{Event as CrosstermEvent, EventStream, KeyEvent, KeyEventKind};
use futures::{FutureExt, StreamExt};
use tokio::{sync::mpsc, task::JoinHandle, time};
#[derive(Debug)]
pub enum Event {
Key(KeyEvent),
Tick,
Render,
Resize,
}
pub struct EventHandler {
rx: mpsc::UnboundedReceiver<Event>,
_task: JoinHandle<()>,
}
impl EventHandler {
pub fn new(tick_rate: Duration, render_rate: Duration) -> Self {
let (tx, rx) = mpsc::unbounded_channel();
let _task = tokio::spawn(event_loop(tx, tick_rate, render_rate));
Self { rx, _task }
}
pub async fn next(&mut self) -> color_eyre::Result<Event> {
self.rx
.recv()
.await
.ok_or_else(|| eyre!("event channel closed"))
}
}
async fn event_loop(tx: mpsc::UnboundedSender<Event>, tick_rate: Duration, render_rate: Duration) {
let mut tick_interval = time::interval(tick_rate);
let mut render_interval = time::interval(render_rate);
let mut reader = EventStream::new();
loop {
let crossterm_event = reader.next().fuse();
tokio::select! {
_ = tick_interval.tick() => {
if tx.send(Event::Tick).is_err() { break; }
}
_ = render_interval.tick() => {
if tx.send(Event::Render).is_err() { break; }
}
maybe_event = crossterm_event => {
match maybe_event {
Some(Ok(CrosstermEvent::Key(key))) => {
if key.kind == KeyEventKind::Press
&& tx.send(Event::Key(key)).is_err()
{
break;
}
}
Some(Ok(CrosstermEvent::Resize(_, _))) => {
if tx.send(Event::Resize).is_err() { break; }
}
Some(Ok(_)) => {}
Some(Err(_)) | None => break,
}
}
}
}
}