use std::io::Write; use crossterm::{ cursor::{Hide, MoveTo, Show}, event::{self, Event}, execute, style::{ContentStyle, PrintStyledContent, StyledContent}, terminal::{ BeginSynchronizedUpdate, Clear, ClearType, EndSynchronizedUpdate, EnterAlternateScreen, LeaveAlternateScreen, WindowSize, disable_raw_mode, enable_raw_mode, window_size, }, }; #[derive(Debug, Clone, Copy, Default, Eq, PartialEq)] pub struct Cell { pub character: char, pub style: ContentStyle, pub x: u16, pub y: u16, pub z: u8, } #[derive(Debug)] pub struct Buffer<'a, W: Write> { stdout: &'a mut W, pub cells: Vec, pub window_size: WindowSize, } impl<'a, W: Write> Buffer<'a, W> { pub fn new(stdout: &'a mut W) -> std::io::Result { Ok(Self { stdout, cells: Vec::new(), window_size: window_size()?, }) } fn initialize(writer: &mut impl Write) -> std::io::Result<()> { enable_raw_mode()?; execute!(writer, Hide, EnterAlternateScreen)?; Ok(()) } fn render(&mut self) -> std::io::Result<()> { self.cells.sort_unstable_by_key(|cell| cell.z); execute!(self.stdout, BeginSynchronizedUpdate, Clear(ClearType::All))?; for cell in &self.cells { execute!( self.stdout, MoveTo(cell.x, cell.y), PrintStyledContent(StyledContent::new(cell.style, cell.character)) )?; } execute!(self.stdout, EndSynchronizedUpdate)?; Ok(()) } pub fn finish(&mut self) -> std::io::Result<()> { execute!(self.stdout, Show, LeaveAlternateScreen)?; disable_raw_mode()?; Ok(()) } pub fn render_loop( &mut self, mut event_handler: impl FnMut(&mut Self, Event), mut update: impl FnMut(&mut Self), ) -> std::io::Result<()> { Self::initialize(self.stdout)?; let (sender, receiver) = std::sync::mpsc::channel(); std::thread::scope(|scope| -> std::io::Result<()> { scope.spawn(|| { loop { let Ok(event) = event::read() else { continue; }; let _ = sender.send(event); } }); loop { while let Ok(event) = receiver.try_recv() { event_handler(self, event); } update(self); self.render()?; } })?; self.finish()?; Ok(()) } }