summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/buffer.rs92
-rw-r--r--src/lib.rs1
-rw-r--r--src/main.rs47
3 files changed, 139 insertions, 1 deletions
diff --git a/src/buffer.rs b/src/buffer.rs
new file mode 100644
index 0000000..fa0feaa
--- /dev/null
+++ b/src/buffer.rs
@@ -0,0 +1,92 @@
+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<Cell>,
+ pub window_size: WindowSize,
+}
+
+impl<'a, W: Write> Buffer<'a, W> {
+ pub fn new(stdout: &'a mut W) -> std::io::Result<Self> {
+ 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(())
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..86b6256
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1 @@
+pub mod buffer;
diff --git a/src/main.rs b/src/main.rs
index e7a11a9..e8c9f23 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,3 +1,48 @@
+use std::time::Instant;
+
+use crossterm::{
+ event::{Event, KeyCode},
+ style::ContentStyle,
+};
+use tortuise::buffer::{Buffer, Cell};
+
fn main() {
- println!("Hello, world!");
+ let mut stdout = std::io::stdout().lock();
+ let mut buffer = Buffer::new(&mut stdout).unwrap();
+ let mut last_frame_start = Instant::now();
+ buffer
+ .render_loop(
+ |renderer, event| {
+ if let Event::Key(event) = event
+ && event.code == KeyCode::Esc
+ {
+ let _ = renderer.finish();
+ std::process::exit(0);
+ }
+ },
+ |renderer| {
+ renderer.cells.clear();
+ let end = Instant::now();
+ let frame_time = (end - last_frame_start).as_micros();
+ let digits = [
+ frame_time % 1_000_000 / 100_000,
+ frame_time % 100_000 / 10_000,
+ frame_time % 10_000 / 1_000,
+ frame_time % 1_000 / 100,
+ frame_time % 100 / 10,
+ frame_time % 10,
+ ];
+ for (x, digit) in digits.into_iter().enumerate() {
+ renderer.cells.push(Cell {
+ character: char::from_digit(digit as u32, 10).unwrap(),
+ x: x as u16,
+ y: 0,
+ z: 0,
+ style: ContentStyle::new(),
+ });
+ }
+ last_frame_start = Instant::now();
+ },
+ )
+ .unwrap();
}