rush_sync_server/input/
mod.rs

1pub mod keyboard;
2pub mod state;
3
4use crossterm::event::{self as crossterm_event, Event as CrosstermEvent, KeyEvent};
5use tokio::sync::mpsc::{self, Sender};
6use tokio::time::{interval, Duration, Instant};
7
8#[derive(Debug)]
9pub enum AppEvent {
10    Input(KeyEvent),
11    Tick,
12    Resize(u16, u16),
13}
14
15pub struct EventHandler {
16    rx: mpsc::Receiver<AppEvent>,
17    shutdown_tx: Vec<Sender<()>>,
18}
19
20impl EventHandler {
21    pub fn new(tick_rate: Duration) -> Self {
22        let (tx, rx) = mpsc::channel(100);
23        let mut shutdown_tx = Vec::new();
24
25        let (input_shutdown_tx, mut input_shutdown_rx) = mpsc::channel(1);
26        shutdown_tx.push(input_shutdown_tx);
27
28        let input_tx = tx.clone();
29        tokio::spawn(async move {
30            let mut last_event_time = Instant::now();
31            let min_event_interval = Duration::from_millis(16);
32            let mut last_resize_time = Instant::now();
33            let min_resize_interval = Duration::from_millis(50);
34
35            loop {
36                tokio::select! {
37                    _ = input_shutdown_rx.recv() => break,
38                    _ = async {
39                        if crossterm_event::poll(Duration::from_millis(99)).unwrap() {
40                            let now = Instant::now();
41
42                            if let Ok(event) = crossterm_event::read() {
43                                match event {
44                                    CrosstermEvent::Key(key) => {
45                                        if now.duration_since(last_event_time) >= min_event_interval {
46                                            let _ = input_tx.send(AppEvent::Input(key)).await;
47                                            last_event_time = now;
48                                        }
49                                    }
50                                    CrosstermEvent::Resize(width, height) => {
51                                        if now.duration_since(last_resize_time) >= min_resize_interval {
52                                            let _ = input_tx.send(AppEvent::Resize(width, height)).await;
53                                            last_resize_time = now;
54
55                                            log::trace!(
56                                                "🔄 Resize event throttled: {}x{} ({}ms since last)",
57                                                width, height,
58                                                now.duration_since(last_resize_time).as_millis()
59                                            );
60                                        } else {
61                                            log::trace!(
62                                                "⏭️ Resize event dropped (too fast): {}x{}",
63                                                width, height
64                                            );
65                                        }
66                                    }
67                                    _ => {}
68                                }
69                            }
70                        }
71                    } => {}
72                }
73            }
74        });
75
76        let (tick_shutdown_tx, mut tick_shutdown_rx) = mpsc::channel(1);
77        shutdown_tx.push(tick_shutdown_tx);
78
79        let tick_tx = tx;
80        tokio::spawn(async move {
81            let mut interval = interval(tick_rate);
82            loop {
83                tokio::select! {
84                    _ = tick_shutdown_rx.recv() => break,
85                    _ = interval.tick() => {
86                        let _ = tick_tx.send(AppEvent::Tick).await;
87                    }
88                }
89            }
90        });
91
92        EventHandler { rx, shutdown_tx }
93    }
94
95    pub async fn next(&mut self) -> Option<AppEvent> {
96        self.rx.recv().await
97    }
98
99    pub async fn shutdown(&mut self) {
100        for tx in self.shutdown_tx.iter() {
101            let _ = tx.send(()).await;
102        }
103    }
104}