leetcode_tui_rs/
signals.rs1use leetcode_tui_core::Event;
2
3use color_eyre::Result;
4use crossterm::event::{Event as CrosstermEvent, EventStream, KeyEvent, KeyEventKind};
5use futures::StreamExt;
6use tokio::{
7 sync::mpsc::{self, UnboundedReceiver, UnboundedSender},
8 task::JoinHandle,
9};
10use tokio_util::sync::CancellationToken;
11
12pub(super) struct Signals {
13 tx: UnboundedSender<Event>,
14 rx: UnboundedReceiver<Event>,
15 cancellation_token: tokio_util::sync::CancellationToken,
16}
17
18impl Signals {
19 pub(super) fn start() -> Result<Self> {
20 let (tx, rx) = mpsc::unbounded_channel();
21 let cancellation_token = tokio_util::sync::CancellationToken::new();
22 let mut signals = Self {
23 tx: tx.clone(),
24 rx,
25 cancellation_token,
26 };
27
28 Event::init(tx);
29 signals.spawn_crossterm_task();
30 Ok(signals)
31 }
32
33 pub(super) async fn recv(&mut self) -> Option<Event> {
34 self.rx.recv().await
35 }
36
37 pub fn stop_looking_for_io_events(&mut self) {
38 self.cancellation_token.cancel()
39 }
40
41 pub fn start_looking_for_io_events(&mut self) {
42 self.reset_cancellation_token();
43 self.spawn_crossterm_task();
44 }
45
46 fn reset_cancellation_token(&mut self) {
47 self.cancellation_token = CancellationToken::new();
48 }
49
50 fn spawn_crossterm_task(&mut self) -> JoinHandle<()> {
51 let tx = self.tx.clone();
52 let token = self.cancellation_token.clone();
53
54 tokio::spawn(async move {
55 let mut reader = EventStream::new();
56
57 loop {
58 tokio::select! {
59 _ = token.cancelled() => {
60 break;
61 }
62
63 Some(Ok(event)) = reader.next() => {
64 let event = match event {
65 CrosstermEvent::Key(key @ KeyEvent { kind: KeyEventKind::Press, .. }) => {
68 let k: leetcode_tui_config::key::Key = key.into();
69 if let leetcode_tui_config::key::Key::Ctrl('c') = k {
70 Event::Quit
71 } else {
72 Event::Key(key)
73 }
74 },
75 CrosstermEvent::Resize(cols, rows) => Event::Resize(cols, rows),
77 _ => continue,
78 };
79 if tx.send(event).is_err() {
80 break;
81 }
82 }
83 }
84 }
85 })
86 }
87}