diff --git a/Cargo.toml b/Cargo.toml
index 4f0f1190984dcf7c3b163e56bf0b9fe136b1ce93..f4b8df3cf774010e52c7e7106b6d330aa803f876 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "termion"
-version = "0.1.0"
+version = "1.0.0"
 authors = ["Ticki <Ticki@users.noreply.github.com>"]
 
 [target.'cfg(not(target_os = "redox"))'.dependencies]
diff --git a/README.md b/README.md
index 69560de7d307fb8826f9eae300d0e27af646dae4..a501bacb05d3cc4e6388cf9d47d1f1d56def28a1 100644
--- a/README.md
+++ b/README.md
@@ -37,6 +37,7 @@ default-features = false
 ## Features
 
 - Raw mode.
+- Truecolor.
 - 256-color mode.
 - Cursor movement.
 - Color output.
@@ -60,26 +61,18 @@ and much more.
 ```rust
 extern crate termion;
 
-use termion::{TermWrite, color, Style};
+use termion::{color, style};
 
 use std::io;
 
 fn main() {
-    let stdout = io::stdout();
-    let mut stdout = stdout.lock();
+    println!("{}Red", color::Fg(color::Red));
 
-    stdout.color(color::Red).unwrap();
-    println!("Red");
+    println!("{}Blue", color::Fg(color::Blue));
 
-    stdout.color(color::Blue).unwrap();
-    println!("Blue");
+    println!("{}Blue'n'Bold{}", style::Bold, style::Reset);
 
-    stdout.style(Style::Bold).unwrap();
-    println!("Blue'n'Bold");
-
-    stdout.reset().unwrap();
-    stdout.style(Style::Italic).unwrap();
-    println!("Just plain italic")
+    println!("{}Just plain italic", style::Italic);
 }
 ```
 
diff --git a/examples/async.rs b/examples/async.rs
index 4b2ec0a53bcebc0b66816bfb797e3bfef21b2752..5227e32f5be04805aa0ea82f1673fc3410bd4ccc 100644
--- a/examples/async.rs
+++ b/examples/async.rs
@@ -1,6 +1,7 @@
 extern crate termion;
 
-use termion::{TermWrite, IntoRawMode, async_stdin};
+use termion::raw::IntoRawMode;
+use termion::async_stdin;
 use std::io::{Read, Write, stdout, stdin};
 use std::thread;
 use std::time::Duration;
