From a4bca446049fd488af29dd5ec26f953845e8e4fe Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Sat, 6 Mar 2021 18:45:50 -0500 Subject: split into sync and async implementations --- src/async.rs | 67 ++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 91 +++++++++++++++++++++--------------------------------------- src/sync.rs | 65 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 163 insertions(+), 60 deletions(-) create mode 100644 src/async.rs create mode 100644 src/sync.rs (limited to 'src') diff --git a/src/async.rs b/src/async.rs new file mode 100644 index 0000000..a384da8 --- /dev/null +++ b/src/async.rs @@ -0,0 +1,67 @@ +use futures_lite::io::AsyncWriteExt as _; + +use super::private::TextmodeImpl as _; + +pub struct Textmode { + cur: vt100::Parser, + next: vt100::Parser, +} + +impl super::private::TextmodeImpl for Textmode { + fn cur(&self) -> &vt100::Parser { + &self.cur + } + + fn cur_mut(&mut self) -> &mut vt100::Parser { + &mut self.cur + } + + fn next(&self) -> &vt100::Parser { + &self.next + } + + fn next_mut(&mut self) -> &mut vt100::Parser { + &mut self.next + } +} + +impl super::Textmode for Textmode {} + +impl Textmode { + pub async fn new() -> std::io::Result { + let (rows, cols) = match terminal_size::terminal_size() { + Some((terminal_size::Width(w), terminal_size::Height(h))) => { + (h, w) + } + _ => (24, 80), + }; + let cur = vt100::Parser::new(rows, cols, 0); + let next = vt100::Parser::new(rows, cols, 0); + + let self_ = Self { cur, next }; + self_ + .write_stdout(b"\x1b7\x1b[?47h\x1b[2J\x1b[H\x1b[?25h") + .await?; + Ok(self_) + } + + // TODO: without async drop or async closures, i'm not sure how to do + // better than this + pub async fn cleanup(&mut self) -> std::io::Result<()> { + self.write_stdout(b"\x1b[?47l\x1b8\x1b[?25h").await + } + + pub async fn refresh(&mut self) -> std::io::Result<()> { + let diff = self.next().screen().contents_diff(self.cur().screen()); + self.write_stdout(&diff).await?; + self.cur_mut().process(&diff); + Ok(()) + } + + async fn write_stdout(&self, buf: &[u8]) -> std::io::Result<()> { + let mut stdout = blocking::Unblock::new(std::io::stdout()); + stdout.write_all(buf).await?; + stdout.flush().await?; + Ok(()) + } +} diff --git a/src/lib.rs b/src/lib.rs index 39c201b..fcee1e9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,53 +1,47 @@ -use std::io::Write as _; - pub mod color; -pub struct Textmode { - cur: vt100::Parser, - next: vt100::Parser, -} +#[cfg(feature = "async")] +pub mod r#async; +pub mod sync; -impl Textmode { - pub fn new() -> std::io::Result { - let (rows, cols) = match terminal_size::terminal_size() { - Some((terminal_size::Width(w), terminal_size::Height(h))) => { - (h, w) - } - _ => (24, 80), - }; - let cur = vt100::Parser::new(rows, cols, 0); - let next = vt100::Parser::new(rows, cols, 0); +mod private { + pub trait TextmodeImpl { + fn cur(&self) -> &vt100::Parser; + fn cur_mut(&mut self) -> &mut vt100::Parser; + fn next(&self) -> &vt100::Parser; + fn next_mut(&mut self) -> &mut vt100::Parser; - let self_ = Self { cur, next }; - self_.write_stdout(b"\x1b7\x1b[?47h\x1b[2J\x1b[H\x1b[?25h")?; - Ok(self_) - } + fn write_u16(&mut self, i: u16) { + // unwrap is fine because vt100::Parser::write can never fail + itoa::write(self.next_mut(), i).unwrap(); + } - pub fn cursor_position(&self) -> (u16, u16) { - self.next.screen().cursor_position() + fn write_u8(&mut self, i: u8) { + // unwrap is fine because vt100::Parser::write can never fail + itoa::write(self.next_mut(), i).unwrap(); + } } +} - pub fn write(&mut self, buf: &[u8]) { - self.next.process(buf); +pub trait Textmode: private::TextmodeImpl { + fn cursor_position(&self) -> (u16, u16) { + self.next().screen().cursor_position() } - pub fn refresh(&mut self) -> std::io::Result<()> { - let diff = self.next.screen().contents_diff(self.cur.screen()); - self.write_stdout(&diff)?; - self.cur.process(&diff); - Ok(()) + fn write(&mut self, buf: &[u8]) { + self.next_mut().process(buf); } - pub fn set_size(&mut self, rows: u16, cols: u16) { - self.cur.set_size(rows, cols); - self.next.set_size(rows, cols); + fn set_size(&mut self, rows: u16, cols: u16) { + self.cur_mut().set_size(rows, cols); + self.next_mut().set_size(rows, cols); } - pub fn write_str(&mut self, text: &str) { + fn write_str(&mut self, text: &str) { self.write(text.as_bytes()); } - pub fn move_to(&mut self, row: u16, col: u16) { + fn move_to(&mut self, row: u16, col: u16) { self.write(b"\x1b["); self.write_u16(row); self.write(b";"); @@ -55,11 +49,11 @@ impl Textmode { self.write(b"H"); } - pub fn clear(&mut self) { + fn clear(&mut self) { self.write(b"\x1b[2J"); } - pub fn set_fgcolor(&mut self, color: vt100::Color) { + fn set_fgcolor(&mut self, color: vt100::Color) { match color { vt100::Color::Default => { self.write(b"\x1b[39m"); @@ -87,7 +81,7 @@ impl Textmode { } } - pub fn set_bgcolor(&mut self, color: vt100::Color) { + fn set_bgcolor(&mut self, color: vt100::Color) { match color { vt100::Color::Default => { self.write(b"\x1b[49m"); @@ -114,27 +108,4 @@ impl Textmode { } } } - - fn write_u16(&mut self, i: u16) { - // unwrap is fine because vt100::Parser::write can never fail - itoa::write(&mut self.next, i).unwrap(); - } - - fn write_u8(&mut self, i: u8) { - // unwrap is fine because vt100::Parser::write can never fail - itoa::write(&mut self.next, i).unwrap(); - } - - fn write_stdout(&self, buf: &[u8]) -> std::io::Result<()> { - let mut stdout = std::io::stdout(); - stdout.write_all(buf)?; - stdout.flush()?; - Ok(()) - } -} - -impl Drop for Textmode { - fn drop(&mut self) { - let _ = self.write_stdout(b"\x1b[?47l\x1b8\x1b[?25h"); - } } diff --git a/src/sync.rs b/src/sync.rs new file mode 100644 index 0000000..047036d --- /dev/null +++ b/src/sync.rs @@ -0,0 +1,65 @@ +use std::io::Write as _; + +use super::private::TextmodeImpl as _; + +pub struct Textmode { + cur: vt100::Parser, + next: vt100::Parser, +} + +impl super::private::TextmodeImpl for Textmode { + fn cur(&self) -> &vt100::Parser { + &self.cur + } + + fn cur_mut(&mut self) -> &mut vt100::Parser { + &mut self.cur + } + + fn next(&self) -> &vt100::Parser { + &self.next + } + + fn next_mut(&mut self) -> &mut vt100::Parser { + &mut self.next + } +} + +impl super::Textmode for Textmode {} + +impl Textmode { + pub fn new() -> std::io::Result { + let (rows, cols) = match terminal_size::terminal_size() { + Some((terminal_size::Width(w), terminal_size::Height(h))) => { + (h, w) + } + _ => (24, 80), + }; + let cur = vt100::Parser::new(rows, cols, 0); + let next = vt100::Parser::new(rows, cols, 0); + + let self_ = Self { cur, next }; + self_.write_stdout(b"\x1b7\x1b[?47h\x1b[2J\x1b[H\x1b[?25h")?; + Ok(self_) + } + + pub fn refresh(&mut self) -> std::io::Result<()> { + let diff = self.next().screen().contents_diff(self.cur().screen()); + self.write_stdout(&diff)?; + self.cur_mut().process(&diff); + Ok(()) + } + + fn write_stdout(&self, buf: &[u8]) -> std::io::Result<()> { + let mut stdout = std::io::stdout(); + stdout.write_all(buf)?; + stdout.flush()?; + Ok(()) + } +} + +impl Drop for Textmode { + fn drop(&mut self) { + let _ = self.write_stdout(b"\x1b[?47l\x1b8\x1b[?25h"); + } +} -- cgit v1.2.3-54-g00ecf