// bibiman - a TUI for managing BibLaTeX databases // Copyright (C) 2024 lukeflo // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . ///// use std::time::Duration; use color_eyre::eyre::{OptionExt, Result}; use crossterm::event::{Event as CrosstermEvent, KeyEvent, MouseEvent}; use futures::{FutureExt, StreamExt}; use tokio::sync::mpsc; /// 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 receiver channel. receiver: mpsc::UnboundedReceiver, /// 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) -> Result { self.receiver.recv().await.ok_or_eyre("This is an IO error") // .ok_or(Box::new(std::io::Error::new( // std::io::ErrorKind::Other, // "This is an IO error", // ))) } }