poddy/input/
events.rs

1use crate::input::key::Key;
2use crate::input::InputEvent;
3use std::sync::atomic::{AtomicBool, Ordering};
4use std::sync::Arc;
5use std::time::Duration;
6use tokio::select;
7use tokio::time::{interval, Interval};
8
9/// A small event handler that wrap crossterm input and tick event. Each event
10/// type is handled in its own thread and returned to a common `Receiver`
11pub struct Events {
12    rx: tokio::sync::mpsc::Receiver<InputEvent>,
13    // Need to be kept around to prevent disposing the sender side.
14    _tx: tokio::sync::mpsc::Sender<InputEvent>,
15    // To stop the loop
16    stop_capture: Arc<AtomicBool>,
17    // render interval
18    interval: Interval,
19}
20
21impl Events {
22    /// Constructs an new instance of `Events` with the default config.
23    pub fn new(render_rate: Duration) -> Events {
24        let (tx, rx) = tokio::sync::mpsc::channel(100);
25        let stop_capture = Arc::new(AtomicBool::new(false));
26
27        let event_tx = tx.clone();
28        let event_stop_capture = stop_capture.clone();
29        tokio::spawn(async move {
30            loop {
31                // poll for tick rate duration, if no event, sent tick event.
32                if crossterm::event::poll(render_rate).unwrap() {
33                    if let crossterm::event::Event::Key(key) = crossterm::event::read().unwrap() {
34                        let key = Key::from(key);
35                        if let Err(err) = event_tx.send(InputEvent::Input(key)).await {
36                            log::error!("Oops (event)!, {}", err);
37                        }
38                    }
39                }
40                if event_stop_capture.load(Ordering::Relaxed) {
41                    break;
42                }
43            }
44        });
45
46        Events {
47            rx,
48            _tx: tx,
49            stop_capture,
50            interval: interval(render_rate),
51        }
52    }
53
54    /// Attempts to read an event.
55    pub async fn next(&mut self) -> InputEvent {
56        select! {
57            msg = self.rx.recv() => msg.unwrap_or(InputEvent::Quit),
58            _ = self.interval.tick() => InputEvent::Render,
59        }
60    }
61}
62
63impl Drop for Events {
64    fn drop(&mut self) {
65        self.stop_capture.store(true, Ordering::Relaxed)
66    }
67}