Skip to main content

binocular/runtime/interactive/
input.rs

1use crate::infra::channel::Sender;
2use crossterm::event::{self, Event, KeyEvent, KeyEventKind};
3use std::time::Duration;
4
5const TICK_RATE: Duration = Duration::from_millis(120);
6
7pub enum InputEvent {
8    Key(KeyEvent),
9    Resize(u16, u16),
10    Tick,
11}
12
13pub fn spawn_input_handler(tx: impl Sender<InputEvent>) {
14    std::thread::spawn(move || {
15        let mut last_tick = std::time::Instant::now();
16
17        loop {
18            let timeout = TICK_RATE
19                .checked_sub(last_tick.elapsed())
20                .unwrap_or_else(|| Duration::from_secs(0));
21
22            if event::poll(timeout).unwrap_or(false) {
23                match event::read() {
24                    Ok(Event::Key(key)) => {
25                        if key.kind == KeyEventKind::Press {
26                            let _ = tx.send(InputEvent::Key(key));
27                        }
28                    }
29                    Ok(Event::Resize(w, h)) => {
30                        let _ = tx.send(InputEvent::Resize(w, h));
31                    }
32                    _ => {}
33                }
34            }
35
36            if last_tick.elapsed() >= TICK_RATE {
37                let _ = tx.send(InputEvent::Tick);
38                last_tick = std::time::Instant::now();
39            }
40        }
41    });
42}