aboutsummaryrefslogtreecommitdiff
path: root/src/frontend/event.rs
diff options
context:
space:
mode:
authorlukeflo2024-09-20 22:31:33 +0200
committerlukeflo2024-09-20 22:31:33 +0200
commitdc45b960a4eda299058e597f6867e4d4be109b1b (patch)
tree30501aa9f1f26413f959d751302fa189508eacc1 /src/frontend/event.rs
downloadbibiman-dc45b960a4eda299058e597f6867e4d4be109b1b.tar.gz
bibiman-dc45b960a4eda299058e597f6867e4d4be109b1b.zip
initial commit
Diffstat (limited to 'src/frontend/event.rs')
-rw-r--r--src/frontend/event.rs97
1 files changed, 97 insertions, 0 deletions
diff --git a/src/frontend/event.rs b/src/frontend/event.rs
new file mode 100644
index 0000000..f83dfea
--- /dev/null
+++ b/src/frontend/event.rs
@@ -0,0 +1,97 @@
+use std::time::Duration;
+
+use crossterm::event::{Event as CrosstermEvent, KeyEvent, MouseEvent};
+use futures::{FutureExt, StreamExt};
+use tokio::sync::mpsc;
+
+use crate::frontend::app::AppResult;
+
+/// Terminal events.
+#[derive(Clone, Copy, Debug)]
+pub enum Event {
+ /// Terminal tick.
+ Tick,
+ /// Key press.
+ Key(KeyEvent),
+ /// Mouse click/scroll.
+ Mouse(MouseEvent),
+ /// Terminal resize.
+ Resize(u16, u16),
+}
+
+/// Terminal event handler.
+#[allow(dead_code)]
+#[derive(Debug)]
+pub struct EventHandler {
+ /// Event sender channel.
+ sender: mpsc::UnboundedSender<Event>,
+ /// Event receiver channel.
+ receiver: mpsc::UnboundedReceiver<Event>,
+ /// Event handler thread.
+ handler: tokio::task::JoinHandle<()>,
+}
+
+impl EventHandler {
+ /// Constructs a new instance of [`EventHandler`].
+ pub fn new(tick_rate: u64) -> Self {
+ let tick_rate = Duration::from_millis(tick_rate);
+ let (sender, receiver) = mpsc::unbounded_channel();
+ let _sender = sender.clone();
+ let handler = tokio::spawn(async move {
+ let mut reader = crossterm::event::EventStream::new();
+ let mut tick = tokio::time::interval(tick_rate);
+ loop {
+ let tick_delay = tick.tick();
+ let crossterm_event = reader.next().fuse();
+ tokio::select! {
+ _ = _sender.closed() => {
+ break;
+ }
+ _ = tick_delay => {
+ _sender.send(Event::Tick).unwrap();
+ }
+ Some(Ok(evt)) = crossterm_event => {
+ match evt {
+ CrosstermEvent::Key(key) => {
+ if key.kind == crossterm::event::KeyEventKind::Press {
+ _sender.send(Event::Key(key)).unwrap();
+ }
+ },
+ CrosstermEvent::Mouse(mouse) => {
+ _sender.send(Event::Mouse(mouse)).unwrap();
+ },
+ CrosstermEvent::Resize(x, y) => {
+ _sender.send(Event::Resize(x, y)).unwrap();
+ },
+ CrosstermEvent::FocusLost => {
+ },
+ CrosstermEvent::FocusGained => {
+ },
+ CrosstermEvent::Paste(_) => {
+ },
+ }
+ }
+ };
+ }
+ });
+ Self {
+ sender,
+ receiver,
+ handler,
+ }
+ }
+
+ /// Receive the next event from the handler thread.
+ ///
+ /// This function will always block the current thread if
+ /// there is no data available and it's possible for more data to be sent.
+ pub async fn next(&mut self) -> AppResult<Event> {
+ self.receiver
+ .recv()
+ .await
+ .ok_or(Box::new(std::io::Error::new(
+ std::io::ErrorKind::Other,
+ "This is an IO error",
+ )))
+ }
+}