leetcode_tui_rs/
signals.rs

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
use leetcode_tui_core::Event;

use color_eyre::Result;
use crossterm::event::{Event as CrosstermEvent, EventStream, KeyEvent, KeyEventKind};
use futures::StreamExt;
use tokio::{
    sync::mpsc::{self, UnboundedReceiver, UnboundedSender},
    task::JoinHandle,
};
use tokio_util::sync::CancellationToken;

pub(super) struct Signals {
    tx: UnboundedSender<Event>,
    rx: UnboundedReceiver<Event>,
    cancellation_token: tokio_util::sync::CancellationToken,
}

impl Signals {
    pub(super) fn start() -> Result<Self> {
        let (tx, rx) = mpsc::unbounded_channel();
        let cancellation_token = tokio_util::sync::CancellationToken::new();
        let mut signals = Self {
            tx: tx.clone(),
            rx,
            cancellation_token,
        };

        Event::init(tx);
        signals.spawn_crossterm_task();
        Ok(signals)
    }

    pub(super) async fn recv(&mut self) -> Option<Event> {
        self.rx.recv().await
    }

    pub fn stop_looking_for_io_events(&mut self) {
        self.cancellation_token.cancel()
    }

    pub fn start_looking_for_io_events(&mut self) {
        self.reset_cancellation_token();
        self.spawn_crossterm_task();
    }

    fn reset_cancellation_token(&mut self) {
        self.cancellation_token = CancellationToken::new();
    }

    fn spawn_crossterm_task(&mut self) -> JoinHandle<()> {
        let tx = self.tx.clone();
        let token = self.cancellation_token.clone();

        tokio::spawn(async move {
            let mut reader = EventStream::new();

            loop {
                tokio::select! {
                    _ = token.cancelled() => {
                        break;
                    }

                    Some(Ok(event)) = reader.next() => {
                        let event = match event {
                            // We need to check key event kind;
                            // otherwise event will be dispatched twice.
                            CrosstermEvent::Key(key @ KeyEvent { kind: KeyEventKind::Press, .. }) => {
                                let k: leetcode_tui_config::key::Key = key.into();
                                if let leetcode_tui_config::key::Key::Ctrl('c') = k {
                                    Event::Quit
                                } else {
                                    Event::Key(key)
                                }
                            },
                            // CrosstermEvent::Paste(str) => Event::Paste(str),
                            CrosstermEvent::Resize(cols, rows) => Event::Resize(cols, rows),
                            _ => continue,
                        };
                        if tx.send(event).is_err() {
                            break;
                        }
                    }
                }
            }
        })
    }
}