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
use std::{
sync::mpsc::{channel, Receiver, Sender},
thread::{self, JoinHandle},
time::{Duration, Instant},
};
use anyhow::Result;
use super::key::Key;
pub enum InputEvent {
/// An input event occurred.
Input(Key),
/// A tick event occurred.
Tick,
// A Terminal resize event occurred.
// Resize,
}
/// A small event handler that wrap crossterm input and tick event. Each event
/// type is handled in its own thread and returned to a common `Receiver`
pub struct EventHandler {
/// Event receiver channel
receiver: Receiver<InputEvent>,
/// Event sender channel
sender: Sender<InputEvent>,
/// Event handler thread
handler: JoinHandle<()>,
}
impl EventHandler {
/// Constructs an new instance of `Events` with the default config.
/// mpsc setup
pub fn new(tick_rate: Duration) -> Self {
let (sender, receiver) = channel();
let handler = {
let sender = sender.clone();
thread::spawn(move || {
let mut last_tick = Instant::now();
loop {
let timeout = tick_rate
.checked_sub(last_tick.elapsed())
.unwrap_or(tick_rate);
// poll for tick rate duration, if no event, sent tick event.
if crossterm::event::poll(timeout).expect("No events available") {
if let crossterm::event::Event::Key(key) =
crossterm::event::read().expect("Undable to read event")
{
if key.kind == crossterm::event::KeyEventKind::Press {
let key = Key::from(key);
sender
.send(InputEvent::Input(key))
.expect("Failed to send terminal event");
}
}
}
// Solves ghost input after holding down
if last_tick.elapsed() >= tick_rate {
sender
.send(InputEvent::Tick)
.expect("failed to send tick event");
last_tick = Instant::now();
}
}
})
};
EventHandler {
receiver,
sender,
handler,
}
}
/// Attempts to read an event.
/// This function will block the current thread.
pub fn next(&self) -> Result<InputEvent> {
Ok(self.receiver.recv()?)
}
}