1use crossterm::event::{Event as CrosstermEvent, KeyEventKind};
2use futures::{FutureExt, StreamExt};
3use ratatui::layout::Rect;
4use std::ops::Div;
5
6#[derive(Debug, Clone, PartialEq)]
7pub enum Event {
8 Key(crossterm::event::KeyEvent),
9 Resize(Rect),
10 Tick,
11 Render,
12}
13
14#[derive(Debug)]
17pub struct EventPool {
18 event_rx: tokio::sync::mpsc::UnboundedReceiver<Event>,
19 event_tx: tokio::sync::mpsc::UnboundedSender<Event>,
20 frame_rate: f64,
21 tick_rate: f64,
22}
23
24impl EventPool {
25 pub fn new(frame_rate: f64, tick_rate: f64) -> Self {
26 let (event_tx, event_rx) = tokio::sync::mpsc::unbounded_channel();
27
28 EventPool {
29 event_rx,
30 event_tx,
31 frame_rate,
32 tick_rate,
33 }
34 }
35
36 #[cfg_attr(test, mutants::skip)]
37 pub fn start(&mut self) {
38 let render_delay = std::time::Duration::from_secs_f64(1.0.div(self.frame_rate));
39 let tick_delay = std::time::Duration::from_secs_f64(1.0.div(self.tick_rate));
40
41 let event_tx = self.event_tx.clone();
42 tokio::spawn(async move {
43 let mut reader = crossterm::event::EventStream::new();
44 let mut render_interval = tokio::time::interval(render_delay);
45 let mut tick_interval = tokio::time::interval(tick_delay);
46
47 loop {
48 let render_delay = render_interval.tick();
49 let tick_delay = tick_interval.tick();
50 let crossterm_event = reader.next().fuse();
51
52 tokio::select! {
53 maybe_event = crossterm_event => {
54 match maybe_event {
55 Some(Ok(CrosstermEvent::Key(key_event))) => {
56 if key_event.kind == KeyEventKind::Press {
57 event_tx.send(Event::Key(key_event)).expect("failed to send event through channel");
58 }
59 }
60 Some(Ok(CrosstermEvent::Resize(width, height))) => event_tx
61 .send(Event::Resize(Rect::new(0, 0, width, height)))
62 .expect("failed to send event through channel"),
63 _ => {}
64 }
65 }
66 _ = tick_delay => {
67 event_tx.send(Event::Tick).expect("failed to send event through channel");
68 },
69 _ = render_delay => {
70 event_tx.send(Event::Render).expect("failed to send event through channel");
71 },
72 }
73 }
74 });
75 }
76
77 #[cfg_attr(test, mutants::skip)]
78 pub async fn next(&mut self) -> Option<Event> {
79 self.event_rx.recv().await
80 }
81}