mirror of
https://codeberg.org/unspeaker/tengri.git
synced 2025-12-06 11:46:42 +01:00
tabula rasa
This commit is contained in:
commit
47b3413d7d
76 changed files with 7000 additions and 0 deletions
82
tui/src/tui_engine.rs
Normal file
82
tui/src/tui_engine.rs
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
use crate::*;
|
||||
use std::time::Duration;
|
||||
pub struct Tui {
|
||||
pub exited: Arc<AtomicBool>,
|
||||
pub backend: CrosstermBackend<Stdout>,
|
||||
pub buffer: Buffer,
|
||||
pub area: [u16;4],
|
||||
pub perf: PerfModel,
|
||||
}
|
||||
impl Tui {
|
||||
/// Construct a new TUI engine and wrap it for shared ownership.
|
||||
pub fn new () -> Usually<Arc<RwLock<Self>>> {
|
||||
let backend = CrosstermBackend::new(stdout());
|
||||
let Size { width, height } = backend.size()?;
|
||||
Ok(Arc::new(RwLock::new(Self {
|
||||
exited: Arc::new(AtomicBool::new(false)),
|
||||
buffer: Buffer::empty(Rect { x: 0, y: 0, width, height }),
|
||||
area: [0, 0, width, height],
|
||||
backend,
|
||||
perf: Default::default(),
|
||||
})))
|
||||
}
|
||||
/// True if done
|
||||
pub fn exited (&self) -> bool {
|
||||
self.exited.fetch_and(true, Relaxed)
|
||||
}
|
||||
/// Prepare before run
|
||||
pub fn setup (&mut self) -> Usually<()> {
|
||||
let better_panic_handler = Settings::auto().verbosity(Verbosity::Full).create_panic_handler();
|
||||
std::panic::set_hook(Box::new(move |info: &std::panic::PanicHookInfo|{
|
||||
stdout().execute(LeaveAlternateScreen).unwrap();
|
||||
CrosstermBackend::new(stdout()).show_cursor().unwrap();
|
||||
disable_raw_mode().unwrap();
|
||||
better_panic_handler(info);
|
||||
}));
|
||||
stdout().execute(EnterAlternateScreen)?;
|
||||
self.backend.hide_cursor()?;
|
||||
enable_raw_mode().map_err(Into::into)
|
||||
}
|
||||
/// Update the display buffer.
|
||||
pub fn flip (&mut self, mut buffer: Buffer, size: ratatui::prelude::Rect) -> Buffer {
|
||||
if self.buffer.area != size {
|
||||
self.backend.clear_region(ClearType::All).unwrap();
|
||||
self.buffer.resize(size);
|
||||
self.buffer.reset();
|
||||
}
|
||||
let updates = self.buffer.diff(&buffer);
|
||||
self.backend.draw(updates.into_iter()).expect("failed to render");
|
||||
self.backend.flush().expect("failed to flush output buffer");
|
||||
std::mem::swap(&mut self.buffer, &mut buffer);
|
||||
buffer.reset();
|
||||
buffer
|
||||
}
|
||||
/// Clean up after run
|
||||
pub fn teardown (&mut self) -> Usually<()> {
|
||||
stdout().execute(LeaveAlternateScreen)?;
|
||||
self.backend.show_cursor()?;
|
||||
disable_raw_mode().map_err(Into::into)
|
||||
}
|
||||
}
|
||||
pub trait TuiRun<R: Render<TuiOut> + Handle<TuiIn> + 'static> {
|
||||
/// Run an app in the main loop.
|
||||
fn run (&self, state: &Arc<RwLock<R>>) -> Usually<()>;
|
||||
}
|
||||
impl<T: Render<TuiOut> + Handle<TuiIn> + 'static> TuiRun<T> for Arc<RwLock<Tui>> {
|
||||
fn run (&self, state: &Arc<RwLock<T>>) -> Usually<()> {
|
||||
let _input_thread = TuiIn::run_input(self, state, Duration::from_millis(100));
|
||||
self.write().unwrap().setup()?;
|
||||
let render_thread = TuiOut::run_output(self, state, Duration::from_millis(10));
|
||||
match render_thread.join() {
|
||||
Ok(result) => {
|
||||
self.write().unwrap().teardown()?;
|
||||
println!("\n\rRan successfully: {result:?}\n\r");
|
||||
},
|
||||
Err(error) => {
|
||||
self.write().unwrap().teardown()?;
|
||||
panic!("\n\rRender thread failed: {error:?}.\n\r")
|
||||
},
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue