1use anyhow::Result;
2use crossterm::event::{self, Event as CrosstermEvent, KeyEvent, MouseEvent};
3use std::sync::mpsc;
4use std::sync::{
5 atomic::{AtomicBool, Ordering},
6 Arc,
7};
8use std::thread;
9use std::time::{Duration, Instant};
10
11#[derive(Clone, Copy, Debug)]
17pub enum Event {
18 Key(KeyEvent),
20 Mouse(MouseEvent),
22 Resize(u16, u16),
24 Tick,
26}
27
28#[allow(dead_code)]
35#[derive(Debug)]
36pub struct EventHandler {
37 sender: mpsc::Sender<Event>,
39 receiver: mpsc::Receiver<Event>,
41 handler: thread::JoinHandle<()>,
43 pub key_input_disabled: Arc<AtomicBool>,
45}
46
47impl EventHandler {
48 pub fn new(tick_rate: u64) -> Self {
50 let tick_rate = Duration::from_millis(tick_rate);
51 let (sender, receiver) = mpsc::channel();
52 let key_input_disabled = Arc::new(AtomicBool::new(false));
53 let handler = {
54 let sender = sender.clone();
55 let key_input_disabled = key_input_disabled.clone();
56 thread::spawn(move || {
57 let mut last_tick = Instant::now();
58 loop {
59 let timeout = tick_rate
60 .checked_sub(last_tick.elapsed())
61 .unwrap_or(tick_rate);
62 if key_input_disabled.load(Ordering::Relaxed) {
63 thread::sleep(timeout);
64 continue;
65 } else if event::poll(timeout).expect("no events available")
66 {
67 match event::read().expect("unable to read event") {
68 CrosstermEvent::Key(e) => {
69 sender.send(Event::Key(e))
70 }
71 CrosstermEvent::Mouse(e) => {
72 sender.send(Event::Mouse(e))
73 }
74 CrosstermEvent::Resize(w, h) => {
75 sender.send(Event::Resize(w, h))
76 }
77 _ => Ok(()),
78 }
79 .expect("failed to send terminal event")
80 }
81 if last_tick.elapsed() >= tick_rate {
82 sender
83 .send(Event::Tick)
84 .expect("failed to send tick event");
85 last_tick = Instant::now();
86 }
87 }
88 })
89 };
90 Self {
91 sender,
92 receiver,
93 handler,
94 key_input_disabled,
95 }
96 }
97
98 pub fn next(&self) -> Result<Event, mpsc::RecvError> {
107 self.receiver.recv()
108 }
109}
110
111#[cfg(feature = "tui-tests")]
112#[cfg(test)]
113mod tests {
114 use super::*;
115 use crossterm::event::{KeyCode, KeyModifiers};
116 use pretty_assertions::assert_eq;
117 #[test]
118 fn test_term_event() -> Result<()> {
119 let events = EventHandler::new(100);
120 for step in 0..2 {
121 if step == 1 {
122 let sender = events.sender.clone();
123 thread::spawn(move || {
124 sender.send(Event::Key(KeyEvent::new(
125 KeyCode::Esc,
126 KeyModifiers::NONE,
127 )))
128 });
129 }
130 match events.next()? {
131 Event::Key(key_event) => {
132 if key_event.code == KeyCode::Esc {
133 assert_eq!(1, step);
134 break;
135 }
136 }
137 Event::Tick => assert_eq!(0, step),
138 _ => {}
139 };
140 }
141 Ok(())
142 }
143}