tek/engine/src/tui/tui_run.rs

75 lines
3.1 KiB
Rust

use crate::{*, tui::*};
use ratatui::prelude::Size;
use std::time::Duration;
use std::thread::{spawn, JoinHandle};
pub trait TuiRun<R: Render<Tui> + Handle<Tui> + Sized + 'static> {
/// Run an app in the main loop.
fn run (&self, state: &Arc<RwLock<R>>) -> Usually<()>;
/// Spawn the input thread.
fn run_input (&self, state: &Arc<RwLock<R>>, poll: Duration) -> JoinHandle<()>;
/// Spawn the output thread.
fn run_output (&self, state: &Arc<RwLock<R>>, sleep: Duration) -> JoinHandle<()>;
}
impl<T: Render<Tui> + Handle<Tui> + Sized + 'static> TuiRun<T> for Arc<RwLock<Tui>> {
fn run (&self, state: &Arc<RwLock<T>>) -> 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<RwLock<T>>, 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<RwLock<T>>, 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);
})
}
}