From 6f9addc42b7e6dcc6025094825c907fcbddb1fef Mon Sep 17 00:00:00 2001
From: Ticki <Ticki@users.noreply.github.com>
Date: Mon, 7 Mar 2016 17:39:25 +0100
Subject: [PATCH] Yay! 256-color mode

---
 examples/simple.rs |  4 +--
 src/color.rs       | 84 ++++++++++++++++++++++++++++++++++++++++++++++
 src/control.rs     | 35 +++++++++++++++++--
 src/lib.rs         |  5 +++
 4 files changed, 124 insertions(+), 4 deletions(-)
 create mode 100644 src/color.rs

diff --git a/examples/simple.rs b/examples/simple.rs
index 61545a25..f7f893f0 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 00000000..34aa0eff
--- /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 88e1fe19..246e32d5 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<usize> {
         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<usize> {
+        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<usize> {
+        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<W: Write> TermControl for W {
diff --git a/src/lib.rs b/src/lib.rs
index bce0482c..c4bf259c 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;
-- 
GitLab