crashdump_viewer_lib/
event.rs

1// Copyright (c) Meta Platforms, Inc. and affiliates.
2
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6
7//     http://www.apache.org/licenses/LICENSE-2.0
8
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::time::Duration;
16
17use crossterm::event::{Event as CrosstermEvent, KeyEvent, MouseEvent};
18use futures::{FutureExt, StreamExt};
19use tokio::sync::mpsc;
20
21use crate::app::AppResult;
22
23/// Terminal events.
24#[derive(Clone, Copy, Debug)]
25pub enum Event {
26    /// Terminal tick.
27    Tick,
28    /// Key press.
29    Key(KeyEvent),
30    /// Mouse click/scroll.
31    Mouse(MouseEvent),
32    /// Terminal resize.
33    Resize(u16, u16),
34}
35
36/// Terminal event handler.
37#[allow(dead_code)]
38#[derive(Debug)]
39pub struct EventHandler {
40    /// Event sender channel.
41    sender: mpsc::UnboundedSender<Event>,
42    /// Event receiver channel.
43    receiver: mpsc::UnboundedReceiver<Event>,
44    /// Event handler thread.
45    handler: tokio::task::JoinHandle<()>,
46}
47
48impl EventHandler {
49    /// Constructs a new instance of [`EventHandler`].
50    pub fn new(tick_rate: u64) -> Self {
51        let tick_rate = Duration::from_millis(tick_rate);
52        let (sender, receiver) = mpsc::unbounded_channel();
53        let _sender = sender.clone();
54        let handler = tokio::spawn(async move {
55            let mut reader = crossterm::event::EventStream::new();
56            let mut tick = tokio::time::interval(tick_rate);
57            loop {
58                let tick_delay = tick.tick();
59                let crossterm_event = reader.next().fuse();
60                tokio::select! {
61                  _ = _sender.closed() => {
62                    break;
63                  }
64                  _ = tick_delay => {
65                    _sender.send(Event::Tick).unwrap();
66                  }
67                  Some(Ok(evt)) = crossterm_event => {
68                    match evt {
69                      CrosstermEvent::Key(key) => {
70                        if key.kind == crossterm::event::KeyEventKind::Press {
71                          _sender.send(Event::Key(key)).unwrap();
72                        }
73                      },
74                      CrosstermEvent::Mouse(mouse) => {
75                        _sender.send(Event::Mouse(mouse)).unwrap();
76                      },
77                      CrosstermEvent::Resize(x, y) => {
78                        _sender.send(Event::Resize(x, y)).unwrap();
79                      },
80                      CrosstermEvent::FocusLost => {
81                      },
82                      CrosstermEvent::FocusGained => {
83                      },
84                      CrosstermEvent::Paste(_) => {
85                      },
86                    }
87                  }
88                };
89            }
90        });
91        Self {
92            sender,
93            receiver,
94            handler,
95        }
96    }
97
98    /// Receive the next event from the handler thread.
99    ///
100    /// This function will always block the current thread if
101    /// there is no data available and it's possible for more data to be sent.
102    pub async fn next(&mut self) -> AppResult<Event> {
103        self.receiver
104            .recv()
105            .await
106            .ok_or(Box::new(std::io::Error::new(
107                std::io::ErrorKind::Other,
108                "This is an IO error",
109            )))
110    }
111}