@@ -10,11 +11,10 @@ fn main() {
     let mut stdout = stdout.lock().into_raw_mode().unwrap();
     let mut stdin = async_stdin().bytes();
 
-    stdout.clear().unwrap();
-    stdout.goto(0, 0).unwrap();
+    write!(stdout, "{}{}", termion::clear::All, termion::cursor::Goto(1, 1)).unwrap();
 
     loop {
-        stdout.clear_line().unwrap();
+        write!(stdout, "{}", termion::clear::CurrentLine).unwrap();
 
         let b = stdin.next();
         write!(stdout, "\r{:?}    <- This demonstrates the async read input char. Between each update a 100 ms. is waited, simply to demonstrate the async fashion. \n\r", b).unwrap();
@@ -29,7 +29,7 @@ fn main() {
         stdout.flush().unwrap();
         thread::sleep(Duration::from_millis(50));
         stdout.write(b"\r #").unwrap();
-        stdout.goto(0, 0).unwrap();
+        write!(stdout, "{}", termion::cursor::Goto(1, 1)).unwrap();
         stdout.flush().unwrap();
     }
 }
diff --git a/examples/color.rs b/examples/color.rs
index 5f1f4801e5915a08b060275c276ab53396807e87..6cddc6fcb773b6de628344d14d3baee3f9faebfa 100644
--- a/examples/color.rs
+++ b/examples/color.rs
@@ -1,23 +1,15 @@
 extern crate termion;
 
-use termion::{TermWrite, color, Style};
+use termion::{color, style};
 
 use std::io;
 
 fn main() {
-    let stdout = io::stdout();
-    let mut stdout = stdout.lock();
+    println!("{}Red", color::Fg(color::Red));
 
-    stdout.color(color::Red).unwrap();
-    println!("Red");
+    println!("{}Blue", color::Fg(color::Blue));
 
-    stdout.color(color::Blue).unwrap();
-    println!("Blue");
+    println!("{}Blue'n'Bold{}", style::Bold, style::Reset);
 
-    stdout.style(Style::Bold).unwrap();
-    println!("Blue'n'Bold");
-
-    stdout.reset().unwrap();
-    stdout.style(Style::Italic).unwrap();
-    println!("Just plain italic")
+    println!("{}Just plain italic", style::Italic);
 }
diff --git a/examples/is_tty.rs b/examples/is_tty.rs
new file mode 100644
index 0000000000000000000000000000000000000000..7b64df6ae5b9127c47a1d5cc96330270081098ae
--- /dev/null
+++ b/examples/is_tty.rs
@@ -0,0 +1,11 @@
+extern crate termion;
+
+use std::fs;
+
+fn main() {
+    if termion::is_tty(fs::File::create("/dev/stdout").unwrap()) {
+        println!("This is a TTY!");
+    } else {
+        println!("This is not a TTY :(");
+    }
+}
diff --git a/examples/keys.rs b/examples/keys.rs
index 55d2f831d6680a8c6c042c773588240ae7629768..03ee9f7f4a01db3df652164d9a947c0ba9626741 100644
--- a/examples/keys.rs
+++ b/examples/keys.rs
@@ -1,42 +1,36 @@
 extern crate termion;
 
-#[cfg(feature = "nightly")]
-fn main() {
-    use termion::{TermRead, TermWrite, IntoRawMode, Key};
-    use std::io::{Write, stdout, stdin};
+use termion::input::{TermRead, Key};
+use termion::raw::IntoRawMode;
+use std::io::{Write, stdout, stdin};
 
+fn main() {
     let stdin = stdin();
     let mut stdout = stdout().into_raw_mode().unwrap();
 
-    stdout.clear().unwrap();
-    stdout.goto(0, 0).unwrap();
-    stdout.write(b"q to exit. Type stuff, use alt, and so on.").unwrap();
-    stdout.hide_cursor().unwrap();
-    stdout.flush().unwrap();
+    write!(stdout, "{}{}q to exit. Type stuff, use alt, and so on.{}",
+           termion::clear::All,
+           termion::cursor::Goto(1, 1),
+           termion::cursor::Hide).unwrap();
 
     for c in stdin.keys() {
-        stdout.goto(5, 5).unwrap();
-        stdout.clear_line().unwrap();
+        write!(stdout, "{}{}", termion::cursor::Goto(1, 1), termion::clear::CurrentLine).unwrap();
+
         match c.unwrap() {
             Key::Char('q') => break,
-            Key::Char(c) => println!("{}", c),
-            Key::Alt(c) => println!("^{}", c),
-            Key::Ctrl(c) => println!("*{}", c),
-            Key::Left => println!("←"),
-            Key::Right => println!("→"),
-            Key::Up => println!("↑"),
-            Key::Down => println!("↓"),
-            Key::Backspace => println!("×"),
-            Key::Invalid => println!("???"),
+            Key::Char(c) => writeln!(stdout, "{}", c).unwrap(),
+            Key::Alt(c) => writeln!(stdout, "^{}", c).unwrap(),
+            Key::Ctrl(c) => writeln!(stdout, "*{}", c).unwrap(),
+            Key::Left => writeln!(stdout, "←").unwrap(),
+            Key::Right => writeln!(stdout, "→").unwrap(),
+            Key::Up => writeln!(stdout, "↑").unwrap(),
+            Key::Down => writeln!(stdout, "↓").unwrap(),
+            Key::Backspace => writeln!(stdout, "×").unwrap(),
+            Key::Invalid => writeln!(stdout, "???").unwrap(),
             _ => {},
         }
         stdout.flush().unwrap();
     }
 
-    stdout.show_cursor().unwrap();
-}
-
-#[cfg(not(feature = "nightly"))]
-fn main() {
-    println!("To run this example, you need to enable the `nightly` feature. Use Rust nightly and compile with `--features nightly`.")
+    write!(stdout, "{}", termion::cursor::Show).unwrap();
 }
diff --git a/examples/rainbow.rs b/examples/rainbow.rs
new file mode 100644
index 0000000000000000000000000000000000000000..fdc741037255826d62e40e1952e236ee5122c47e
--- /dev/null
+++ b/examples/rainbow.rs
@@ -0,0 +1,50 @@
+#![feature(step_by)]
+
+extern crate termion;
+
+use termion::raw::IntoRawMode;
+use termion::input::{TermRead, Key};
+use std::io::{Write, stdout, stdin};
+
+fn rainbow<W: Write>(stdout: &mut W, blue: u8) {
+    write!(stdout, "{}", termion::cursor::Goto(1, 1)).unwrap();
+
+    for red in (0..255).step_by(8 as u8) {
+        for green in (0..255).step_by(4) {
+            write!(stdout, "{} ", termion::color::Bg(termion::color::Rgb(red, green, blue))).unwrap();
+        }
+        write!(stdout, "\n\r").unwrap();
+    }
+
+    writeln!(stdout, "{}b = {}", termion::style::Reset, blue).unwrap();
+}
+
+fn main() {
+    let stdin = stdin();
+    let mut stdout = stdout().into_raw_mode().unwrap();
+
+    writeln!(stdout, "{}{}{}Use the arrow keys to change the blue in the rainbow.",
+           termion::clear::All,
+           termion::cursor::Goto(1, 1),
+           termion::cursor::Hide).unwrap();
+
+    let mut blue = 0u8;
+
+    for c in stdin.keys() {
+        match c.unwrap() {
+            Key::Up => {
+                blue = blue.saturating_add(4);
+                rainbow(&mut stdout, blue);
+            },
+            Key::Down => {
+                blue = blue.saturating_sub(4);
+                rainbow(&mut stdout, blue);
+            },
+            Key::Char('q') => break,
+            _ => {},
+        }
+        stdout.flush().unwrap();
+    }
+
+    write!(stdout, "{}", termion::cursor::Show).unwrap();
+}
diff --git a/examples/read.rs b/examples/read.rs
index b13ec56dbc29185cb8a390ed73cfc48e1531eaac..28d5df8fe5913c7c1e5e12e3b8f2ab6c1f207170 100644
--- a/examples/read.rs
+++ b/examples/read.rs
@@ -1,6 +1,6 @@
 extern crate termion;
 
-use termion::TermRead;
+use termion::input::TermRead;
 use std::io::{Write, stdout, stdin};
 
 fn main() {
diff --git a/examples/rustc_fun.rs b/examples/rustc_fun.rs
index 1a16753a8ed09bea6a0361b0401554e8001018e6..fcca0432cd8501da9d55a80f8fb70b03485e14b9 100644
--- a/examples/rustc_fun.rs
+++ b/examples/rustc_fun.rs
@@ -1,137 +1,24 @@
 extern crate termion;
 
-use termion::{TermWrite, color, Style};
-use std::io::{self, Write};
+use termion::{color, style};
 
 fn main() {
-    let line_num_bg: color::AnsiValue = color::grayscale(3);
-    let line_num_fg: color::AnsiValue = color::grayscale(18);
-    let error_fg: color::AnsiValue = color::grayscale(17);
-    let info_line: &'static str = "|  ";
-
-    let stdout = io::stdout();
-    let mut stdout = stdout.lock();
-
-    stdout.color(color::LightGreen).unwrap();
-    stdout.write("-- src/test/ui/borrow-errors.rs at 82:18 --\n".as_bytes()).unwrap();
-    stdout.reset().unwrap();
-
-    stdout.color(color::Red).unwrap();
-    stdout.style(Style::Bold).unwrap();
-    stdout.write(b"error: ").unwrap();
-    stdout.reset().unwrap();
-
-    stdout.style(Style::Bold).unwrap();
-    stdout.write(b"two closures require unique access to `vec` at the same time").unwrap();
-    stdout.reset().unwrap();
-
-    stdout.style(Style::Bold).unwrap();
-    stdout.color(color::Magenta).unwrap();
-    stdout.write(b" [E0524]\n").unwrap();
-    stdout.reset().unwrap();
-
-    stdout.color(line_num_fg).unwrap();
-    stdout.bg_color(line_num_bg).unwrap();
-    stdout.write(b"79 ").unwrap();
-    stdout.reset().unwrap();
-
-    stdout.write(b"     let append = |e| {\n").unwrap();
-
-    stdout.color(line_num_fg).unwrap();
-    stdout.bg_color(line_num_bg).unwrap();
-    stdout.write(info_line.as_bytes()).unwrap();
-    stdout.reset().unwrap();
-    stdout.color(color::Red).unwrap();
-    stdout.write("                  ^^^ ".as_bytes()).unwrap();
-    stdout.reset().unwrap();
-
-    stdout.color(error_fg).unwrap();
-    stdout.write(b"first closure is constructed here\n").unwrap();
-    stdout.reset().unwrap();
-
-    stdout.color(line_num_fg).unwrap();
-    stdout.bg_color(line_num_bg).unwrap();
-    stdout.write(b"80 ").unwrap();
-    stdout.reset().unwrap();
-
-    stdout.write(b"         vec.push(e)\n").unwrap();
-
-    stdout.color(line_num_fg).unwrap();
-    stdout.bg_color(line_num_bg).unwrap();
-    stdout.write(info_line.as_bytes()).unwrap();
-    stdout.reset().unwrap();
-    stdout.color(color::Red).unwrap();
-    stdout.write("         ^^^ ".as_bytes()).unwrap();
-    stdout.reset().unwrap();
-
-    stdout.color(error_fg).unwrap();
-    stdout.write(b"previous borrow occurs due to use of `vec` in closure\n").unwrap();
-    stdout.reset().unwrap();
-
-    stdout.color(line_num_fg).unwrap();
-    stdout.bg_color(line_num_bg).unwrap();
-    stdout.write(b"81 ").unwrap();
-    stdout.reset().unwrap();
-    stdout.write(b"     };\n").unwrap();
-
-    stdout.color(line_num_fg).unwrap();
-    stdout.bg_color(line_num_bg).unwrap();
-    stdout.write(b"82 ").unwrap();
-    stdout.reset().unwrap();
-    stdout.write(b"     let append = |e| {\n").unwrap();
-
-    stdout.color(line_num_fg).unwrap();
-    stdout.bg_color(line_num_bg).unwrap();
-    stdout.write(info_line.as_bytes()).unwrap();
-    stdout.reset().unwrap();
-    stdout.color(color::Red).unwrap();
-    stdout.write("                  ^^^ ".as_bytes()).unwrap();
-    stdout.reset().unwrap();
-
-    stdout.color(error_fg).unwrap();
-    stdout.write(b"second closure is constructed here\n").unwrap();
-    stdout.reset().unwrap();
-
-    stdout.color(line_num_fg).unwrap();
-    stdout.bg_color(line_num_bg).unwrap();
-    stdout.write(b"83 ").unwrap();
-    stdout.reset().unwrap();
-
-    stdout.write(b"         vec.push(e)\n").unwrap();
-
-    stdout.color(line_num_fg).unwrap();
-    stdout.bg_color(line_num_bg).unwrap();
-    stdout.write(info_line.as_bytes()).unwrap();
-    stdout.reset().unwrap();
-    stdout.color(color::Red).unwrap();
-    stdout.write("         ^^^ ".as_bytes()).unwrap();
-    stdout.reset().unwrap();
-
-    stdout.color(error_fg).unwrap();
-    stdout.write(b"borrow occurs due to use of `vec` in closure\n").unwrap();
-    stdout.reset().unwrap();
-
-    stdout.color(line_num_fg).unwrap();
-    stdout.bg_color(line_num_bg).unwrap();
-    stdout.write(b"84 ").unwrap();
-    stdout.reset().unwrap();
-    stdout.write(b"     };\n").unwrap();
-
-    stdout.color(line_num_fg).unwrap();
-    stdout.bg_color(line_num_bg).unwrap();
-    stdout.write(b"85 ").unwrap();
-    stdout.reset().unwrap();
-    stdout.write(b" }\n").unwrap();
-
-    stdout.color(line_num_fg).unwrap();
-    stdout.bg_color(line_num_bg).unwrap();
-    stdout.write(info_line.as_bytes()).unwrap();
-    stdout.reset().unwrap();
-    stdout.color(color::Red).unwrap();
-    stdout.write(" ^ ".as_bytes()).unwrap();
-    stdout.reset().unwrap();
-
-    stdout.color(error_fg).unwrap();
-    stdout.write(b"borrow from first closure ends here\n").unwrap();
-    stdout.reset().unwrap();
+    println!("{lighgreen}-- src/test/ui/borrow-errors.rs at 82:18 --\n\
+              {red}error: {reset}{bold}two closures require unique access to `vec` at the same time {reset}{bold}{magenta}[E0524]{reset}\n\
+              {line_num_fg}{line_num_bg}79 {reset}     let append = |e| {{\n\
+              {line_num_fg}{line_num_bg}{info_line}{reset}                  {red}^^^{reset} {error_fg}first closure is constructed here\n\
+              {line_num_fg}{line_num_bg}80 {reset}         vec.push(e)\n\
+              {line_num_fg}{line_num_bg}{info_line}{reset}                 {red}^^^{reset} {error_fg}previous borrow occurs due to use of `vec` in closure\n\
+              {line_num_fg}{line_num_bg}84 {reset}     }};\n\
+              {line_num_fg}{line_num_bg}85 {reset} }}\n\
+              {line_num_fg}{line_num_bg}{info_line}{reset} {red}^{reset} {error_fg}borrow from first closure ends here",
+                  lighgreen=color::Fg(color::LightGreen),
+                  red=color::Fg(color::Red),
+                  bold=style::Bold,
+                  reset=style::Reset,
+                  magenta=color::Fg(color::Magenta),
+                  line_num_bg=color::Bg(color::AnsiValue::grayscale(3)),
+                  line_num_fg=color::Fg(color::AnsiValue::grayscale(18)),
+                  info_line="|  ",
+                  error_fg=color::Fg(color::AnsiValue::grayscale(17)))
 }
diff --git a/examples/simple.rs b/examples/simple.rs
index 5cf2cc21831d80a2be0990deff49b5756870e51a..23fe480f450fb831388646a423d092d44d4ee165 100644
--- a/examples/simple.rs
+++ b/examples/simple.rs
@@ -1,6 +1,7 @@
 extern crate termion;
 
-use termion::{TermWrite, IntoRawMode, Color, Style};
+use termion::color;
+use termion::raw::IntoRawMode;
 use std::io::{Read, Write, stdout, stdin};
 
 fn main() {
@@ -10,19 +11,9 @@ fn main() {
     let stdin = stdin();
     let stdin = stdin.lock();
 
-    // Move the cursor to (5, 5)
-    stdout.goto(5, 5).unwrap();
-    // Clear the screen.
-    stdout.clear().unwrap();
-    // Set style to bold.
-    stdout.style(Style::Bold).unwrap();
-    // Write some guiding stuff
-    stdout.write(b"yo, 'q' will exit.").unwrap();
-    // Reset the style.
-    stdout.reset().unwrap();
-    // Flush and goto (20, 10)
+    write!(stdout, "{}{}{}yo, 'q' will exit.{}{}", termion::clear::All, termion::cursor::Goto(5, 5),
+           termion::style::Bold, termion::style::Reset, termion::cursor::Goto(20, 10)).unwrap();
     stdout.flush().unwrap();
-    stdout.goto(20, 10).unwrap();
 
     let mut bytes = stdin.bytes();
     loop {
@@ -32,11 +23,11 @@ fn main() {
             // Quit
             b'q' => return,
             // Clear the screen
-            b'c' => stdout.clear(),
+            b'c' => write!(stdout, "{}", termion::clear::All),
             // Set red color
-            b'r' => stdout.color(Color::Rgb(5, 0, 0)),
+            b'r' => write!(stdout, "{}", color::Fg(color::Rgb(5, 0, 0))),
             // Write it to stdout.
-            a => stdout.write(&[a]),
+            a => write!(stdout, "{}", a),
         }.unwrap();
 
         stdout.flush().unwrap();
diff --git a/examples/truecolor.rs b/examples/truecolor.rs
new file mode 100644
index 0000000000000000000000000000000000000000..ba5c8685b89d2011c033b466ce5e6cea08b7fb30
--- /dev/null
+++ b/examples/truecolor.rs
@@ -0,0 +1,12 @@
+extern crate termion;
+
+use termion::{color, cursor, clear};
+use std::{thread, time};
+
+fn main() {
+    for r in 0..255 {
+        let c = color::Rgb(r, !r, 2 * ((r % 128) as i8 - 64).abs() as u8);
+        println!("{}{}{}wow", cursor::Goto(1, 1), color::Bg(c), clear::All);
+        thread::sleep(time::Duration::from_millis(100));
+    }
+}
diff --git a/src/clear.rs b/src/clear.rs
new file mode 100644
index 0000000000000000000000000000000000000000..d37f2c6b917da566f78eeabfe1c975c6f1db152f
--- /dev/null
+++ b/src/clear.rs
@@ -0,0 +1,9 @@
+//! Clearing the screen.
+
+use std::fmt;
+
+derive_csi_sequence!("Clear the entire screen.", All, "2J");
+derive_csi_sequence!("Clear everything after the cursor.", AfterCursor, "J");
+derive_csi_sequence!("Clear everything before the cursor.", BeforeCursor, "1J");
+derive_csi_sequence!("Clear the current line.", CurrentLine, "2K");
+derive_csi_sequence!("Clear from cursor to newline.", UntilNewline, "K");
diff --git a/src/color.rs b/src/color.rs
index 3c8063fb3574df316333d893f2ae24aeb3c4a325..60e2687ef75af5230d229e328122006ee1779af5 100644
--- a/src/color.rs
+++ b/src/color.rs
@@ -1,219 +1,119 @@
+//! Colors.
+
+use std::fmt;
+
 /// A terminal color.
 pub trait Color {
-    /// Convert this to its ANSI value.
-    fn to_ansi_val(self) -> u8;
+    /// Write the foreground version of this color.
+    fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result;
+    /// Write the background version of this color.
+    fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result;
 }
 
 macro_rules! derive_color {
     ($doc:expr, $name:ident, $value:expr) => {
         #[doc = $doc]
+        #[derive(Copy, Clone)]
         pub struct $name;
 
         impl Color for $name {
             #[inline]
-            fn to_ansi_val(self) -> u8 {
-                $value
+            fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result {
+                write!(f, csi!("38;5;", $value, "m"))
+            }
+
+            #[inline]
+            fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result {
+                write!(f, csi!("48;5;", $value, "m"))
             }
         }
     };
 }
 
-
-derive_color!("Black.", Black, 0x0);
-derive_color!("Red.", Red, 0x1);
-derive_color!("Green.", Green, 0x2);
-derive_color!("Yellow.", Yellow, 0x3);
-derive_color!("Blue.", Blue, 0x4);
-derive_color!("Magenta.", Magenta, 0x5);
-derive_color!("Cyan.", Cyan, 0x6);
-derive_color!("White.", White, 0x7);
-derive_color!("High-intensity light black.", LightBlack, 0x8);
-derive_color!("High-intensity light red.", LightRed, 0x9);
-derive_color!("High-intensity light green.", LightGreen, 0xA);
-derive_color!("High-intensity light yellow.", LightYellow, 0xB);
-derive_color!("High-intensity light blue.", LightBlue, 0xC);
-derive_color!("High-intensity light magenta.", LightMagenta, 0xD);
-derive_color!("High-intensity light cyan.", LightCyan, 0xE);
-derive_color!("High-intensity light white.", LightWhite, 0xF);
-
-/// 216-color (r, g, b ≤ 5) RGB.
-pub fn rgb(r: u8, g: u8, b: u8) -> AnsiValue {
-    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);
-
-    AnsiValue(16 + 36 * r + 6 * g + b)
-}
-
-/// Grayscale color.
-///
-/// There are 24 shades of gray.
-pub fn grayscale(shade: u8) -> AnsiValue {
-    // 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);
-
-    AnsiValue(0xE8 + shade)
-}
+derive_color!("Black.", Black, "0");
+derive_color!("Red.", Red, "1");
+derive_color!("Green.", Green, "2");
+derive_color!("Yellow.", Yellow, "3");
+derive_color!("Blue.", Blue, "4");
+derive_color!("Magenta.", Magenta, "5");
+derive_color!("Cyan.", Cyan, "6");
+derive_color!("White.", White, "7");
+derive_color!("High-intensity light black.", LightBlack, "8");
+derive_color!("High-intensity light red.", LightRed, "9");
+derive_color!("High-intensity light green.", LightGreen, "10");
+derive_color!("High-intensity light yellow.", LightYellow, "11");
+derive_color!("High-intensity light blue.", LightBlue, "12");
+derive_color!("High-intensity light magenta.", LightMagenta, "13");
+derive_color!("High-intensity light cyan.", LightCyan, "14");
+derive_color!("High-intensity light white.", LightWhite, "15");
 
 /// An arbitrary ANSI color value.
 #[derive(Clone, Copy)]
 pub struct AnsiValue(pub u8);
 
-impl Color for AnsiValue {
-    #[inline]
-    fn to_ansi_val(self) -> u8 {
-        self.0
-    }
-}
-
-/// A color palette.
-///
-/// This should generally only be used when the color is runtime determined. Otherwise, use the
-/// color types, which resolves the value at compile time.
-#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
-pub enum Palette {
-    /// Black.
-    Black,
-    /// Red.
-    Red,
-    /// Green.
-    Green,
-    /// Yellow.
-    Yellow,
-    /// Blue.
-    Blue,
-    /// Megenta.
-    Magenta,
-    /// Cyan.
-    Cyan,
-    /// White.
-    White,
-    /// High-intensity black.
-    LightBlack,
-    /// High-intensity red.
-    LightRed,
-    /// High-intensity green.
-    LightGreen,
-    /// High-intensity yellow.
-    LightYellow,
-    /// High-intensity blue.
-    LightBlue,
-    /// High-intensity magenta.
-    LightMagenta,
-    /// High-intensity cyan.
-    LightCyan,
-    /// High-intensity white.
-    LightWhite,
+impl AnsiValue {
     /// 216-color (r, g, b ≤ 5) RGB.
-    Rgb(u8, u8, u8),
-    /// Grayscale (max value: 24).
-    Grayscale(u8),
-}
+    pub fn rgb(r: u8, g: u8, b: u8) -> AnsiValue {
+        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);
 
-impl Color for Palette {
-    fn to_ansi_val(self) -> u8 {
-        match self {
-            Palette::Black => Black.to_ansi_val(),
-            Palette::Red => Red.to_ansi_val(),
-            Palette::Green => Green.to_ansi_val(),
-            Palette::Yellow => Yellow.to_ansi_val(),
-            Palette::Blue => Blue.to_ansi_val(),
-            Palette::Magenta => Magenta.to_ansi_val(),
-            Palette::Cyan => Cyan.to_ansi_val(),
-            Palette::White => White.to_ansi_val(),
-            Palette::LightBlack => LightBlack.to_ansi_val(),
-            Palette::LightRed => LightRed.to_ansi_val(),
-            Palette::LightGreen => LightGreen.to_ansi_val(),
-            Palette::LightYellow => LightYellow.to_ansi_val(),
-            Palette::LightBlue => LightBlue.to_ansi_val(),
-            Palette::LightMagenta => LightMagenta.to_ansi_val(),
-            Palette::LightCyan => LightCyan.to_ansi_val(),
-            Palette::LightWhite => LightWhite.to_ansi_val(),
-            Palette::Rgb(r, g, b) => rgb(r, g, b).to_ansi_val(),
-            Palette::Grayscale(shade) => grayscale(shade).to_ansi_val(),
-        }
+        AnsiValue(16 + 36 * r + 6 * g + b)
     }
-}
 
-#[cfg(test)]
-mod test {
-    use super::*;
-
-    #[test]
-    fn test_rgb() {
-        assert_eq!(rgb(2, 3, 4).to_ansi_val(), 110);
-        assert_eq!(rgb(2, 1, 4).to_ansi_val(), 98);
-        assert_eq!(rgb(5, 1, 4).to_ansi_val(), 206);
-    }
+    /// Grayscale color.
+    ///
+    /// There are 24 shades of gray.
+    pub fn grayscale(shade: u8) -> AnsiValue {
+        // 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);
 
-    #[test]
-    fn test_grayscale() {
-        assert_eq!(grayscale(2).to_ansi_val(), 234);
-        assert_eq!(grayscale(5).to_ansi_val(), 237);
+        AnsiValue(0xE8 + shade)
     }
+}
 
-    #[test]
-    fn test_normal() {
-        assert_eq!(Black.to_ansi_val(), 0);
-        assert_eq!(Green.to_ansi_val(), 2);
-        assert_eq!(White.to_ansi_val(), 7);
+impl Color for AnsiValue {
+    #[inline]
+    fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, csi!("38;5;{}m"), self.0)
     }
 
-    #[test]
-    fn test_hi() {
-        assert_eq!(LightRed.to_ansi_val(), 9);
-        assert_eq!(LightCyan.to_ansi_val(), 0xE);
-        assert_eq!(LightWhite.to_ansi_val(), 0xF);
+    #[inline]
+    fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, csi!("48;5;{}m"), self.0)
     }
+}
 
-    #[test]
-    fn test_palette() {
-        assert_eq!(Palette::Black.to_ansi_val(), Black.to_ansi_val());
-        assert_eq!(Palette::Red.to_ansi_val(), Red.to_ansi_val());
-        assert_eq!(Palette::LightBlue.to_ansi_val(), LightBlue.to_ansi_val());
-        assert_eq!(Palette::Rgb(2, 2, 2).to_ansi_val(), rgb(2, 2, 2).to_ansi_val());
-    }
+/// A truecolor RGB.
+pub struct Rgb(pub u8, pub u8, pub u8);
 
-    #[cfg(debug)]
-    #[should_panic]
-    #[test]
-    fn test_bound_check_rgb() {
-        rgb(3, 9, 1);
+impl Color for Rgb {
+    #[inline]
+    fn write_fg(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, csi!("38;2;{};{};{}m"), self.0, self.1, self.2)
     }
 
-    #[cfg(debug)]
-    #[should_panic]
-    #[test]
-    fn test_bound_check_rgb_2() {
-        rgb(3, 6, 1);
+    #[inline]
+    fn write_bg(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, csi!("48;2;{};{};{}m"), self.0, self.1, self.2)
     }
+}
 
-    #[cfg(debug)]
-    #[should_panic]
-    #[test]
-    fn test_bound_check_grayscale() {
-        grayscale(25);
-    }
+/// A foreground color.
+pub struct Fg<C: Color>(pub C);
 
-    #[cfg(debug)]
-    #[should_panic]
-    #[test]
-    fn test_palette_rgb_bound_check_1() {
-        Palette::Rgb(3, 6, 1).to_ansi_val();
+impl<C: Color> fmt::Display for Fg<C> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        self.0.write_fg(f)
     }
+}
 
-    #[cfg(debug)]
-    #[should_panic]
-    #[test]
-    fn test_palette_rgb_bound_check_2() {
-        Palette::Rgb(3, 9, 1).to_ansi_val();
-    }
+/// A background color.
+pub struct Bg<C: Color>(pub C);
 
-    #[cfg(debug)]
-    #[should_panic]
-    #[test]
-    fn test_palette_grayscale_bound_check_2() {
-        Palette::Grayscale(25).to_ansi_val();
+impl<C: Color> fmt::Display for Bg<C> {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        self.0.write_bg(f)
     }
 }
diff --git a/src/control.rs b/src/control.rs
deleted file mode 100644
index 810a087c8340f30a4767f5f5294719299349f42b..0000000000000000000000000000000000000000
--- a/src/control.rs
+++ /dev/null
@@ -1,297 +0,0 @@
-use std::io::{self, Write};
-
-use Style;
-use color::Color;
-
-/// Extension to the `Write` trait.
-///
-/// This extension to the `Write` trait is capable of producing the correct ANSI escape sequences
-/// for given commands, effectively controlling the terminal.
-pub trait TermWrite {
-
-    /// Print the CSI (control sequence introducer) followed by a byte string.
-    #[inline]
-    fn csi(&mut self, b: &[u8]) -> io::Result<usize>;
-    /// Print OSC (operating system command) followed by a byte string.
-    #[inline]
-    fn osc(&mut self, b: &[u8]) -> io::Result<usize>;
-    /// Print DSC (device control string) followed by a byte string.
-    #[inline]
-    fn dsc(&mut self, b: &[u8]) -> io::Result<usize>;
-
-
-    /// Clear the entire screen.
-    #[inline]
-    fn clear(&mut self) -> io::Result<usize> {
-        self.csi(b"2J")
-    }
-
-    /// Clear everything _after_ the cursor.
-    #[inline]
-    fn clear_after(&mut self) -> io::Result<usize> {
-        self.csi(b"J")
-    }
-
-    /// Clear everything _before_ the cursor.
-    #[inline]
-    fn clear_before(&mut self) -> io::Result<usize> {
-        self.csi(b"1J")
-    }
-
-    /// Clear the current line.
-    #[inline]
-    fn clear_line(&mut self) -> io::Result<usize> {
-        self.csi(b"2K")
-    }
-
-    /// Clear from the cursor until newline.
-    #[inline]
-    fn clear_until_newline(&mut self) -> io::Result<usize> {
-        self.csi(b"K")
-    }
-
-    /// Show the cursor.
-    #[inline]
-    fn show_cursor(&mut self) -> io::Result<usize> {
-        self.csi(b"?25h")
-    }
-
-    /// Hide the cursor.
-    #[inline]
-    fn hide_cursor(&mut self) -> io::Result<usize> {
-        self.csi(b"?25l")
-    }
-
-    /// Move the cursor `num` spaces to the left.
-    #[inline]
-    fn move_cursor_left(&mut self, num: u32) -> io::Result<usize> {
-        if num > 0 {
-            self.csi(&[b'0' + (num / 10000) as u8,
-                       b'0' + (num / 1000) as u8 % 10,
-                       b'0' + (num / 100) as u8 % 10,
-                       b'0' + (num / 10) as u8 % 10,
-                       b'0' + num as u8 % 10,
-                       b'D'])
-        } else {
-            Ok(0)
-        }
-    }
-    /// Move the cursor `num` spaces to the right.
-    #[inline]
-    fn move_cursor_right(&mut self, num: u32) -> io::Result<usize> {
-        if num > 0 {
-            self.csi(&[b'0' + (num / 10000) as u8,
-                       b'0' + (num / 1000) as u8 % 10,
-                       b'0' + (num / 100) as u8 % 10,
-                       b'0' + (num / 10) as u8 % 10,
-                       b'0' + num as u8 % 10,
-                       b'C'])
-        } else {
-            Ok(0)
-        }
-    }
-
-    /// Reset the rendition mode.
-    ///
-    /// This will reset both the current style and color.
-    #[inline]
-    fn reset(&mut self) -> io::Result<usize> {
-        self.csi(b"m")
-    }
-
-    /// Restore the defaults.
-    ///
-    /// This will reset color, position, cursor state, and so on. It is recommended that you use
-    /// this before you exit your program, to avoid messing up the user's terminal.
-    #[inline]
-    fn restore(&mut self) -> io::Result<usize> {
-        Ok(try!(self.reset()) + try!(self.clear()) + try!(self.goto(0, 0)) + try!(self.show_cursor()))
-    }
-
-    /// Go to a given position.
-    ///
-    /// The position is 0-based.
-    #[inline]
-    fn goto(&mut self, mut x: u16, mut y: u16) -> io::Result<usize> {
-        x += 1;
-        y += 1;
-
-        self.csi(&[
-            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';',
-            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'H',
-        ])
-    }
-
-    /// Set graphic rendition.
-    #[inline]
-    fn rendition(&mut self, r: u8) -> io::Result<usize> {
-        self.csi(&[
-            b'0' + r / 100,
-            b'0' + r / 10 % 10,
-            b'0' + r % 10,
-            b'm',
-        ])
-    }
-
-    /// Set foreground color.
-    #[inline]
-    fn color<C: Color>(&mut self, color: C) -> io::Result<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.
-    #[inline]
-    fn bg_color<C: Color>(&mut self, color: C) -> io::Result<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',
-        ])
-    }
-
-    /// Set rendition mode (SGR).
-    #[inline]
-    fn style(&mut self, mode: Style) -> io::Result<usize> {
-        self.rendition(mode as u8)
-    }
-}
-
-impl<W: Write> TermWrite for W {
-    #[inline]
-    fn csi(&mut self, b: &[u8]) -> io::Result<usize> {
-        Ok(try!(self.write(b"\x1B[")) + try!(self.write(b)))
-    }
-
-    #[inline]
-    fn osc(&mut self, b: &[u8]) -> io::Result<usize> {
-        Ok(try!(self.write(b"\x1B]")) + try!(self.write(b)))
-    }
-
-    #[inline]
-    fn dsc(&mut self, b: &[u8]) -> io::Result<usize> {
-        Ok(try!(self.write(b"\x1BP")) + try!(self.write(b)))
-    }
-}
-
-#[cfg(test)]
-mod test {
-    use super::*;
-    use std::io::Cursor;
-
-    #[test]
-    fn test_csi() {
-        let mut buf = Cursor::new(Vec::new());
-        buf.csi(b"bluh").unwrap();
-
-        assert_eq!(buf.get_ref(), b"\x1B[bluh");
-
-        buf.csi(b"blah").unwrap();
-        assert_eq!(buf.get_ref(), b"\x1B[bluh\x1B[blah");
-    }
-
-    #[test]
-    fn test_csi_partial() {
-        let mut buf = [0; 3];
-        let mut buf = &mut buf[..];
-        assert_eq!(buf.csi(b"blu").unwrap(), 3);
-        assert_eq!(buf.csi(b"").unwrap(), 0);
-        assert_eq!(buf.csi(b"nooooo").unwrap(), 0);
-    }
-
-    #[test]
-    fn test_osc() {
-        let mut buf = Cursor::new(Vec::new());
-        buf.osc(b"bluh").unwrap();
-
-        assert_eq!(buf.get_ref(), b"\x1B]bluh");
-
-        buf.osc(b"blah").unwrap();
-        assert_eq!(buf.get_ref(), b"\x1B]bluh\x1B]blah");
-    }
-
-    #[test]
-    fn test_osc_partial() {
-        let mut buf = [0; 3];
-        let mut buf = &mut buf[..];
-        assert_eq!(buf.osc(b"blu").unwrap(), 3);
-        assert_eq!(buf.osc(b"").unwrap(), 0);
-        assert_eq!(buf.osc(b"nooooo").unwrap(), 0);
-    }
-
-    #[test]
-    fn test_dsc() {
-        let mut buf = Cursor::new(Vec::new());
-        buf.dsc(b"bluh").unwrap();
-
-        assert_eq!(buf.get_ref(), b"\x1BPbluh");
-
-        buf.dsc(b"blah").unwrap();
-        assert_eq!(buf.get_ref(), b"\x1BPbluh\x1BPblah");
-    }
-
-    #[test]
-    fn test_dsc_partial() {
-        let mut buf = [0; 3];
-        let mut buf = &mut buf[..];
-        assert_eq!(buf.dsc(b"blu").unwrap(), 3);
-        assert_eq!(buf.dsc(b"").unwrap(), 0);
-        assert_eq!(buf.dsc(b"nooooo").unwrap(), 0);
-    }
-
-    #[test]
-    fn test_clear() {
-        let mut buf = Cursor::new(Vec::new());
-        buf.clear().unwrap();
-        assert_eq!(buf.get_ref(), b"\x1B[2J");
-        buf.clear().unwrap();
-        assert_eq!(buf.get_ref(), b"\x1B[2J\x1B[2J");
-    }
-
-    #[test]
-    fn test_goto() {
-        let mut buf = Cursor::new(Vec::new());
-        buf.goto(34, 43).unwrap();
-        assert_eq!(buf.get_ref(), b"\x1B[00044;00035H");
-        buf.goto(24, 45).unwrap();
-        assert_eq!(buf.get_ref(), b"\x1B[00044;00035H\x1B[00046;00025H");
-    }
-
-    #[test]
-    fn test_style() {
-        use Style;
-
-        let mut buf = Cursor::new(Vec::new());
-        buf.style(Style::Bold).unwrap();
-        assert_eq!(buf.get_ref(), b"\x1B[001m");
-        buf.style(Style::Italic).unwrap();
-        assert_eq!(buf.get_ref(), b"\x1B[001m\x1B[003m");
-    }
-}
diff --git a/src/cursor.rs b/src/cursor.rs
new file mode 100644
index 0000000000000000000000000000000000000000..a169c2d74ebae98ed3b0cfc978fc4175c6885383
--- /dev/null
+++ b/src/cursor.rs
@@ -0,0 +1,48 @@
+//! Cursor.
+
+use std::fmt;
+
+derive_csi_sequence!("Hide the cursor.", Hide, "?25l");
+derive_csi_sequence!("Show the cursor.", Show, "?25h");
+
+/// Goto some position ((1,1)-based).
+///
+/// # Why one-based?
+///
+/// ANSI escapes are very poorly designed, and one of the many odd aspects is being one-based. This
+/// can be quite strange at first, but it is not that big of an obstruction once you get used to
+/// it.
+#[derive(Copy, Clone, PartialEq, Eq)]
+pub struct Goto(pub u16, pub u16);
+
+impl Default for Goto {
+    fn default() -> Goto { Goto(1, 1) }
+}
+
+impl fmt::Display for Goto {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        debug_assert!(self != &Goto(0, 0), "Goto is one-based.");
+
+        write!(f, csi!("{};{}H"), self.0, self.1)
+    }
+}
+
+/// Move cursor left.
+#[derive(Copy, Clone, PartialEq, Eq)]
+pub struct Left(pub u16);
+
+impl fmt::Display for Left {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, csi!("{}D"), self.0)
+    }
+}
+
+/// Move cursor right.
+#[derive(Copy, Clone, PartialEq, Eq)]
+pub struct Right(pub u16);
+
+impl fmt::Display for Right {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        write!(f, csi!("{}C"), self.0)
+    }
+}
diff --git a/src/input.rs b/src/input.rs
index 7258d34b9ffadfe5fcae0f298d288afc28be491a..55a7ac082f2bd3f7a7c21833ba85d19a58ac8434 100644
--- a/src/input.rs
+++ b/src/input.rs
@@ -1,6 +1,8 @@
+//! Input.
+
 use std::io::{self, Read, Write};
 
