#[cfg(not(feature = "web"))]
mod tui {
use crate::tui::TuiResult;
use crossterm::{
execute,
terminal::{
EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode,
},
};
use ratatui::{Terminal, backend::CrosstermBackend};
use std::io::{self, Stdout};
pub type Tui = Terminal<CrosstermBackend<Stdout>>;
fn init() -> TuiResult<Tui> {
execute!(io::stdout(), EnterAlternateScreen)?;
enable_raw_mode()?;
let backend = CrosstermBackend::new(io::stdout());
let mut terminal = Terminal::new(backend)?;
terminal.hide_cursor()?;
terminal.clear()?;
let original_hook = std::panic::take_hook();
std::panic::set_hook(Box::new(move |panic_info| {
let _ = restore();
original_hook(panic_info);
}));
Ok(terminal)
}
pub fn restore() -> TuiResult<()> {
let mut first_err: Option<std::io::Error> = None;
if let Err(e) = disable_raw_mode() {
tracing::error!("Failed to disable raw mode: {e:?}");
first_err = Some(e);
}
if let Err(e) = execute!(io::stdout(), LeaveAlternateScreen) {
tracing::error!("Failed to leave alternate screen: {e:?}");
if first_err.is_none() {
first_err = Some(e);
}
}
if let Some(e) = first_err {
return Err(e.into());
}
Ok(())
}
pub struct TuiWrapper {
pub terminal: Tui,
}
impl TuiWrapper {
pub fn new() -> TuiResult<Self> {
Ok(Self { terminal: init()? })
}
}
impl Drop for TuiWrapper {
fn drop(&mut self) {
let _ = restore();
}
}
}
#[cfg(feature = "web")]
mod web {
use crate::tui::TuiResult;
use ratatui::Terminal;
use ratzilla::backend::webgl2::WebGl2Backend;
pub type WebTui = Terminal<WebGl2Backend>;
pub struct TuiWrapper {
pub terminal: WebTui,
}
impl TuiWrapper {
pub fn new() -> TuiResult<Self> {
let backend = WebGl2Backend::new().map_err(|e| {
crate::tui::error::TuiError::IO(std::io::Error::other(e.to_string()))
})?;
Ok(Self {
terminal: Terminal::new(backend)?,
})
}
}
}
#[cfg(not(feature = "web"))]
pub use tui::TuiWrapper;
#[cfg(feature = "web")]
pub use web::TuiWrapper;