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