use crate::{*, tui::*}; use ratatui::prelude::Size; use std::time::Duration; use std::thread::{spawn, JoinHandle}; pub trait TuiRun + Handle + Sized + 'static> { /// Run an app in the main loop. fn run (&self, state: &Arc>) -> Usually<()>; /// Spawn the input thread. fn run_input (&self, state: &Arc>, poll: Duration) -> JoinHandle<()>; /// Spawn the output thread. fn run_output (&self, state: &Arc>, sleep: Duration) -> JoinHandle<()>; } impl + Handle + Sized + 'static> TuiRun for Arc> { fn run (&self, state: &Arc>) -> Usually<()> { let _input_thread = self.run_input(state, Duration::from_millis(100)); self.write().unwrap().setup()?; let render_thread = self.run_output(state, Duration::from_millis(10)); render_thread.join().expect("main thread failed"); self.write().unwrap().teardown()?; Ok(()) } fn run_input (&self, state: &Arc>, poll: Duration) -> JoinHandle<()> { let exited = self.read().unwrap().exited.clone(); let state = state.clone(); spawn(move || loop { if exited.fetch_and(true, Relaxed) { break } if ::crossterm::event::poll(poll).is_ok() { let event = ::crossterm::event::read().unwrap(); match event { kpat!(Ctrl-KeyCode::Char('c')) => { exited.store(true, Relaxed); }, _ => { let exited = exited.clone(); if let Err(e) = state.write().unwrap().handle(&TuiIn(exited, event)) { panic!("{e}") } } } } }) } fn run_output (&self, state: &Arc>, sleep: Duration) -> JoinHandle<()> { let exited = self.read().unwrap().exited.clone(); let engine = self.clone(); let state = state.clone(); let Size { width, height } = engine.read().unwrap().backend.size().expect("get size failed"); let mut buffer = Buffer::empty(Rect { x: 0, y: 0, width, height }); spawn(move || loop { if exited.fetch_and(true, Relaxed) { break } let Size { width, height } = engine.read().unwrap().backend.size() .expect("get size failed"); if let Ok(state) = state.try_read() { let size = Rect { x: 0, y: 0, width, height }; if buffer.area != size { engine.write().unwrap().backend.clear_region(ClearType::All) .expect("clear failed"); buffer.resize(size); buffer.reset(); } let mut output = TuiOut { buffer, area: [0, 0, width, height] }; state.render(&mut output); buffer = engine.write().unwrap().flip(output.buffer, size); } std::thread::sleep(sleep); }) } }