use crate::{backend, event::Event, theme, Cursive, Vec2}; use std::borrow::{Borrow, BorrowMut}; use std::time::Duration; // How long we wait between two empty input polls const INPUT_POLL_DELAY_MS: u64 = 30; /// Event loop runner for a cursive instance. /// /// You can get one from `Cursive::runner`, then either call `.run()`, or /// manually `.step()`. pub struct CursiveRunner { siv: C, backend: Box, boring_frame_count: u32, // Last layer sizes of the stack view. // If it changed, clear the screen. last_sizes: Vec, } impl std::ops::Deref for CursiveRunner where C: Borrow, { type Target = Cursive; fn deref(&self) -> &Cursive { self.siv.borrow() } } impl std::ops::DerefMut for CursiveRunner where C: BorrowMut, { fn deref_mut(&mut self) -> &mut Cursive { self.siv.borrow_mut() } } impl CursiveRunner { pub(crate) fn new(siv: C, backend: Box) -> Self { CursiveRunner { siv, backend, boring_frame_count: 0, last_sizes: Vec::new(), } } /// Returns the size of the screen, in characters. fn screen_size(&self) -> Vec2 { self.backend.screen_size() } /// Clean out the terminal and get back the wrapped object. pub fn into_inner(self) -> C { self.siv } } impl CursiveRunner where C: BorrowMut, { fn layout(&mut self) { let size = self.screen_size(); self.siv.borrow_mut().layout(size); } fn draw(&mut self) { let sizes = self.screen().layer_sizes(); if self.last_sizes != sizes { // TODO: Maybe we only need to clear if the _max_ size differs? // Or if the positions change? self.clear(); self.last_sizes = sizes; } if self.needs_clear { self.backend.clear( self.current_theme().palette[theme::PaletteColor::Background], ); self.needs_clear = false; } let size = self.screen_size(); self.siv.borrow_mut().draw(size, &*self.backend); } /// Performs the first half of `Self::step()`. /// /// This is an advanced method for fine-tuned manual stepping; /// you probably want [`run`][1] or [`step`][2]. /// /// This processes any pending event or callback. After calling this, /// you will want to call [`post_events`][3] with the result from this /// function. /// /// Returns `true` if an event or callback was received, /// and `false` otherwise. /// /// [1]: CursiveRun::run() /// [2]: CursiveRun::step() /// [3]: CursiveRun::post_events() pub fn process_events(&mut self) -> bool { // Things are boring if nothing significant happened. let mut boring = true; // First, handle all available input while let Some(event) = self.backend.poll_event() { boring = false; self.on_event(event); if !self.is_running() { return true; } } // Then, handle any available callback while self.process_callback() { boring = false; if !self.is_running() { return true; } } !boring } /// Performs the second half of `Self::step()`. /// /// This is an advanced method for fine-tuned manual stepping; /// you probably want [`run`][1] or [`step`][2]. /// /// You should call this after [`process_events`][3]. /// /// [1]: Cursive::run() /// [2]: Cursive::step() /// [3]: Cursive::process_events() pub fn post_events(&mut self, received_something: bool) { let boring = !received_something; // How many times should we try if it's still boring? // Total duration will be INPUT_POLL_DELAY_MS * repeats // So effectively fps = 1000 / INPUT_POLL_DELAY_MS / repeats if !boring || self .fps() .map(|fps| 1000 / INPUT_POLL_DELAY_MS as u32 / fps.get()) .map(|repeats| self.boring_frame_count >= repeats) .unwrap_or(false) { // We deserve to draw something! if boring { // We're only here because of a timeout. self.on_event(Event::Refresh); } self.refresh(); } if boring { std::thread::sleep(Duration::from_millis(INPUT_POLL_DELAY_MS)); self.boring_frame_count += 1; } } /// Refresh the screen with the current view tree state. pub fn refresh(&mut self) { self.boring_frame_count = 0; // Do we need to redraw everytime? // Probably, actually. // TODO: Do we need to re-layout everytime? self.layout(); // TODO: Do we need to redraw every view every time? // (Is this getting repetitive? :p) self.draw(); self.backend.refresh(); } /// Return the name of the backend used. /// /// Mostly used for debugging. pub fn backend_name(&self) -> &str { self.backend.name() } /// Performs a single step from the event loop. /// /// Useful if you need tighter control on the event loop. /// Otherwise, [`run(&mut self)`] might be more convenient. /// /// Returns `true` if an input event or callback was received /// during this step, and `false` otherwise. /// /// [`run(&mut self)`]: #method.run pub fn step(&mut self) -> bool { let received_something = self.process_events(); self.post_events(received_something); received_something } /// Runs the event loop. /// /// It will wait for user input (key presses) /// and trigger callbacks accordingly. /// /// Internally, it calls [`step(&mut self)`] until [`quit(&mut self)`] is /// called. /// /// After this function returns, you can call it again and it will start a /// new loop. /// /// [`step(&mut self)`]: #method.step /// [`quit(&mut self)`]: #method.quit pub fn run(&mut self) { self.refresh(); // And the big event loop begins! while self.is_running() { self.step(); } } }