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::Moved
55 | MouseEventKind::ScrollUp
56 | MouseEventKind::ScrollDown
57 );
58 if forward
59 && event_tx.send(AppEvent::Mouse(mouse)).is_err()
60 {
61 return;
62 }
63 }
64 Some(Ok(CEvent::Paste(text))) => {
65 if event_tx.send(AppEvent::Paste(text)).is_err() {
66 return;
67 }
68 }
69 Some(Ok(CEvent::Resize(w, h))) => {
70 if event_tx.send(AppEvent::Resize(w, h)).is_err() {
71 return;
72 }
73 }
74 Some(Ok(_)) => {}
75 Some(Err(_)) => return,
76 None => return,
77 }
78 }
79 _ = tick.tick() => {
80 if event_tx.send(AppEvent::Tick).is_err() {
81 return;
82 }
83 }
84 }
85 }
86 });
87
88 Self {
89 rx,
90 tx,
91 _task: task,
92 }
93 }
94
95 pub async fn next(&mut self) -> Option<AppEvent> {
96 self.rx.recv().await
97 }
98
99 pub fn tx(&self) -> mpsc::UnboundedSender<AppEvent> {
100 self.tx.clone()
101 }
102}