use crate::app::Task; use crate::app::{ExternalMsg, InternalMsg, MsgIn}; use crate::input::Key; use anyhow::Error; use crossterm::event::{self, Event, MouseEventKind}; use std::sync::mpsc; use std::sync::mpsc::{Receiver, Sender}; use std::thread; use std::time::Duration; pub(crate) struct EventReader { task_sender: Sender, stopper: Option<(Sender, Receiver<()>)>, } impl EventReader { pub(crate) fn new(task_sender: Sender) -> Self { Self { task_sender, stopper: None, } } pub(crate) fn start(&mut self) { let sender = self.task_sender.clone(); let (tx_stopper, rx_stopper) = mpsc::channel(); let (tx_ack, rx_ack) = mpsc::channel(); self.stopper = Some((tx_stopper, rx_ack)); thread::spawn(move || { keep_reading(sender, rx_stopper, tx_ack); }); } pub(crate) fn stop(&self) { if let Some((stopper, ack)) = &self.stopper { stopper.send(true).unwrap_or_default(); // Let's not panic when xplr stops. ack.recv().unwrap_or_default(); } } } fn keep_reading( tx_msg_in: Sender, rx_stopper: Receiver, tx_ack: Sender<()>, ) { loop { if rx_stopper.try_recv().unwrap_or(false) { tx_ack.send(()).unwrap(); break; } else if event::poll(std::time::Duration::from_millis(150)).unwrap_or_default() { // NOTE: The poll timeout need to stay low, else spawning sub subshell // and start typing immediately will cause panic. // To reproduce, press `:`, then press and hold `!`. let res = match event::read() { Ok(Event::Key(key)) => { let key = Key::from_event(key); let msg = MsgIn::Internal(InternalMsg::HandleKey(key)); tx_msg_in .send(Task::new(msg, Some(key))) .map_err(Error::new) } Ok(Event::Mouse(evt)) => match evt.kind { MouseEventKind::ScrollUp => { let msg = MsgIn::External(ExternalMsg::FocusPrevious); tx_msg_in.send(Task::new(msg, None)).map_err(Error::new) } MouseEventKind::ScrollDown => { let msg = MsgIn::External(ExternalMsg::FocusNext); tx_msg_in.send(Task::new(msg, None)).map_err(Error::new) } _ => Ok(()), }, Ok(Event::Resize(_, _)) => { let msg = MsgIn::External(ExternalMsg::Refresh); tx_msg_in.send(Task::new(msg, None)).map_err(Error::new) } Err(e) => Err(Error::new(e)), }; if let Err(e) = res { tx_msg_in .send(Task::new( MsgIn::External(ExternalMsg::LogError(e.to_string())), None, )) .unwrap_or_default(); // Let's not panic when xplr stops thread::sleep(Duration::from_secs(1)); } } } }