-use IntoRawMode;
+use raw::IntoRawMode;
 
 /// A key.
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
diff --git a/src/lib.rs b/src/lib.rs
index 8a789818131fa6fbb0ecc49afa9eb496ce905bbc..285d15f2ce3543f240aa3f40e1f8cfd65e5c5662 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -20,29 +20,20 @@ extern crate libc;
 #[cfg(not(target_os = "redox"))]
 mod termios;
 
-mod control;
-pub use control::TermWrite;
-
 mod async;
 pub use async::{AsyncReader, async_stdin};
 
-mod input;
-pub use input::{TermRead, Key};
-#[cfg(feature = "nightly")]
-pub use input::Keys;
-
-mod raw;
-pub use raw::{IntoRawMode, RawTerminal};
-
 mod size;
 pub use size::terminal_size;
 
-/// ANSI colors.
-pub mod color;
-
-/// Deprecated reexport.
-#[deprecated]
-pub use color::Palette as Color;
+mod tty;
+pub use tty::is_tty;
 
-mod style;
-pub use style::Style;
+#[macro_use]
+mod macros;
+pub mod clear;
+pub mod color;
+pub mod cursor;
+pub mod input;
+pub mod raw;
+pub mod style;
diff --git a/src/macros.rs b/src/macros.rs
new file mode 100644
index 0000000000000000000000000000000000000000..28370e36b4c29a760886e10d7ea016a5e8c5828e
--- /dev/null
+++ b/src/macros.rs
@@ -0,0 +1,19 @@
+/// Create a CSI-introduced sequence.
+macro_rules! csi {
+    ($( $l:expr ),*) => { concat!("\x1B[", $( $l ),*) };
+}
+
+/// Derive a CSI sequence struct.
+macro_rules! derive_csi_sequence {
+    ($doc:expr, $name:ident, $value:expr) => {
+        #[doc = $doc]
+        #[derive(Copy, Clone)]
+        pub struct $name;
+
+        impl fmt::Display for $name {
+            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+                write!(f, csi!($value))
+            }
+        }
+    };
+}
diff --git a/src/raw.rs b/src/raw.rs
index ca5215dc6dbd45c77d0a54805f58414bdc19f081..1c097ff02a3b41a648113679a00a851e5aa0e25f 100644
--- a/src/raw.rs
+++ b/src/raw.rs
@@ -1,3 +1,5 @@
+//! Raw mode.
+
 use std::io::{self, Write};
 use std::ops::{Deref, DerefMut};
 
