miv_editor/
event.rs

1use crate::app::AppResult;
2use crossterm::event::{self, Event as CrosstermEvent, KeyEvent, MouseEvent};
3use std::sync::mpsc;
4use std::thread;
5use std::time::{Duration, Instant};
6
7/// Terminal events.
8#[derive(Clone, Copy, Debug)]
9pub enum Event {
10    /// Terminal tick.
11    Tick,
12    /// Key press.
13    Key(KeyEvent),
14    /// Mouse click/scroll.
15    Mouse(MouseEvent),
16    /// Terminal resize.
17    Resize(u16, u16),
18}
19
20/// Terminal event handler.
21#[allow(dead_code)]
22#[derive(Debug)]
23pub struct EventHandler {
24    /// Event sender channel.
25    sender: mpsc::Sender<Event>,
26    /// Event receiver channel.
27    receiver: mpsc::Receiver<Event>,
28    /// Event handler thread.
29    handler: thread::JoinHandle<()>,
30}
31
32impl EventHandler {
33    /// Constructs a new instance of [`EventHandler`].
34    pub fn new(tick_rate: u64) -> Self {
35        let tick_rate = Duration::from_millis(tick_rate);
36        let (sender, receiver) = mpsc::channel();
37        let handler = {
38            let sender = sender.clone();
39            thread::spawn(move || {
40                let mut last_tick = Instant::now();
41                loop {
42                    let timeout = tick_rate
43                        .checked_sub(last_tick.elapsed())
44                        .unwrap_or(tick_rate);
45
46                    if event::poll(timeout).expect("no events available") {
47                        match event::read().expect("unable to read event") {
48                            CrosstermEvent::Key(e) => sender.send(Event::Key(e)),
49                            CrosstermEvent::Mouse(e) => sender.send(Event::Mouse(e)),
50                            CrosstermEvent::Resize(w, h) => sender.send(Event::Resize(w, h)),
51                            CrosstermEvent::FocusGained => Ok(()),
52                            CrosstermEvent::FocusLost => Ok(()),
53                            CrosstermEvent::Paste(_) => unimplemented!(),
54                        }
55                        .expect("failed to send terminal event")
56                    }
57
58                    if last_tick.elapsed() >= tick_rate {
59                        sender.send(Event::Tick).expect("failed to send tick event");
60                        last_tick = Instant::now();
61                    }
62                }
63            })
64        };
65        Self {
66            sender,
67            receiver,
68            handler,
69        }
70    }
71
72    /// Receive the next event from the handler thread.
73    ///
74    /// This function will always block the current thread if
75    /// there is no data available and it's possible for more data to be sent.
76    pub fn next(&self) -> AppResult<Event> {
77        Ok(self.receiver.recv()?)
78    }
79}