Skip to main content

systemctl_tui/
event.rs

1use std::{sync::Arc, time::Duration};
2
3use crossterm::event::{Event as CrosstermEvent, KeyEvent, KeyEventKind, MouseEvent};
4use futures::{FutureExt, StreamExt};
5use tokio::{
6  sync::{mpsc, Mutex},
7  task::JoinHandle,
8};
9use tokio_util::sync::CancellationToken;
10
11use crate::{
12  action::Action,
13  components::{home::Home, Component},
14};
15
16#[derive(Clone, Copy, Debug)]
17pub enum Event {
18  Quit,
19  Error,
20  Closed,
21  RenderTick,
22  RefreshTick,
23  Key(KeyEvent),
24  Mouse(MouseEvent),
25  Resize(u16, u16),
26}
27
28pub struct EventHandler {
29  pub task: JoinHandle<()>,
30  cancellation_token: CancellationToken,
31}
32
33const SERVICE_REFRESH_INTERVAL_MS: u64 = 5000;
34
35impl EventHandler {
36  pub fn new(home: Arc<Mutex<Home>>, action_tx: mpsc::UnboundedSender<Action>) -> Self {
37    let (event_tx, mut event_rx) = mpsc::unbounded_channel();
38    let cancellation_token = CancellationToken::new();
39    let _cancellation_token = cancellation_token.clone();
40    let task = tokio::spawn(async move {
41      let mut reader = crossterm::event::EventStream::new();
42      let mut refresh_services_interval = tokio::time::interval(Duration::from_millis(SERVICE_REFRESH_INTERVAL_MS));
43      refresh_services_interval.tick().await;
44      loop {
45        let refresh_delay = refresh_services_interval.tick();
46        let crossterm_event = reader.next().fuse();
47        tokio::select! {
48          _ = _cancellation_token.cancelled() => {
49            break;
50          }
51          maybe_event = crossterm_event => {
52            match maybe_event {
53              Some(Ok(evt)) => {
54                match evt {
55                  CrosstermEvent::Key(key) => {
56                    if key.kind == KeyEventKind::Press {
57                      event_tx.send(Event::Key(key)).unwrap();
58                    }
59                  },
60                  // interestingly, we never get these if running in dev mode with watchexec
61                  CrosstermEvent::Resize(x, y) => {
62                    event_tx.send(Event::Resize(x, y)).unwrap();
63                  },
64                  _ => {},
65                }
66              }
67              Some(Err(_)) => {
68                event_tx.send(Event::Error).unwrap();
69              }
70              None => {},
71            }
72          },
73          _ = refresh_delay => {
74            event_tx.send(Event::RefreshTick).unwrap();
75          },
76          event = event_rx.recv() => {
77            let actions = home.lock().await.handle_events(event);
78            for action in actions {
79              action_tx.send(action).unwrap();
80            }
81          }
82        }
83      }
84    });
85    Self { task, cancellation_token }
86  }
87
88  pub fn stop(&mut self) {
89    self.cancellation_token.cancel();
90  }
91}