diff --git a/src/style.rs b/src/style.rs
index ac750d494eb9f98a498b13030d4f0bbf8cd327e2..f1b1a2ef61e169470901869e254e2205f287a2b4 100644
--- a/src/style.rs
+++ b/src/style.rs
@@ -1,21 +1,13 @@
-/// A SGR parameter (rendition mode).
-pub enum Style {
-    /// Reset SGR parameters.
-    Reset = 0,
-    /// Bold text.
-    Bold = 1,
-    /// Fainted text (not widely supported).
-    Faint = 2,
-    /// Italic text.
-    Italic = 3,
-    /// Underlined text.
-    Underline = 4,
-    /// Blinking text (not widely supported).
-    Blink = 5,
-    /// Inverted colors (negative mode).
-    Invert = 7,
-    /// Crossed out text (not widely supported).
-    CrossedOut = 9,
-    /// Framed text (not widely supported).
-    Framed = 51,
-}
+//! Style.
+
+use std::fmt;
+
+derive_csi_sequence!("Reset SGR parameters.", Reset, "m");
+derive_csi_sequence!("Bold text.", Bold, "1m");
+derive_csi_sequence!("Fainted text (not widely supported).", Faint, "2m");
+derive_csi_sequence!("Italic text.", Italic, "3m");
+derive_csi_sequence!("Underlined text.", Underline, "4m");
+derive_csi_sequence!("Blinking text (not widely supported).", Blink, "5m");
+derive_csi_sequence!("Inverted colors (negative mode).", Invert, "7m");
+derive_csi_sequence!("Crossed out text (not widely supported).", CrossedOut, "9m");
+derive_csi_sequence!("Framed text (not widely supported).", Framed, "51m");
diff --git a/src/tty.rs b/src/tty.rs
new file mode 100644
index 0000000000000000000000000000000000000000..17b4177cbed2ace74f3026ccc449e10881fd954e
--- /dev/null
+++ b/src/tty.rs
@@ -0,0 +1,15 @@
+use std::os::unix::io::AsRawFd;
+
+/// Is this stream an TTY?
+#[cfg(not(target_os = "redox"))]
+pub fn is_tty<T: AsRawFd>(stream: T) -> bool {
+    use libc;
+
+    unsafe { libc::isatty(stream.as_raw_fd()) == 1}
+}
+
+/// This will panic.
+#[cfg(target_os = "redox")]
+pub fn is_tty<T: AsRawFd>(_stream: T) -> bool {
+    unimplemented!();
+}