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}