-
Notifications
You must be signed in to change notification settings - Fork 4
/
event.rs
116 lines (103 loc) · 3.6 KB
/
event.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
use crate::handler::MatuiEvent;
use crossterm::event::{self, Event as CrosstermEvent, KeyEvent};
use std::ops::Sub;
use std::sync::mpsc::{channel, Receiver, Sender};
use std::thread;
use std::time::{Duration, Instant};
/// Terminal events.
#[derive(Clone, Debug)]
pub enum Event {
/// Terminal tick.
Tick,
/// Force a clear and full re-draw.
Redraw,
/// The window has gained focus
Focus,
/// The window has lost focus
Blur,
/// Key press.
Key(KeyEvent),
/// App event
Matui(MatuiEvent),
}
/// Terminal event handler.
#[allow(dead_code)]
#[derive(Debug)]
pub struct EventHandler {
/// Event sender channel.
sender: Sender<Event>,
/// Event receiver channel.
receiver: Receiver<Event>,
/// Park sender.
pk_sender: Sender<bool>,
/// Event handler thread.
handler: thread::JoinHandle<()>,
}
impl EventHandler {
pub fn park(&self) {
self.pk_sender.send(true).expect("could send park event");
}
pub fn unpark(&self) {
self.handler.thread().unpark();
}
/// Constructs a new instance of [`EventHandler`].
pub fn new(tick_rate: u64) -> Self {
let tick_rate = Duration::from_millis(tick_rate);
let (sender, receiver) = channel();
let (pk_sender, pk_receiver) = channel();
let handler = {
let sender = sender.clone();
thread::spawn(move || {
let mut last_tick = Instant::now();
let mut last_park = Instant::now().sub(Duration::from_secs(10));
loop {
let timeout = tick_rate
.checked_sub(last_tick.elapsed())
.unwrap_or(tick_rate);
if pk_receiver.try_recv().is_ok() {
thread::park();
last_park = Instant::now()
}
if event::poll(timeout).expect("no events available") {
let event = event::read().expect("unable to read event");
if pk_receiver.try_recv().is_ok() {
thread::park();
last_park = Instant::now()
}
// right after we unpark, we can get a stream of
// garbage events
if last_park.elapsed() > Duration::from_millis(250) {
match event {
CrosstermEvent::Key(e) => sender.send(Event::Key(e)),
CrosstermEvent::FocusGained => sender.send(Event::Focus),
CrosstermEvent::FocusLost => sender.send(Event::Blur),
_ => Ok(()),
}
.expect("failed to send terminal event")
}
}
if last_tick.elapsed() >= tick_rate {
sender.send(Event::Tick).expect("failed to send tick event");
last_tick = Instant::now();
}
}
})
};
Self {
sender,
receiver,
pk_sender,
handler,
}
}
/// Receive the next event from the handler thread.
///
/// This function will always block the current thread if
/// there is no data available and it's possible for more data to be sent.
pub fn next(&self) -> anyhow::Result<Event> {
Ok(self.receiver.recv()?)
}
pub fn sender(&self) -> Sender<Event> {
self.sender.clone()
}
}