1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
//! Event handling for the TUI
//!
//! This module handles terminal events (key presses, mouse events, resize)
//! using crossterm's event system.
use crossterm::event::{self, Event as CrosstermEvent, KeyEvent, KeyEventKind, MouseEvent};
use std::sync::mpsc;
use std::thread;
use std::time::{Duration, Instant};
/// Terminal events
#[derive(Debug, Clone)]
pub enum Event {
/// Key press event
Key(KeyEvent),
/// Mouse event
Mouse(MouseEvent),
/// Terminal resize
Resize(u16, u16),
/// Tick event for periodic updates
Tick,
}
/// Event handler for terminal events
pub struct EventHandler {
/// Event sender
#[allow(dead_code)]
sender: mpsc::Sender<Event>,
/// Event receiver
receiver: mpsc::Receiver<Event>,
/// Event thread handle
#[allow(dead_code)]
handler: thread::JoinHandle<()>,
}
impl EventHandler {
/// Create a new event handler with the specified tick rate
pub fn new(tick_rate: Duration) -> Self {
let (sender, receiver) = mpsc::channel();
let handler = {
let sender = sender.clone();
thread::spawn(move || {
let mut last_tick = Instant::now();
loop {
// Calculate timeout for next tick
let timeout = tick_rate
.checked_sub(last_tick.elapsed())
.unwrap_or(Duration::ZERO);
// Poll for events
if event::poll(timeout).expect("Failed to poll events") {
match event::read().expect("Failed to read event") {
CrosstermEvent::Key(key) if key.kind == KeyEventKind::Press => {
if sender.send(Event::Key(key)).is_err() {
return;
}
}
CrosstermEvent::Mouse(mouse) => {
if sender.send(Event::Mouse(mouse)).is_err() {
return;
}
}
CrosstermEvent::Resize(width, height) => {
if sender.send(Event::Resize(width, height)).is_err() {
return;
}
}
_ => {}
}
}
// Send tick event if needed
if last_tick.elapsed() >= tick_rate {
if sender.send(Event::Tick).is_err() {
return;
}
last_tick = Instant::now();
}
}
})
};
Self {
sender,
receiver,
handler,
}
}
/// Get the next event (blocking)
pub fn next(&self) -> Result<Event, mpsc::RecvError> {
self.receiver.recv()
}
/// Try to get the next event (non-blocking)
#[allow(dead_code)]
pub fn try_next(&self) -> Result<Event, mpsc::TryRecvError> {
self.receiver.try_recv()
}
}
impl Default for EventHandler {
fn default() -> Self {
Self::new(Duration::from_millis(250))
}
}