rush_sync_server/input/
mod.rs1pub mod input;
2pub mod keyboard;
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}