Skip to main content

hac_client/
event_pool.rs

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/// Core component responsible for pooling events from crossterm and sending
15/// them over to be handled
16#[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}