diff --git a/examples/simple.rs b/examples/simple.rs index 61545a2..f7f893f 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -1,6 +1,6 @@ extern crate libterm; -use libterm::{TermControl, raw_mode}; +use libterm::{TermControl, raw_mode, Color}; use std::io::{Read, Write, stdout, stdin}; fn main() { @@ -21,7 +21,7 @@ fn main() { match b { b'q' => return, b'c' => stdout.clear(), - b'r' => stdout.rendition(91), + b'r' => stdout.color(Color::Rgb(5, 0, 0)), a => stdout.write(&[a]), }.unwrap(); diff --git a/src/color.rs b/src/color.rs new file mode 100644 index 0000000..34aa0ef --- /dev/null +++ b/src/color.rs @@ -0,0 +1,84 @@ +/// A terminal color. +#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] +pub enum Color { + /// Black. + Black, + /// Red. + Red, + /// Green. + Green, + /// Yellow. + Yellow, + /// Blue. + Blue, + /// Megenta. + Magenta, + /// Cyan. + Cyan, + /// White. + White, + /// High-intensity black. + HiBlack, + /// High-intensity red. + HiRed, + /// High-intensity green. + HiGreen, + /// High-intensity yellow. + HiYellow, + /// High-intensity blue. + HiBlue, + /// High-intensity megenta. + HiMagenta, + /// High-intensity cyan. + HiCyan, + /// High-intensity white. + HiWhite, + /// 216-color (6, 6, 6) RGB. + Rgb(u8, u8, u8), + /// Grayscale (max value: 24) + Grayscale(u8), +} + +use Color::*; + +impl Color { + pub fn to_ansi_val(self) -> u8 { + self.debug_check(); + + match self { + Black => 0x0, + Red => 0x1, + Green => 0x2, + Yellow => 0x3, + Blue => 0x4, + Magenta => 0x5, + Cyan => 0x6, + White => 0x7, + HiBlack => 0x8, + HiRed => 0x9, + HiGreen => 0xA, + HiYellow => 0xB, + HiBlue => 0xC, + HiMagenta => 0xD, + HiCyan => 0xE, + HiWhite => 0xF, + Rgb(r, g, b) => 16 + 36 * r + 6 * g + b, + Grayscale(shade) => 0xE8 + shade, + } + } + + pub fn debug_check(self) { + match self { + Rgb(r, g, b) => { + debug_assert!(r <= 5, "Red color fragment (r = {}) is out of bound. Make sure r ≤ 5.", r); + debug_assert!(g <= 5, "Green color fragment (g = {}) is out of bound. Make sure g ≤ 5.", g); + debug_assert!(b <= 5, "Blue color fragment (b = {}) is out of bound. Make sure b ≤ 5.", b); + }, + Grayscale(shade) => { + // Unfortunately, there are a little less than fifty shades. + debug_assert!(shade < 24, "Grayscale out of bound (shade = {}). There are only 24 shades of gray.", shade); + }, + _ => {}, + } + } +} diff --git a/src/control.rs b/src/control.rs index 88e1fe1..246e32d 100644 --- a/src/control.rs +++ b/src/control.rs @@ -1,4 +1,5 @@ use std::io::{Write, Result as IoResult}; +use Color; /// Controlling terminals. pub trait TermControl { @@ -27,9 +28,9 @@ pub trait TermControl { /// Go to a given position. fn goto(&mut self, x: u16, y: u16) -> IoResult { self.csi(&[ - (x / 10000) as u8 + b'0', ((x / 1000) % 10) as u8 + b'0', ((x / 100) % 10) as u8 + b'0', ((x / 10) % 10) as u8 + b'0', (x % 10) as u8 + b'0', + (x / 10000) as u8 + b'0', (x / 1000) as u8 % 10 + b'0', (x / 100) as u8 % 10 + b'0', (x / 10) as u8 % 10 + b'0', x as u8 % 10 + b'0', b';', - (y / 10000) as u8 + b'0', ((y / 1000) % 10) as u8 + b'0', ((y / 100) % 10) as u8 + b'0', ((y / 10) % 10) as u8 + b'0', (y % 10) as u8 + b'0', + (y / 10000) as u8 + b'0', (y / 1000) as u8 % 10 + b'0', (y / 100) as u8 % 10 + b'0', (y / 10) as u8 % 10 + b'0', y as u8 % 10 + b'0', b'H', ]) } @@ -40,6 +41,36 @@ pub trait TermControl { b'm', ]) } + /// Set foreground color + fn color(&mut self, color: Color) -> IoResult { + let ansi = color.to_ansi_val(); + self.csi(&[ + b'3', + b'8', + b';', + b'5', + b';', + b'0' + ansi / 100, + b'0' + ansi / 10 % 10, + b'0' + ansi % 10, + b'm', + ]) + } + /// Set background color + fn bg_color(&mut self, color: Color) -> IoResult { + let ansi = color.to_ansi_val(); + self.csi(&[ + b'4', + b'8', + b';', + b'5', + b';', + b'0' + ansi / 100, + b'0' + ansi / 10 % 10, + b'0' + ansi % 10, + b'm', + ]) + } } impl TermControl for W { diff --git a/src/lib.rs b/src/lib.rs index bce0482..c4bf259 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,6 @@ #![feature(libc)] +#[warn(missing_docs)] + extern crate libc; mod termios; @@ -11,3 +13,6 @@ pub use raw::{raw_mode, TerminalRestorer}; mod size; pub use size::termsize; + +mod color; +pub use color::Color;