use std::io::{self, Stdout, stdout};
use bevy::{app::AppExit, prelude::*};
use crossterm::{
ExecutableCommand, cursor,
terminal::{EnterAlternateScreen, LeaveAlternateScreen, disable_raw_mode, enable_raw_mode},
};
use ratatui::backend::CrosstermBackend;
use crate::{kitty::KittyEnabled, mouse::MouseCaptureEnabled};
pub struct TerminalPlugin;
impl Plugin for TerminalPlugin {
fn build(&self, app: &mut App) {
app.add_systems(Startup, setup)
.add_systems(PostUpdate, cleanup_system);
}
}
pub fn setup(mut commands: Commands) -> Result {
let terminal = RatatuiContext::init()?;
commands.insert_resource(terminal);
Ok(())
}
pub fn cleanup_system(mut commands: Commands, mut exit_reader: EventReader<AppExit>) {
for _ in exit_reader.read() {
commands.remove_resource::<KittyEnabled>();
commands.remove_resource::<MouseCaptureEnabled>();
commands.remove_resource::<RatatuiContext>();
}
}
#[derive(Resource, Deref, DerefMut)]
pub struct RatatuiContext(ratatui::Terminal<CrosstermBackend<Stdout>>);
impl RatatuiContext {
pub fn init() -> io::Result<Self> {
stdout().execute(EnterAlternateScreen)?;
enable_raw_mode()?;
let backend = CrosstermBackend::new(stdout());
let terminal = ratatui::Terminal::new(backend)?;
Ok(RatatuiContext(terminal))
}
pub fn restore() -> io::Result<()> {
stdout()
.execute(LeaveAlternateScreen)?
.execute(cursor::Show)?;
disable_raw_mode()?;
Ok(())
}
}
impl Drop for RatatuiContext {
fn drop(&mut self) {
if let Err(err) = RatatuiContext::restore() {
eprintln!("Failed to restore terminal: {}", err);
}
}
}