leetcode_tui_rs/
signals.rs

1use 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                            // We need to check key event kind;
66                            // otherwise event will be dispatched twice.
67                            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::Paste(str) => Event::Paste(str),
76                            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}