dyd/app/
event.rs

1use crate::app::AppResult;
2use crate::git::repo::{Log, RepoStatus};
3use crossterm::event::{self, Event as CrosstermEvent, KeyEvent, MouseEvent};
4use std::sync::mpsc;
5use std::thread;
6use std::time::{Duration, Instant};
7
8/// Terminal events.
9#[derive(Clone, Debug)]
10pub enum Event {
11  /// Terminal tick.
12  Tick(mpsc::Sender<Event>),
13  /// Key press.
14  Key(KeyEvent),
15  /// Mouse click/scroll.
16  Mouse(MouseEvent),
17  /// Terminal resize.
18  Resize(u16, u16),
19  /// The current state of a Repo has changed.
20  RepoStatusChange(String, RepoStatus),
21  /// The Repo git actions are complete
22  RepoStatusComplete(String, Vec<Log>),
23}
24
25/// Terminal event handler.
26#[derive(Debug)]
27pub struct EventHandler {
28  /// Event sender channel.
29  pub sender: mpsc::Sender<Event>,
30  /// Event receiver channel.
31  receiver: mpsc::Receiver<Event>,
32  /// Event handler thread.
33  pub handler: thread::JoinHandle<()>,
34}
35
36impl EventHandler {
37  /// Constructs a new instance of [`EventHandler`].
38  pub fn new(tick_rate: u64) -> Self {
39    let tick_rate = Duration::from_millis(tick_rate);
40    let (sender, receiver) = mpsc::channel();
41    let handler = {
42      let sender = sender.clone();
43      thread::spawn(move || {
44        let mut last_tick = Instant::now();
45        loop {
46          let timeout = tick_rate
47            .checked_sub(last_tick.elapsed())
48            .unwrap_or(tick_rate);
49
50          if event::poll(timeout).expect("no events available") {
51            match event::read().expect("unable to read event") {
52              CrosstermEvent::Key(e) => sender.send(Event::Key(e)),
53              CrosstermEvent::Mouse(e) => sender.send(Event::Mouse(e)),
54              CrosstermEvent::Resize(w, h) => sender.send(Event::Resize(w, h)),
55              _ => Ok(()),
56            }
57            .expect("failed to send terminal event")
58          }
59
60          if last_tick.elapsed() >= tick_rate {
61            sender
62              .send(Event::Tick(sender.clone()))
63              .expect("failed to send tick event");
64            last_tick = Instant::now();
65          }
66        }
67      })
68    };
69    Self {
70      sender,
71      receiver,
72      handler,
73    }
74  }
75
76  /// Receive the next event from the handler thread.
77  ///
78  /// This function will always block the current thread if
79  /// there is no data available and it's possible for more data to be sent.
80  pub fn next(&self) -> AppResult<Event> {
81    Ok(self.receiver.recv()?)
82  }
83}