diff options
Diffstat (limited to 'src/event.rs')
-rw-r--r-- | src/event.rs | 125 |
1 files changed, 125 insertions, 0 deletions
diff --git a/src/event.rs b/src/event.rs new file mode 100644 index 0000000..ca44239 --- /dev/null +++ b/src/event.rs @@ -0,0 +1,125 @@ +#[derive(Debug)] +pub enum Event { + Key(textmode::Key), + Resize((u16, u16)), + ProcessOutput, + ProcessAlternateScreen, + ProcessExit, + ClockTimer, + Quit, +} + +pub struct Reader { + pending: async_std::sync::Mutex<Pending>, + cvar: async_std::sync::Condvar, +} + +impl Reader { + pub fn new( + input: async_std::channel::Receiver<Event>, + ) -> async_std::sync::Arc<Self> { + let this = std::sync::Arc::new(Self { + pending: async_std::sync::Mutex::new(Pending::new()), + cvar: async_std::sync::Condvar::new(), + }); + { + let this = std::sync::Arc::clone(&this); + async_std::task::spawn(async move { + while let Ok(event) = input.recv().await { + this.new_event(Some(event)).await; + } + this.new_event(None).await; + }); + } + this + } + + pub async fn recv(&self) -> Option<Event> { + let mut pending = self + .cvar + .wait_until(self.pending.lock().await, |pending| { + pending.has_event() + }) + .await; + pending.get_event() + } + + async fn new_event(&self, event: Option<Event>) { + let mut pending = self.pending.lock().await; + pending.new_event(&event); + self.cvar.notify_one(); + } +} + +#[derive(Default)] +struct Pending { + key: std::collections::VecDeque<textmode::Key>, + size: Option<(u16, u16)>, + process_output: bool, + process_alternate_screen: bool, + process_exit: bool, + clock_timer: bool, + done: bool, +} + +impl Pending { + fn new() -> Self { + Self::default() + } + + fn has_event(&self) -> bool { + self.done + || !self.key.is_empty() + || self.size.is_some() + || self.process_output + || self.process_alternate_screen + || self.process_exit + || self.clock_timer + } + + fn get_event(&mut self) -> Option<Event> { + if self.done { + return None; + } + if let Some(key) = self.key.pop_front() { + return Some(Event::Key(key)); + } + if let Some(size) = self.size.take() { + return Some(Event::Resize(size)); + } + if self.process_exit { + self.process_exit = false; + return Some(Event::ProcessExit); + } + if self.process_alternate_screen { + self.process_alternate_screen = false; + return Some(Event::ProcessAlternateScreen); + } + if self.clock_timer { + self.clock_timer = false; + return Some(Event::ClockTimer); + } + // process_output should be last because it will often be the case + // that there is ~always new process output (cat on large files, yes, + // etc) and that shouldn't prevent other events from happening + if self.process_output { + self.process_output = false; + return Some(Event::ProcessOutput); + } + unreachable!() + } + + fn new_event(&mut self, event: &Option<Event>) { + match event { + Some(Event::Key(key)) => self.key.push_back(key.clone()), + Some(Event::Resize(size)) => self.size = Some(*size), + Some(Event::ProcessOutput) => self.process_output = true, + Some(Event::ProcessAlternateScreen) => { + self.process_alternate_screen = true; + } + Some(Event::ProcessExit) => self.process_exit = true, + Some(Event::ClockTimer) => self.clock_timer = true, + Some(Event::Quit) | None => self.done = true, + } + } +} |