1use crossterm::event::{Event as CEvent, EventStream, KeyEventKind, MouseEventKind};
2use futures::StreamExt;
3use tokio::sync::mpsc;
4
5use crate::agent::AgentEvent;
6
7pub enum AppEvent {
8 Key(crossterm::event::KeyEvent),
9 Mouse(crossterm::event::MouseEvent),
10 Paste(String),
11 Tick,
12 Agent(AgentEvent),
13 Resize(u16, u16),
14}
15
16impl Default for EventHandler {
17 fn default() -> Self {
18 Self::new()
19 }
20}
21
22pub struct EventHandler {
23 rx: mpsc::UnboundedReceiver<AppEvent>,
24 tx: mpsc::UnboundedSender<AppEvent>,
25 _task: tokio::task::JoinHandle<()>,
26}
27
28impl EventHandler {
29 pub fn new() -> Self {
30 let (tx, rx) = mpsc::unbounded_channel();
31 let event_tx = tx.clone();
32
33 let task = tokio::spawn(async move {
34 let mut reader = EventStream::new();
35 let mut tick = tokio::time::interval(std::time::Duration::from_millis(16));
36
37 loop {
38 tokio::select! {
39 maybe_event = reader.next() => {
40 match maybe_event {
41 Some(Ok(CEvent::Key(key))) => {
42 if key.kind == KeyEventKind::Press
43 && event_tx.send(AppEvent::Key(key)).is_err()
44 {
45 return;
46 }
47 }
48 Some(Ok(CEvent::Mouse(mouse))) => {
49 let forward = matches!(
50 mouse.kind,
51 MouseEventKind::Down(_)
52 | MouseEventKind::Up(_)
53 | MouseEventKind::Drag(_)
54 | MouseEventKind::ScrollUp
55 | MouseEventKind::ScrollDown
56 );
57 if forward
58 && event_tx.send(AppEvent::Mouse(mouse)).is_err()
59 {
60 return;
61 }
62 }
63 Some(Ok(CEvent::Paste(text))) => {
64 if event_tx.send(AppEvent::Paste(text)).is_err() {
65 return;
66 }
67 }
68 Some(Ok(CEvent::Resize(w, h))) => {
69 if event_tx.send(AppEvent::Resize(w, h)).is_err() {
70 return;
71 }
72 }
73 Some(Ok(_)) => {}
74 Some(Err(_)) => return,
75 None => return,
76 }
77 }
78 _ = tick.tick() => {
79 if event_tx.send(AppEvent::Tick).is_err() {
80 return;
81 }
82 }
83 }
84 }
85 });
86
87 Self {
88 rx,
89 tx,
90 _task: task,
91 }
92 }
93
94 pub async fn next(&mut self) -> Option<AppEvent> {
95 self.rx.recv().await
96 }
97
98 pub fn tx(&self) -> mpsc::UnboundedSender<AppEvent> {
99 self.tx.clone()
100 }
101}