rush_sync_server/input/
mod.rs1pub mod keyboard;
2pub mod state;
3
4use crossterm::event::{self as crossterm_event, Event as CrosstermEvent, KeyEvent, MouseEventKind};
5use std::sync::OnceLock;
6use tokio::sync::mpsc::{self, Sender};
7use tokio::time::{interval, Duration, Instant};
8
9#[derive(Debug)]
10pub enum AppEvent {
11 Input(KeyEvent),
12 MouseScrollUp,
13 MouseScrollDown,
14 Tick,
15 Resize(u16, u16),
16 Progress(String),
18}
19
20static PROGRESS_TX: OnceLock<mpsc::UnboundedSender<String>> = OnceLock::new();
24
25pub fn init_progress_channel() -> mpsc::UnboundedReceiver<String> {
28 let (tx, rx) = mpsc::unbounded_channel();
29 let _ = PROGRESS_TX.set(tx);
30 rx
31}
32
33pub fn send_progress(message: String) -> bool {
36 if let Some(tx) = PROGRESS_TX.get() {
37 tx.send(message).is_ok()
38 } else {
39 false
40 }
41}
42
43pub struct EventHandler {
44 rx: mpsc::Receiver<AppEvent>,
45 shutdown_tx: Vec<Sender<()>>,
46}
47
48impl EventHandler {
49 pub fn new(tick_rate: Duration) -> Self {
50 let (tx, rx) = mpsc::channel(100);
51 let mut shutdown_tx = Vec::new();
52
53 let (input_shutdown_tx, input_shutdown_rx) = mpsc::channel(1);
55 shutdown_tx.push(input_shutdown_tx);
56 Self::spawn_input_handler(tx.clone(), input_shutdown_rx);
57
58 let (tick_shutdown_tx, tick_shutdown_rx) = mpsc::channel(1);
60 shutdown_tx.push(tick_shutdown_tx);
61 Self::spawn_tick_handler(tx, tick_rate, tick_shutdown_rx);
62
63 EventHandler { rx, shutdown_tx }
64 }
65
66 fn spawn_input_handler(tx: mpsc::Sender<AppEvent>, mut shutdown_rx: mpsc::Receiver<()>) {
67 tokio::spawn(async move {
68 let (mut last_key_time, mut last_resize_time) = (Instant::now(), Instant::now());
69 let (key_interval, resize_interval) =
70 (Duration::from_millis(16), Duration::from_millis(50));
71
72 loop {
73 tokio::select! {
74 _ = shutdown_rx.recv() => break,
75 _ = async {
76 if crossterm_event::poll(Duration::from_millis(99)).unwrap_or(false) {
77 if let Ok(event) = crossterm_event::read() {
78 let now = Instant::now();
79 match event {
80 CrosstermEvent::Key(key) if now.duration_since(last_key_time) >= key_interval => {
81 let _ = tx.send(AppEvent::Input(key)).await;
82 last_key_time = now;
83 }
84 CrosstermEvent::Mouse(mouse) => {
85 match mouse.kind {
86 MouseEventKind::ScrollUp => {
87 let _ = tx.send(AppEvent::MouseScrollUp).await;
88 }
89 MouseEventKind::ScrollDown => {
90 let _ = tx.send(AppEvent::MouseScrollDown).await;
91 }
92 _ => {}
93 }
94 }
95 CrosstermEvent::Resize(w, h) if now.duration_since(last_resize_time) >= resize_interval => {
96 let _ = tx.send(AppEvent::Resize(w, h)).await;
97 last_resize_time = now;
98 }
99 _ => {}
100 }
101 }
102 }
103 } => {}
104 }
105 }
106 });
107 }
108
109 fn spawn_tick_handler(
110 tx: mpsc::Sender<AppEvent>,
111 tick_rate: Duration,
112 mut shutdown_rx: mpsc::Receiver<()>,
113 ) {
114 tokio::spawn(async move {
115 let mut interval = interval(tick_rate);
116 loop {
117 tokio::select! {
118 _ = shutdown_rx.recv() => break,
119 _ = interval.tick() => { let _ = tx.send(AppEvent::Tick).await; }
120 }
121 }
122 });
123 }
124
125 pub async fn next(&mut self) -> Option<AppEvent> {
126 self.rx.recv().await
127 }
128
129 pub async fn shutdown(&mut self) {
130 for tx in &self.shutdown_tx {
131 let _ = tx.send(()).await;
132 }
133 }
134}