aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2021-03-06 16:47:16 -0500
committerJesse Luehrs <doy@tozt.net>2021-03-06 16:47:16 -0500
commite358c58826b1732763970848c0c6d77f12ef5e5a (patch)
tree7f81561870b340157af00d902eaceab1e43e0d4e
parent0a85d9961543ef9709d0e070aa51461c8b4df34b (diff)
downloadtextmode-e358c58826b1732763970848c0c6d77f12ef5e5a.tar.gz
textmode-e358c58826b1732763970848c0c6d77f12ef5e5a.zip
basic sketch of an implementation
-rw-r--r--.rustfmt.toml2
-rw-r--r--Cargo.toml5
-rw-r--r--examples/basic.rs19
-rw-r--r--src/color.rs16
-rw-r--r--src/lib.rs143
5 files changed, 178 insertions, 7 deletions
diff --git a/.rustfmt.toml b/.rustfmt.toml
new file mode 100644
index 0000000..55b0b14
--- /dev/null
+++ b/.rustfmt.toml
@@ -0,0 +1,2 @@
+edition = "2018"
+max_width = 78
diff --git a/Cargo.toml b/Cargo.toml
index a6962d2..1007f0d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,6 +4,7 @@ version = "0.1.0"
authors = ["Jesse Luehrs <doy@tozt.net>"]
edition = "2018"
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
[dependencies]
+itoa = "0.4"
+terminal_size = "0.1"
+vt100 = "0.9"
diff --git a/examples/basic.rs b/examples/basic.rs
new file mode 100644
index 0000000..1026834
--- /dev/null
+++ b/examples/basic.rs
@@ -0,0 +1,19 @@
+fn main() {
+ let mut tm = textmode::Textmode::new().unwrap();
+
+ tm.move_to(5, 5);
+ tm.write_str("foo");
+ std::thread::sleep(std::time::Duration::from_secs(2));
+ tm.refresh().unwrap();
+ std::thread::sleep(std::time::Duration::from_secs(2));
+
+ tm.move_to(8, 8);
+ tm.set_fgcolor(textmode::color::GREEN);
+ tm.write_str("bar");
+ tm.move_to(11, 11);
+ tm.set_fgcolor(vt100::Color::Default);
+ tm.write_str("baz");
+ std::thread::sleep(std::time::Duration::from_secs(2));
+ tm.refresh().unwrap();
+ std::thread::sleep(std::time::Duration::from_secs(2));
+}
diff --git a/src/color.rs b/src/color.rs
new file mode 100644
index 0000000..c2ba2b3
--- /dev/null
+++ b/src/color.rs
@@ -0,0 +1,16 @@
+pub const BLACK: vt100::Color = vt100::Color::Idx(0);
+pub const RED: vt100::Color = vt100::Color::Idx(1);
+pub const GREEN: vt100::Color = vt100::Color::Idx(2);
+pub const YELLOW: vt100::Color = vt100::Color::Idx(3);
+pub const BLUE: vt100::Color = vt100::Color::Idx(4);
+pub const MAGENTA: vt100::Color = vt100::Color::Idx(5);
+pub const CYAN: vt100::Color = vt100::Color::Idx(6);
+pub const LIGHTGREY: vt100::Color = vt100::Color::Idx(7);
+pub const DARKGREY: vt100::Color = vt100::Color::Idx(8);
+pub const LIGHTRED: vt100::Color = vt100::Color::Idx(9);
+pub const LIGHTGREEN: vt100::Color = vt100::Color::Idx(10);
+pub const LIGHTYELLOW: vt100::Color = vt100::Color::Idx(11);
+pub const LIGHTBLUE: vt100::Color = vt100::Color::Idx(12);
+pub const LIGHTMAGENTA: vt100::Color = vt100::Color::Idx(13);
+pub const LIGHTCYAN: vt100::Color = vt100::Color::Idx(14);
+pub const WHITE: vt100::Color = vt100::Color::Idx(15);
diff --git a/src/lib.rs b/src/lib.rs
index 31e1bb2..39c201b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,7 +1,140 @@
-#[cfg(test)]
-mod tests {
- #[test]
- fn it_works() {
- assert_eq!(2 + 2, 4);
+use std::io::Write as _;
+
+pub mod color;
+
+pub struct Textmode {
+ cur: vt100::Parser,
+ next: vt100::Parser,
+}
+
+impl Textmode {
+ pub fn new() -> std::io::Result<Self> {
+ 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 cursor_position(&self) -> (u16, u16) {
+ self.next.screen().cursor_position()
+ }
+
+ pub fn write(&mut self, buf: &[u8]) {
+ self.next.process(buf);
+ }
+
+ 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(())
+ }
+
+ pub fn set_size(&mut self, rows: u16, cols: u16) {
+ self.cur.set_size(rows, cols);
+ self.next.set_size(rows, cols);
+ }
+
+ pub fn write_str(&mut self, text: &str) {
+ self.write(text.as_bytes());
+ }
+
+ pub fn move_to(&mut self, row: u16, col: u16) {
+ self.write(b"\x1b[");
+ self.write_u16(row);
+ self.write(b";");
+ self.write_u16(col);
+ self.write(b"H");
+ }
+
+ pub fn clear(&mut self) {
+ self.write(b"\x1b[2J");
+ }
+
+ pub fn set_fgcolor(&mut self, color: vt100::Color) {
+ match color {
+ vt100::Color::Default => {
+ self.write(b"\x1b[39m");
+ }
+ vt100::Color::Idx(i) => {
+ if i < 8 {
+ self.write(b"\x1b[");
+ self.write_u8(30 + i);
+ self.write(b"m");
+ } else {
+ self.write(b"\x1b[38;5;");
+ self.write_u8(i);
+ self.write(b"m");
+ }
+ }
+ vt100::Color::Rgb(r, g, b) => {
+ self.write(b"\x1b[38;2;");
+ self.write_u8(r);
+ self.write(b";");
+ self.write_u8(g);
+ self.write(b";");
+ self.write_u8(b);
+ self.write(b"m");
+ }
+ }
+ }
+
+ pub fn set_bgcolor(&mut self, color: vt100::Color) {
+ match color {
+ vt100::Color::Default => {
+ self.write(b"\x1b[49m");
+ }
+ vt100::Color::Idx(i) => {
+ if i < 8 {
+ self.write(b"\x1b[");
+ self.write_u8(40 + i);
+ self.write(b"m");
+ } else {
+ self.write(b"\x1b[48;5;");
+ self.write_u8(i);
+ self.write(b"m");
+ }
+ }
+ vt100::Color::Rgb(r, g, b) => {
+ self.write(b"\x1b[48;2;");
+ self.write_u8(r);
+ self.write(b";");
+ self.write_u8(g);
+ self.write(b";");
+ self.write_u8(b);
+ self.write(b"m");
+ }
+ }
+ }
+
+ 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");
}
}