diff --git a/Cargo.toml b/Cargo.toml
index 69996f00a30d1c987196f812338238e9939fa5b7..bb21214d86795abc66905baa6076b044d0be9dd4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,14 +13,13 @@ license = "MIT"
 libc = "0.2.7"
 unicode-width = "0.1.3"
 encode_unicode = "0.1.3"
-clippy = {version = "~0.0.58", optional = true}
 
 [target.'cfg(unix)'.dependencies]
 nix = "0.5.0"
 
 [target.'cfg(windows)'.dependencies]
 winapi = "0.2"
-kernel32-sys = "0.2.2"
+kernel32-sys = "0.2"
 
 [dev-dependencies]
 tempdir = "0.3.4"
diff --git a/README.md b/README.md
index 3737f41a042c0fb9568e5021a08693d97558c278..22872813fd78e4de462e7e9bdae7feb29df02d9b 100644
--- a/README.md
+++ b/README.md
@@ -88,7 +88,8 @@ Ctrl-D       | (if line *is* empty) End of File
 Ctrl-E, End  | Move cursor to end of line
 Ctrl-F, Right| Move cursor one character right
 Ctrl-H, BackSpace | Delete character before cursor
-Ctrl-J, Return | Finish the line entry
+Ctrl-I, Tab  | Next completion
+Ctrl-J, Ctrl-M, Enter | Finish the line entry
 Ctrl-K       | Delete from cursor to end of line
 Ctrl-L       | Clear screen
 Ctrl-N, Down | Next match from history
@@ -98,19 +99,41 @@ Ctrl-T       | Transpose previous character with current character
 Ctrl-U       | Delete from start of line to cursor
 Ctrl-V       | Insert any special character without perfoming its associated action
 Ctrl-W       | Delete word leading up to cursor (using white space as a word boundary)
-Ctrl-Y       | Paste from Yank buffer (Alt-Y to paste next yank instead)
-Tab          | Next completion
-Alt-B, Alt-Left | Move cursor to previous word
-Alt-C        | Capitalize the current word
-Alt-D        | Delete forwards one word
-Alt-F, Alt-Right | Move cursor to next word
-Alt-L        | Lower-case the next word
-Alt-T        | Transpose words
-Alt-U        | Upper-case the next word
-Alt-Y        | See Ctrl-Y
-Alt-BackSpace | Kill from the start of the current word, or, if between words, to the start of the previous word
+Ctrl-Y       | Paste from Yank buffer (Meta-Y to paste next yank instead)
+Meta-<       | Move to first entry in history
+Meta->       | Move to last entry in history
+Meta-B, Alt-Left | Move cursor to previous word
+Meta-C       | Capitalize the current word
+Meta-D       | Delete forwards one word
+Meta-F, Alt-Right | Move cursor to next word
+Meta-L       | Lower-case the next word
+Meta-T       | Transpose words
+Meta-U       | Upper-case the next word
+Meta-Y       | See Ctrl-Y
+Meta-BackSpace | Kill from the start of the current word, or, if between words, to the start of the previous word
 
 ## ToDo
 
  - Show completion list
+ - Undos
+ - Read input with timeout to properly handle single ESC key
  - expose an API callable from C
+
+## Wine
+
+```sh
+$ cargo run --example example --target 'x86_64-pc-windows-gnu'
+...
+Error: Io(Error { repr: Os { code: 6, message: "Invalid handle." } })
+$ wineconsole --backend=curses target/x86_64-pc-windows-gnu/debug/examples/example.exe
+...
+```
+
+## Similar projects
+
+ - [copperline](https://github.com/srijs/rust-copperline) (Rust)
+ - [liner](https://github.com/MovingtoMars/liner) (Rust)
+ - [linenoise-ng](https://github.com/arangodb/linenoise-ng) (C++)
+ - [liner](https://github.com/peterh/liner) (Go)
+ - [readline](https://github.com/chzyer/readline) (Go)
+ - [haskeline](https://github.com/judah/haskeline) (Haskell)
diff --git a/appveyor.yml b/appveyor.yml
index 03a90c521251b46bb68211871f7dc97fb6deaf88..e0a1efeeb52d234caff1ce7415647c59b873c1b2 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -19,4 +19,3 @@ test_script:
 
 cache:
   - C:\Users\appveyor\.cargo
-
diff --git a/examples/example.rs b/examples/example.rs
index 134e1e25c88dc78478502a9d0f5fd86238810409..62fb4fe609146ce1fcb0ed1ba734f2726929fca5 100644
--- a/examples/example.rs
+++ b/examples/example.rs
@@ -8,7 +8,7 @@ use rustyline::Editor;
 #[cfg(unix)]
 static PROMPT: &'static str = "\x1b[1;32m>>\x1b[0m ";
 
-// Windows consoles typically don't support ANSI escape sequences out 
+// Windows consoles typically don't support ANSI escape sequences out
 // of the box
 #[cfg(windows)]
 static PROMPT: &'static str = ">> ";
diff --git a/src/char_iter.rs b/src/char_iter.rs
index 129786d34dd55d0a60a38dec7d93d3b3ed4a60c7..0cfdb071ccf5adcc5fab766aad77ab673451fe46 100644
--- a/src/char_iter.rs
+++ b/src/char_iter.rs
@@ -9,7 +9,9 @@ use std::io;
 use std::io::Read;
 use std::str;
 
-pub fn chars<R: Read>(read: R) -> Chars<R> where R: Sized {
+pub fn chars<R: Read>(read: R) -> Chars<R>
+    where R: Sized
+{
     Chars { inner: read }
 }
 
@@ -60,8 +62,12 @@ impl<R: Read> Iterator for Chars<R> {
             Err(e) => return Some(Err(CharsError::Other(e))),
         };
         let width = utf8_char_width(first_byte);
-        if width == 1 { return Some(Ok(first_byte as char)) }
-        if width == 0 { return Some(Err(CharsError::NotUtf8)) }
+        if width == 1 {
+            return Some(Ok(first_byte as char));
+        }
+        if width == 0 {
+            return Some(Err(CharsError::NotUtf8));
+        }
         let mut buf = [first_byte, 0, 0, 0];
         {
             let mut start = 1;
@@ -98,9 +104,7 @@ impl error::Error for CharsError {
 impl fmt::Display for CharsError {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         match *self {
-            CharsError::NotUtf8 => {
-                "byte stream did not contain valid utf8".fmt(f)
-            }
+            CharsError::NotUtf8 => "byte stream did not contain valid utf8".fmt(f),
             CharsError::Other(ref e) => e.fmt(f),
         }
     }
diff --git a/src/consts.rs b/src/consts.rs
index e0a6d3ce7ad325cceb6bd71b58234a91302f6f7f..5fc32ea3b41016e3a801c7d54c1d094fe9d3d292 100644
--- a/src/consts.rs
+++ b/src/consts.rs
@@ -1,75 +1,56 @@
 
-
 #[derive(Debug, Clone, PartialEq, Copy)]
 pub enum KeyPress {
-    NULL,
-    CTRL_A,
-    CTRL_B,
-    CTRL_C,
-    CTRL_D,
-    CTRL_E,
-    CTRL_F,
-    CTRL_G,
-    CTRL_H,
-    TAB,
-    CTRL_J,
-    CTRL_K,
-    CTRL_L,
-    ENTER,
-    CTRL_N,
-    CTRL_P,
-    CTRL_R,
-    CTRL_S,
-    CTRL_T,
-    CTRL_U,
-    CTRL_V,
-    CTRL_W,
-    CTRL_Y,
-    CTRL_Z,
-    ESC,
-    BACKSPACE,
     UNKNOWN_ESC_SEQ,
-    ESC_SEQ_DELETE,
-    ESC_BACKSPACE,
-    ESC_B,
-    ESC_C,
-    ESC_D,
-    ESC_F,
-    ESC_L,
-    ESC_T,
-    ESC_U,
-    ESC_Y,
+    Backspace,
+    Char(char),
+    Ctrl(char),
+    Delete,
+    Down,
+    End,
+    Enter, // Ctrl('M')
+    Esc,
+    Home,
+    Left,
+    Meta(char),
+    Null,
+    Right,
+    Tab, // Ctrl('I')
+    Up,
 }
 
 #[cfg_attr(feature="clippy", allow(match_same_arms))]
 pub fn char_to_key_press(c: char) -> KeyPress {
+    if !c.is_control() {
+        return KeyPress::Char(c);
+    }
     match c {
-        '\x00' => KeyPress::NULL,
-        '\x01' => KeyPress::CTRL_A,
-        '\x02' => KeyPress::CTRL_B,
-        '\x03' => KeyPress::CTRL_C,
-        '\x04' => KeyPress::CTRL_D,
-        '\x05' => KeyPress::CTRL_E,
-        '\x06' => KeyPress::CTRL_F,
-        '\x07' => KeyPress::CTRL_G,
-        '\x08' => KeyPress::CTRL_H,
-        '\x09' => KeyPress::TAB,
-        '\x0a' => KeyPress::CTRL_J,
-        '\x0b' => KeyPress::CTRL_K,
-        '\x0c' => KeyPress::CTRL_L,
-        '\x0d' => KeyPress::ENTER,
-        '\x0e' => KeyPress::CTRL_N,
-        '\x10' => KeyPress::CTRL_P,
-        '\x12' => KeyPress::CTRL_R,
-        '\x13' => KeyPress::CTRL_S,
-        '\x14' => KeyPress::CTRL_T,
-        '\x15' => KeyPress::CTRL_U,
-        '\x16' => KeyPress::CTRL_V,
-        '\x17' => KeyPress::CTRL_W,
-        '\x19' => KeyPress::CTRL_Y,
-        '\x1a' => KeyPress::CTRL_Z,
-        '\x1b' => KeyPress::ESC,
-        '\x7f' => KeyPress::BACKSPACE,
-        _ => KeyPress::NULL,
+        '\x00' => KeyPress::Null,
+        '\x01' => KeyPress::Ctrl('A'),
+        '\x02' => KeyPress::Ctrl('B'),
+        '\x03' => KeyPress::Ctrl('C'),
+        '\x04' => KeyPress::Ctrl('D'),
+        '\x05' => KeyPress::Ctrl('E'),
+        '\x06' => KeyPress::Ctrl('F'),
+        '\x07' => KeyPress::Ctrl('G'),
+        '\x08' => KeyPress::Backspace,
+        '\x09' => KeyPress::Tab,
+        '\x0a' => KeyPress::Ctrl('J'),
+        '\x0b' => KeyPress::Ctrl('K'),
+        '\x0c' => KeyPress::Ctrl('L'),
+        '\x0d' => KeyPress::Enter,
+        '\x0e' => KeyPress::Ctrl('N'),
+        '\x10' => KeyPress::Ctrl('P'),
+        '\x12' => KeyPress::Ctrl('R'),
+        '\x13' => KeyPress::Ctrl('S'),
+        '\x14' => KeyPress::Ctrl('T'),
+        '\x15' => KeyPress::Ctrl('U'),
+        '\x16' => KeyPress::Ctrl('V'),
+        '\x17' => KeyPress::Ctrl('W'),
+        '\x19' => KeyPress::Ctrl('Y'),
+        '\x1a' => KeyPress::Ctrl('Z'),
+        '\x1b' => KeyPress::Esc,
+        '\x7f' => KeyPress::Backspace, // TODO Validate
+        _ => KeyPress::Null,
     }
 }
diff --git a/src/error.rs b/src/error.rs
index 2f1b259a59b717a70ce8b268070dc67224df3a71..e516a1957a25bc709d34323808933d893eac9a8d 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -1,4 +1,6 @@
 //! Contains error type for handling I/O and Errno errors
+#[cfg(windows)]
+use std::char;
 use std::io;
 use std::error;
 use std::fmt;
@@ -22,6 +24,10 @@ pub enum ReadlineError {
     /// Unix Error from syscall
     #[cfg(unix)]
     Errno(nix::Error),
+    #[cfg(windows)]
+    WindowResize,
+    #[cfg(windows)]
+    Decode(char::DecodeUtf16Error),
 }
 
 impl fmt::Display for ReadlineError {
@@ -33,6 +39,10 @@ impl fmt::Display for ReadlineError {
             ReadlineError::Interrupted => write!(f, "Interrupted"),
             #[cfg(unix)]
             ReadlineError::Errno(ref err) => write!(f, "Errno: {}", err.errno().desc()),
+            #[cfg(windows)]
+            ReadlineError::WindowResize => write!(f, "WindowResize"),
+            #[cfg(windows)]
+            ReadlineError::Decode(ref err) => err.fmt(f),
         }
     }
 }
@@ -46,6 +56,10 @@ impl error::Error for ReadlineError {
             ReadlineError::Interrupted => "Interrupted",
             #[cfg(unix)]
             ReadlineError::Errno(ref err) => err.errno().desc(),
+            #[cfg(windows)]
+            ReadlineError::WindowResize => "WindowResize",
+            #[cfg(windows)]
+            ReadlineError::Decode(ref err) => err.description(),
         }
     }
 }
@@ -63,15 +77,16 @@ impl From<nix::Error> for ReadlineError {
     }
 }
 
+#[cfg(unix)]
 impl From<char_iter::CharsError> for ReadlineError {
     fn from(err: char_iter::CharsError) -> ReadlineError {
         ReadlineError::Char(err)
     }
 }
 
-impl ReadlineError {
-    #[cfg(unix)]
-    pub fn from_errno(errno: nix::errno::Errno) -> ReadlineError {
-        ReadlineError::from(nix::Error::from_errno(errno))
+#[cfg(windows)]
+impl From<char::DecodeUtf16Error> for ReadlineError {
+    fn from(err: char::DecodeUtf16Error) -> ReadlineError {
+        ReadlineError::Decode(err)
     }
 }
diff --git a/src/history.rs b/src/history.rs
index 39667917f40233c1ee29dc04164ca1afe00133a6..8b28f24987f7772a76b29290c60d70c07ab4a743 100644
--- a/src/history.rs
+++ b/src/history.rs
@@ -20,7 +20,7 @@ impl History {
         History {
             entries: VecDeque::new(),
             max_len: DEFAULT_HISTORY_MAX_LEN,
-            ignore_space: true,
+            ignore_space: false,
             ignore_dups: true,
         }
     }
@@ -169,6 +169,7 @@ mod tests {
     #[test]
     fn add() {
         let mut history = super::History::new();
+        history.ignore_space(true);
         assert!(history.add("line1"));
         assert!(history.add("line2"));
         assert!(!history.add("line2"));
diff --git a/src/lib.rs b/src/lib.rs
index 03a0bd9b8c41f807bea7b78aeccabf0fc3fdb299..4912cbccf6c1a39862864ad162061e67ae981698 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -20,6 +20,10 @@ extern crate libc;
 extern crate nix;
 extern crate unicode_width;
 extern crate encode_unicode;
+#[cfg(windows)]
+extern crate winapi;
+#[cfg(windows)]
+extern crate kernel32;
 
 pub mod completion;
 #[allow(non_camel_case_types)]
@@ -33,20 +37,19 @@ mod char_iter;
 mod tty;
 
 use std::fmt;
-use std::io::{self, Write};
+use std::io::{self, Read, Write};
 use std::mem;
 use std::path::Path;
 use std::result;
 #[cfg(unix)]
 use std::sync;
 use std::sync::atomic;
-
 #[cfg(unix)]
 use nix::sys::signal;
+
 use encode_unicode::CharExt;
-use tty::Terminal;
 use completion::Completer;
-use consts::{KeyPress, char_to_key_press};
+use consts::KeyPress;
 use history::History;
 use line_buffer::{LineBuffer, MAX_LINE, WordAction};
 use kill_ring::KillRing;
@@ -62,8 +65,10 @@ struct State<'out, 'prompt> {
     line: LineBuffer, // Edited line buffer
     cursor: Position, // Cursor position (relative to the start of the prompt for `row`)
     cols: usize, // Number of columns in terminal
-    history_index: usize, // The history index we are currently editing.
+    old_rows: usize, // Number of rows used so far (from start of prompt to end of input)
+    history_index: usize, // The history index we are currently editing
     snapshot: LineBuffer, // Current edited line before history browsing/completion
+    output_handle: tty::Handle, // output handle (for windows)
 }
 
 #[derive(Copy, Clone, Debug, Default)]
@@ -74,11 +79,12 @@ struct Position {
 
 impl<'out, 'prompt> State<'out, 'prompt> {
     fn new(out: &'out mut Write,
+           output_handle: tty::Handle,
            prompt: &'prompt str,
-           capacity: usize,
-           cols: usize,
            history_index: usize)
            -> State<'out, 'prompt> {
+        let capacity = MAX_LINE;
+        let cols = tty::get_columns(output_handle);
         let prompt_size = calculate_position(prompt, Default::default(), cols);
         State {
             out: out,
@@ -87,8 +93,10 @@ impl<'out, 'prompt> State<'out, 'prompt> {
             line: LineBuffer::with_capacity(capacity),
             cursor: prompt_size,
             cols: cols,
+            old_rows: prompt_size.row,
             history_index: history_index,
             snapshot: LineBuffer::with_capacity(capacity),
+            output_handle: output_handle,
         }
     }
 
@@ -112,20 +120,29 @@ impl<'out, 'prompt> State<'out, 'prompt> {
         self.refresh(prompt, prompt_size)
     }
 
+    #[cfg(unix)]
     fn refresh(&mut self, prompt: &str, prompt_size: Position) -> Result<()> {
         use std::fmt::Write;
 
+        // calculate the position of the end of the input line
         let end_pos = calculate_position(&self.line, prompt_size, self.cols);
+        // calculate the desired position of the cursor
         let cursor = calculate_position(&self.line[..self.line.pos()], prompt_size, self.cols);
 
         let mut ab = String::new();
-        let cursor_row_movement = self.cursor.row - self.prompt_size.row;
-        // move the cursor up as required
+
+        let cursor_row_movement = self.old_rows - self.cursor.row;
+        // move the cursor down as required
         if cursor_row_movement > 0 {
-            write!(ab, "\x1b[{}A", cursor_row_movement).unwrap();
+            write!(ab, "\x1b[{}B", cursor_row_movement).unwrap();
         }
-        // position at the start of the prompt, clear to end of screen
-        ab.push_str("\r\x1b[J");
+        // clear old rows
+        for _ in 0..self.old_rows {
+            ab.push_str("\r\x1b[0K\x1b[1A");
+        }
+        // clear the line
+        ab.push_str("\r\x1b[0K");
+
         // display the prompt
         ab.push_str(prompt);
         // display the input line
@@ -148,9 +165,56 @@ impl<'out, 'prompt> State<'out, 'prompt> {
         }
 
         self.cursor = cursor;
+        self.old_rows = end_pos.row;
 
         write_and_flush(self.out, ab.as_bytes())
     }
+
+    #[cfg(windows)]
+    fn refresh(&mut self, prompt: &str, prompt_size: Position) -> Result<()> {
+        let handle = self.output_handle;
+        if cfg!(test) && handle.is_null() {
+            return Ok(());
+        }
+        // calculate the position of the end of the input line
+        let end_pos = calculate_position(&self.line, prompt_size, self.cols);
+        // calculate the desired position of the cursor
+        let cursor = calculate_position(&self.line[..self.line.pos()], prompt_size, self.cols);
+
+        // position at the start of the prompt, clear to end of previous input
+        let mut info = unsafe { mem::zeroed() };
+        check!(kernel32::GetConsoleScreenBufferInfo(handle, &mut info));
+        info.dwCursorPosition.X = 0;
+        info.dwCursorPosition.Y -= self.cursor.row as i16;
+        check!(kernel32::SetConsoleCursorPosition(handle, info.dwCursorPosition));
+        let mut _count = 0;
+        check!(kernel32::FillConsoleOutputCharacterA(handle,
+                                                 ' ' as winapi::CHAR,
+                                                 (info.dwSize.X * (self.old_rows as i16 +1)) as winapi::DWORD,
+                                                 info.dwCursorPosition,
+                                                 &mut _count));
+        let mut ab = String::new();
+        // display the prompt
+        ab.push_str(prompt); // TODO handle ansi escape code (SetConsoleTextAttribute)
+        // display the input line
+        ab.push_str(&self.line);
+        try!(write_and_flush(self.out, ab.as_bytes()));
+
+        // position the cursor
+        check!(kernel32::GetConsoleScreenBufferInfo(handle, &mut info));
+        info.dwCursorPosition.X = cursor.col as i16;
+        info.dwCursorPosition.Y -= (end_pos.row - cursor.row) as i16;
+        check!(kernel32::SetConsoleCursorPosition(handle, info.dwCursorPosition));
+
+        self.cursor = cursor;
+        self.old_rows = end_pos.row;
+
+        Ok(())
+    }
+
+    fn update_columns(&mut self) {
+        self.cols = tty::get_columns(self.output_handle);
+    }
 }
 
 impl<'out, 'prompt> fmt::Debug for State<'out, 'prompt> {
@@ -161,13 +225,13 @@ impl<'out, 'prompt> fmt::Debug for State<'out, 'prompt> {
             .field("buf", &self.line)
             .field("cursor", &self.cursor)
             .field("cols", &self.cols)
+            .field("old_rows", &self.old_rows)
             .field("history_index", &self.history_index)
             .field("snapshot", &self.snapshot)
             .finish()
     }
 }
 
-
 fn write_and_flush(w: &mut Write, buf: &[u8]) -> Result<()> {
     try!(w.write_all(buf));
     try!(w.flush());
@@ -175,8 +239,24 @@ fn write_and_flush(w: &mut Write, buf: &[u8]) -> Result<()> {
 }
 
 /// Clear the screen. Used to handle ctrl+l
-fn clear_screen(out: &mut Write) -> Result<()> {
-    write_and_flush(out, b"\x1b[H\x1b[2J")
+#[cfg(unix)]
+fn clear_screen(s: &mut State) -> Result<()> {
+    write_and_flush(s.out, b"\x1b[H\x1b[2J")
+}
+#[cfg(windows)]
+fn clear_screen(s: &mut State) -> Result<()> {
+    let handle = s.output_handle;
+    let mut info = unsafe { mem::zeroed() };
+    check!(kernel32::GetConsoleScreenBufferInfo(handle, &mut info));
+    let coord = winapi::COORD { X: 0, Y: 0 };
+    check!(kernel32::SetConsoleCursorPosition(handle, coord));
+    let mut _count = 0;
+    check!(kernel32::FillConsoleOutputCharacterA(handle,
+                                                 ' ' as winapi::CHAR,
+                                                 (info.dwSize.X * info.dwSize.Y) as winapi::DWORD,
+                                                 coord,
+                                                 &mut _count));
+    Ok(())
 }
 
 /// Beep, used for completion when there is nothing to complete or when all
@@ -244,6 +324,8 @@ fn edit_insert(s: &mut State, ch: char) -> Result<()> {
         if push {
             if s.cursor.col + unicode_width::UnicodeWidthChar::width(ch).unwrap_or(0) < s.cols {
                 // Avoid a full update of the line in the trivial case.
+                let cursor = calculate_position(&s.line[..s.line.pos()], s.prompt_size, s.cols);
+                s.cursor = cursor;
                 write_and_flush(s.out, ch.to_utf8().as_bytes())
             } else {
                 s.refresh_line()
@@ -437,15 +519,42 @@ fn edit_history_next(s: &mut State, history: &History, prev: bool) -> Result<()>
     } else {
         // Restore current edited line
         s.snapshot();
-    };
+    }
+    s.refresh_line()
+}
+
+/// Substitute the currently edited line with the first/last history entry.
+fn edit_history(s: &mut State, history: &History, first: bool) -> Result<()> {
+    if history.is_empty() {
+        return Ok(());
+    }
+    if s.history_index == history.len() {
+        if first {
+            // Save the current edited line before to overwrite it
+            s.snapshot();
+        } else {
+            return Ok(());
+        }
+    } else if s.history_index == 0 && first {
+        return Ok(());
+    }
+    if first {
+        s.history_index = 0;
+        let buf = history.get(s.history_index).unwrap();
+        s.line.update(buf, buf.len());
+    } else {
+        s.history_index = history.len();
+        // Restore current edited line
+        s.snapshot();
+    }
     s.refresh_line()
 }
 
 /// Completes the line/word
-fn complete_line<R: io::Read>(chars: &mut char_iter::Chars<R>,
-                              s: &mut State,
-                              completer: &Completer)
-                              -> Result<Option<char>> {
+fn complete_line<R: Read>(rdr: &mut tty::RawReader<R>,
+                          s: &mut State,
+                          completer: &Completer)
+                          -> Result<Option<KeyPress>> {
     let (start, candidates) = try!(completer.complete(&s.line, s.line.pos()));
     if candidates.is_empty() {
         try!(beep());
@@ -453,7 +562,7 @@ fn complete_line<R: io::Read>(chars: &mut char_iter::Chars<R>,
     } else {
         // Save the current edited line before to overwrite it
         s.backup();
-        let mut ch;
+        let mut key;
         let mut i = 0;
         loop {
             // Show completion or original buffer
@@ -467,16 +576,15 @@ fn complete_line<R: io::Read>(chars: &mut char_iter::Chars<R>,
                 s.snapshot();
             }
 
-            ch = try!(chars.next().unwrap());
-            let key = char_to_key_press(ch);
+            key = try!(rdr.next_key(false));
             match key {
-                KeyPress::TAB => {
+                KeyPress::Tab => {
                     i = (i + 1) % (candidates.len() + 1); // Circular
                     if i == candidates.len() {
                         try!(beep());
                     }
                 }
-                KeyPress::ESC => {
+                KeyPress::Esc => {
                     // Re-show original buffer
                     s.snapshot();
                     if i < candidates.len() {
@@ -489,16 +597,19 @@ fn complete_line<R: io::Read>(chars: &mut char_iter::Chars<R>,
                 }
             }
         }
-        Ok(Some(ch))
+        Ok(Some(key))
     }
 }
 
 /// Incremental search
 #[cfg_attr(feature="clippy", allow(if_not_else))]
-fn reverse_incremental_search<R: io::Read>(chars: &mut char_iter::Chars<R>,
-                                           s: &mut State,
-                                           history: &History)
-                                           -> Result<Option<KeyPress>> {
+fn reverse_incremental_search<R: Read>(rdr: &mut tty::RawReader<R>,
+                                       s: &mut State,
+                                       history: &History)
+                                       -> Result<Option<KeyPress>> {
+    if history.is_empty() {
+        return Ok(None);
+    }
     // Save the current edited line (and cursor position) before to overwrite it
     s.snapshot();
 
@@ -507,7 +618,6 @@ fn reverse_incremental_search<R: io::Read>(chars: &mut char_iter::Chars<R>,
     let mut reverse = true;
     let mut success = true;
 
-    let mut ch;
     let mut key;
     // Display the reverse-i-search prompt and process chars
     loop {
@@ -518,20 +628,17 @@ fn reverse_incremental_search<R: io::Read>(chars: &mut char_iter::Chars<R>,
         };
         try!(s.refresh_prompt_and_line(&prompt));
 
-        ch = try!(chars.next().unwrap());
-        if !ch.is_control() {
-            search_buf.push(ch);
+        key = try!(rdr.next_key(true));
+        if let KeyPress::Char(c) = key {
+            search_buf.push(c);
         } else {
-            key = char_to_key_press(ch);
-            if key == KeyPress::ESC {
-                key = try!(escape_sequence(chars));
-            }
             match key {
-                KeyPress::CTRL_H | KeyPress::BACKSPACE => {
+                KeyPress::Ctrl('H') |
+                KeyPress::Backspace => {
                     search_buf.pop();
                     continue;
                 }
-                KeyPress::CTRL_R => {
+                KeyPress::Ctrl('R') => {
                     reverse = true;
                     if history_idx > 0 {
                         history_idx -= 1;
@@ -540,7 +647,7 @@ fn reverse_incremental_search<R: io::Read>(chars: &mut char_iter::Chars<R>,
                         continue;
                     }
                 }
-                KeyPress::CTRL_S => {
+                KeyPress::Ctrl('S') => {
                     reverse = false;
                     if history_idx < history.len() - 1 {
                         history_idx += 1;
@@ -549,7 +656,7 @@ fn reverse_incremental_search<R: io::Read>(chars: &mut char_iter::Chars<R>,
                         continue;
                     }
                 }
-                KeyPress::CTRL_G => {
+                KeyPress::Ctrl('G') => {
                     // Restore current edited line (before search)
                     s.snapshot();
                     try!(s.refresh_line());
@@ -572,146 +679,82 @@ fn reverse_incremental_search<R: io::Read>(chars: &mut char_iter::Chars<R>,
     Ok(Some(key))
 }
 
-fn escape_sequence<R: io::Read>(chars: &mut char_iter::Chars<R>) -> Result<KeyPress> {
-    // Read the next two bytes representing the escape sequence.
-    let seq1 = try!(chars.next().unwrap());
-    if seq1 == '[' {
-        // ESC [ sequences.
-        let seq2 = try!(chars.next().unwrap());
-        if seq2.is_digit(10) {
-            // Extended escape, read additional byte.
-            let seq3 = try!(chars.next().unwrap());
-            if seq3 == '~' {
-                match seq2 {
-                    '3' => Ok(KeyPress::ESC_SEQ_DELETE),
-                    // TODO '1' // Home
-                    // TODO '4' // End
-                    _ => Ok(KeyPress::UNKNOWN_ESC_SEQ),
-                }
-            } else {
-                Ok(KeyPress::UNKNOWN_ESC_SEQ)
-            }
-        } else {
-            match seq2 {
-                'A' => Ok(KeyPress::CTRL_P), // Up
-                'B' => Ok(KeyPress::CTRL_N), // Down
-                'C' => Ok(KeyPress::CTRL_F), // Right
-                'D' => Ok(KeyPress::CTRL_B), // Left
-                'F' => Ok(KeyPress::CTRL_E), // End
-                'H' => Ok(KeyPress::CTRL_A), // Home
-                _ => Ok(KeyPress::UNKNOWN_ESC_SEQ),
-            }
-        }
-    } else if seq1 == 'O' {
-        // ESC O sequences.
-        let seq2 = try!(chars.next().unwrap());
-        match seq2 {
-            'F' => Ok(KeyPress::CTRL_E),
-            'H' => Ok(KeyPress::CTRL_A),
-            _ => Ok(KeyPress::UNKNOWN_ESC_SEQ),
-        }
-    } else {
-        // TODO ESC-N (n): search history forward not interactively
-        // TODO ESC-P (p): search history backward not interactively
-        // TODO ESC-R (r): Undo all changes made to this line.
-        // TODO ESC-<: move to first entry in history
-        // TODO ESC->: move to last entry in history
-        match seq1 {
-            'b' | 'B' => Ok(KeyPress::ESC_B),
-            'c' | 'C' => Ok(KeyPress::ESC_C),
-            'd' | 'D' => Ok(KeyPress::ESC_D),
-            'f' | 'F' => Ok(KeyPress::ESC_F),
-            'l' | 'L' => Ok(KeyPress::ESC_L),
-            't' | 'T' => Ok(KeyPress::ESC_T),
-            'u' | 'U' => Ok(KeyPress::ESC_U),
-            'y' | 'Y' => Ok(KeyPress::ESC_Y),
-            '\x08' | '\x7f' => Ok(KeyPress::ESC_BACKSPACE),
-            _ => {
-                writeln!(io::stderr(), "key: {:?}, seq1, {:?}", KeyPress::ESC, seq1).unwrap();
-                Ok(KeyPress::UNKNOWN_ESC_SEQ)
-            }
-        }
-    }
-}
-
 /// Handles reading and editting the readline buffer.
 /// It will also handle special inputs in an appropriate fashion
 /// (e.g., C-c will exit readline)
 #[cfg_attr(feature="clippy", allow(cyclomatic_complexity))]
-fn readline_edit<T: tty::Terminal>(prompt: &str,
+fn readline_edit(prompt: &str,
                  history: &mut History,
                  completer: Option<&Completer>,
                  kill_ring: &mut KillRing,
-                 mut term: T)
+                 original_mode: tty::Mode)
                  -> Result<String> {
     let mut stdout = io::stdout();
-    try!(write_and_flush(&mut stdout, prompt.as_bytes()));
+    let stdout_handle = try!(tty::stdout_handle());
 
     kill_ring.reset();
-    let mut s = State::new(&mut stdout, prompt, MAX_LINE, tty::get_columns(), history.len());
-    let stdin = io::stdin();
-    let mut chars = char_iter::chars(stdin.lock());
+    let mut s = State::new(&mut stdout, stdout_handle, prompt, history.len());
+    try!(s.refresh_line());
+
+    let mut rdr = try!(tty::RawReader::new(io::stdin()));
+
     loop {
-        let c = chars.next().unwrap();
-        if c.is_err() && SIGWINCH.compare_and_swap(true, false, atomic::Ordering::SeqCst) {
-            s.cols = tty::get_columns();
+        let rk = rdr.next_key(true);
+        if rk.is_err() && SIGWINCH.compare_and_swap(true, false, atomic::Ordering::SeqCst) {
+            s.update_columns();
             try!(s.refresh_line());
             continue;
         }
-        let mut ch = try!(c);
-        if !ch.is_control() {
+        let mut key = try!(rk);
+        if let KeyPress::Char(c) = key {
             kill_ring.reset();
-            try!(edit_insert(&mut s, ch));
+            try!(edit_insert(&mut s, c));
             continue;
         }
 
-        let mut key = char_to_key_press(ch);
         // autocomplete
-        if key == KeyPress::TAB && completer.is_some() {
-            let next = try!(complete_line(&mut chars, &mut s, completer.unwrap()));
+        if key == KeyPress::Tab && completer.is_some() {
+            let next = try!(complete_line(&mut rdr, &mut s, completer.unwrap()));
             if next.is_some() {
                 kill_ring.reset();
-                ch = next.unwrap();
-                if !ch.is_control() {
-                    try!(edit_insert(&mut s, ch));
+                key = next.unwrap();
+                if let KeyPress::Char(c) = key {
+                    try!(edit_insert(&mut s, c));
                     continue;
                 }
-                key = char_to_key_press(ch);
             } else {
                 continue;
             }
-        } else if key == KeyPress::CTRL_R {
+        } else if key == KeyPress::Ctrl('R') {
             // Search history backward
-            let next = try!(reverse_incremental_search(&mut chars, &mut s, history));
+            let next = try!(reverse_incremental_search(&mut rdr, &mut s, history));
             if next.is_some() {
                 key = next.unwrap();
             } else {
                 continue;
             }
-        } else if key == KeyPress::ESC {
-            // escape sequence
-            key = try!(escape_sequence(&mut chars));
-            if key == KeyPress::UNKNOWN_ESC_SEQ {
-                continue;
-            }
+        } else if key == KeyPress::UNKNOWN_ESC_SEQ {
+            continue;
         }
 
         match key {
-            KeyPress::CTRL_A => {
+            KeyPress::Ctrl('A') |
+            KeyPress::Home => {
                 kill_ring.reset();
                 // Move to the beginning of line.
                 try!(edit_move_home(&mut s))
             }
-            KeyPress::CTRL_B => {
+            KeyPress::Ctrl('B') |
+            KeyPress::Left => {
                 kill_ring.reset();
                 // Move back a character.
                 try!(edit_move_left(&mut s))
             }
-            KeyPress::CTRL_C => {
+            KeyPress::Ctrl('C') => {
                 kill_ring.reset();
                 return Err(error::ReadlineError::Interrupted);
             }
-            KeyPress::CTRL_D => {
+            KeyPress::Ctrl('D') => {
                 kill_ring.reset();
                 if s.line.is_empty() {
                     return Err(error::ReadlineError::Eof);
@@ -720,87 +763,94 @@ fn readline_edit<T: tty::Terminal>(prompt: &str,
                     try!(edit_delete(&mut s))
                 }
             }
-            KeyPress::CTRL_E => {
+            KeyPress::Ctrl('E') |
+            KeyPress::End => {
                 kill_ring.reset();
                 // Move to the end of line.
                 try!(edit_move_end(&mut s))
             }
-            KeyPress::CTRL_F => {
+            KeyPress::Ctrl('F') |
+            KeyPress::Right => {
                 kill_ring.reset();
                 // Move forward a character.
                 try!(edit_move_right(&mut s))
             }
-            KeyPress::CTRL_H | KeyPress::BACKSPACE => {
+            KeyPress::Ctrl('H') |
+            KeyPress::Backspace => {
                 kill_ring.reset();
                 // Delete one character backward.
                 try!(edit_backspace(&mut s))
             }
-            KeyPress::CTRL_K => {
+            KeyPress::Ctrl('K') => {
                 // Kill the text from point to the end of the line.
                 if let Some(text) = try!(edit_kill_line(&mut s)) {
                     kill_ring.kill(&text, true)
                 }
             }
-            KeyPress::CTRL_L => {
+            KeyPress::Ctrl('L') => {
                 // Clear the screen leaving the current line at the top of the screen.
-                try!(clear_screen(s.out));
+                try!(clear_screen(&mut s));
                 try!(s.refresh_line())
             }
-            KeyPress::CTRL_N => {
+            KeyPress::Ctrl('N') |
+            KeyPress::Down => {
                 kill_ring.reset();
                 // Fetch the next command from the history list.
                 try!(edit_history_next(&mut s, history, false))
             }
-            KeyPress::CTRL_P => {
+            KeyPress::Ctrl('P') |
+            KeyPress::Up => {
                 kill_ring.reset();
                 // Fetch the previous command from the history list.
                 try!(edit_history_next(&mut s, history, true))
             }
-            KeyPress::CTRL_T => {
+            KeyPress::Ctrl('T') => {
                 kill_ring.reset();
                 // Exchange the char before cursor with the character at cursor.
                 try!(edit_transpose_chars(&mut s))
             }
-            KeyPress::CTRL_U => {
+            KeyPress::Ctrl('U') => {
                 // Kill backward from point to the beginning of the line.
                 if let Some(text) = try!(edit_discard_line(&mut s)) {
                     kill_ring.kill(&text, false)
                 }
             }
-            KeyPress::CTRL_V => {
+            #[cfg(unix)]
+            KeyPress::Ctrl('V') => {
                 // Quoted insert
                 kill_ring.reset();
-                let c = chars.next().unwrap();
-                let ch = try!(c);
-                try!(edit_insert(&mut s, ch))
+                let c = try!(rdr.next_char());
+                try!(edit_insert(&mut s, c)) // FIXME
             }
-            KeyPress::CTRL_W => {
+            KeyPress::Ctrl('W') => {
                 // Kill the word behind point, using white space as a word boundary
                 if let Some(text) = try!(edit_delete_prev_word(&mut s, char::is_whitespace)) {
                     kill_ring.kill(&text, false)
                 }
             }
-            KeyPress::CTRL_Y => {
+            KeyPress::Ctrl('Y') => {
                 // retrieve (yank) last item killed
                 if let Some(text) = kill_ring.yank() {
                     try!(edit_yank(&mut s, text))
                 }
             }
             #[cfg(unix)]
-            KeyPress::CTRL_Z => {
-                try!(term.disable_raw_mode());
+            KeyPress::Ctrl('Z') => {
+                try!(tty::disable_raw_mode(original_mode));
                 try!(signal::raise(signal::SIGSTOP));
-                try!(term.enable_raw_mode()); // TODO term may have changed
+                try!(tty::enable_raw_mode()); // TODO original_mode may have changed
                 try!(s.refresh_line())
             }
             // TODO CTRL-_ // undo
-            KeyPress::ENTER | KeyPress::CTRL_J => {
+            KeyPress::Enter |
+            KeyPress::Ctrl('J') => {
                 // Accept the line regardless of where the cursor is.
                 kill_ring.reset();
                 try!(edit_move_end(&mut s));
                 break;
             }
-            KeyPress::ESC_BACKSPACE => {
+            KeyPress::Meta('\x08') |
+            KeyPress::Meta('\x7f') => {
                 // kill one word backward
                 // Kill from the cursor to the start of the current word, or, if between words, to the start of the previous word.
                 if let Some(text) = try!(edit_delete_prev_word(&mut s,
@@ -808,62 +858,81 @@ fn readline_edit<T: tty::Terminal>(prompt: &str,
                     kill_ring.kill(&text, false)
                 }
             }
-            KeyPress::ESC_B => {
+            KeyPress::Meta('<') => {
+                // move to first entry in history
+                kill_ring.reset();
+                try!(edit_history(&mut s, history, true))
+            }
+            KeyPress::Meta('>') => {
+                // move to last entry in history
+                kill_ring.reset();
+                try!(edit_history(&mut s, history, false))
+            }
+            KeyPress::Meta('B') => {
                 // move backwards one word
                 kill_ring.reset();
                 try!(edit_move_to_prev_word(&mut s))
             }
-            KeyPress::ESC_C => {
+            KeyPress::Meta('C') => {
                 // capitalize word after point
                 kill_ring.reset();
                 try!(edit_word(&mut s, WordAction::CAPITALIZE))
             }
-            KeyPress::ESC_D => {
+            KeyPress::Meta('D') => {
                 // kill one word forward
                 if let Some(text) = try!(edit_delete_word(&mut s)) {
                     kill_ring.kill(&text, true)
                 }
             }
-            KeyPress::ESC_F => {
+            KeyPress::Meta('F') => {
                 // move forwards one word
                 kill_ring.reset();
                 try!(edit_move_to_next_word(&mut s))
             }
-            KeyPress::ESC_L => {
+            KeyPress::Meta('L') => {
                 // lowercase word after point
                 kill_ring.reset();
                 try!(edit_word(&mut s, WordAction::LOWERCASE))
             }
-            KeyPress::ESC_T => {
+            KeyPress::Meta('T') => {
                 // transpose words
                 kill_ring.reset();
                 try!(edit_transpose_words(&mut s))
             }
-            KeyPress::ESC_U => {
+            KeyPress::Meta('U') => {
                 // uppercase word after point
                 kill_ring.reset();
                 try!(edit_word(&mut s, WordAction::UPPERCASE))
             }
-            KeyPress::ESC_Y => {
+            KeyPress::Meta('Y') => {
                 // yank-pop
                 if let Some((yank_size, text)) = kill_ring.yank_pop() {
                     try!(edit_yank_pop(&mut s, yank_size, text))
                 }
             }
-            KeyPress::ESC_SEQ_DELETE => {
+            KeyPress::Delete => {
                 kill_ring.reset();
                 try!(edit_delete(&mut s))
             }
             _ => {
                 kill_ring.reset();
-                // Insert the character typed.
-                try!(edit_insert(&mut s, ch))
+                // Ignore the character typed.
             }
         }
     }
     Ok(s.line.into_string())
 }
 
+struct Guard(tty::Mode);
+
+#[allow(unused_must_use)]
+impl Drop for Guard {
+    fn drop(&mut self) {
+        let Guard(mode) = *self;
+        tty::disable_raw_mode(mode);
+    }
+}
+
 /// Readline method that will enable RAW mode, call the `readline_edit()`
 /// method and disable raw mode
 fn readline_raw(prompt: &str,
@@ -871,9 +940,10 @@ fn readline_raw(prompt: &str,
                 completer: Option<&Completer>,
                 kill_ring: &mut KillRing)
                 -> Result<String> {
-    let mut term = tty::get_terminal();
-    try!(term.enable_raw_mode());
-    let user_input = readline_edit(prompt, history, completer, kill_ring, term);
+    let original_mode = try!(tty::enable_raw_mode());
+    let guard = Guard(original_mode);
+    let user_input = readline_edit(prompt, history, completer, kill_ring, original_mode);
+    drop(guard); // try!(disable_raw_mode(original_mode));
     println!("");
     user_input
 }
@@ -904,8 +974,8 @@ impl<C> Editor<C> {
         // if the number of columns is stored here, we need a SIGWINCH handler...
         let editor = Editor {
             unsupported_term: tty::is_unsupported_term(),
-            stdin_isatty: tty::is_a_tty(tty::StandardStream::StdIn),
-            stdout_isatty: tty::is_a_tty(tty::StandardStream::StdOut),
+            stdin_isatty: tty::is_a_tty(tty::STDIN_FILENO),
+            stdout_isatty: tty::is_a_tty(tty::STDOUT_FILENO),
             history: History::new(),
             completer: None,
             kill_ring: KillRing::new(60),
@@ -916,13 +986,13 @@ impl<C> Editor<C> {
         editor
     }
 
-    pub fn history_ignore_space(mut self, yes: bool) -> Editor<C> {
-        self.history.ignore_space(yes);
+    pub fn history_ignore_dups(mut self, yes: bool) -> Editor<C> {
+        self.history.ignore_dups(yes);
         self
     }
 
-    pub fn history_ignore_dups(mut self, yes: bool) -> Editor<C> {
-        self.history.ignore_dups(yes);
+    pub fn history_ignore_space(mut self, yes: bool) -> Editor<C> {
+        self.history.ignore_space(yes);
         self
     }
 
@@ -1006,25 +1076,37 @@ fn install_sigwinch_handler() {
         let _ = signal::sigaction(signal::SIGWINCH, &sigwinch);
     });
 }
-
-// no-op on windows
-#[cfg(windows)]
-fn install_sigwinch_handler() {
-}
-
 #[cfg(unix)]
 extern "C" fn sigwinch_handler(_: signal::SigNum) {
     SIGWINCH.store(true, atomic::Ordering::SeqCst);
 }
+#[cfg(windows)]
+fn install_sigwinch_handler() {
+    // See ReadConsoleInputW && WINDOW_BUFFER_SIZE_EVENT
+}
 
 #[cfg(test)]
 mod test {
     use std::io::Write;
     use line_buffer::LineBuffer;
     use history::History;
+    #[cfg(unix)]
     use completion::Completer;
+    #[cfg(unix)]
+    use consts::KeyPress;
     use State;
     use super::Result;
+    use tty::{Handle, RawReader};
+
+    #[cfg(unix)]
+    fn default_handle() -> Handle {
+        ()
+    }
+    #[cfg(windows)]
+    fn default_handle() -> Handle {
+        ::std::ptr::null_mut()
+        // super::get_std_handle(super::STDOUT_FILENO).expect("Valid stdout")
+    }
 
     fn init_state<'out>(out: &'out mut Write,
                         line: &str,
@@ -1038,8 +1120,10 @@ mod test {
             line: LineBuffer::init(line, pos),
             cursor: Default::default(),
             cols: cols,
+            old_rows: 0,
             history_index: 0,
             snapshot: LineBuffer::with_capacity(100),
+            output_handle: default_handle(),
         }
     }
 
@@ -1081,7 +1165,9 @@ mod test {
         assert_eq!(line, s.line.as_str());
     }
 
+    #[cfg(unix)]
     struct SimpleCompleter;
+    #[cfg(unix)]
     impl Completer for SimpleCompleter {
         fn complete(&self, line: &str, _pos: usize) -> Result<(usize, Vec<String>)> {
             Ok((0, vec![line.to_string() + "t"]))
@@ -1089,14 +1175,15 @@ mod test {
     }
 
     #[test]
+    #[cfg(unix)]
     fn complete_line() {
         let mut out = ::std::io::sink();
         let mut s = init_state(&mut out, "rus", 3, 80);
         let input = b"\n";
-        let mut chars = ::char_iter::chars(&input[..]);
+        let mut rdr = RawReader::new(&input[..]).unwrap();
         let completer = SimpleCompleter;
-        let ch = super::complete_line(&mut chars, &mut s, &completer).unwrap();
-        assert_eq!(Some('\n'), ch);
+        let key = super::complete_line(&mut rdr, &mut s, &completer).unwrap();
+        assert_eq!(Some(KeyPress::Ctrl('J')), key);
         assert_eq!("rust", s.line.as_str());
         assert_eq!(4, s.line.pos());
     }
diff --git a/src/tty/mod.rs b/src/tty/mod.rs
index 72cc3f368b50b8701dce469782a26ea0db95cadd..3a15003c03894e4571346db022b1727d45997893 100644
--- a/src/tty/mod.rs
+++ b/src/tty/mod.rs
@@ -1,33 +1,14 @@
 //! This module implements and describes common TTY methods & traits
 extern crate libc;
-use super::Result;
 
-// If on Windows platform import Windows TTY module 
+// If on Windows platform import Windows TTY module
 // and re-export into mod.rs scope
-#[cfg(windows)] mod windows;
-#[cfg(windows)] pub use self::windows::*;
+#[cfg(windows)]mod windows;
+#[cfg(windows)]
+pub use self::windows::*;
 
-// If on Unix platform import Unix TTY module 
+// If on Unix platform import Unix TTY module
 // and re-export into mod.rs scope
-#[cfg(unix)] mod unix;
-#[cfg(unix)] pub use self::unix::*;
-
-/// Trait that should be for each TTY/Terminal on various platforms
-/// (e.g. unix & windows)
-pub trait Terminal {
-    /// Enable RAW mode for the terminal
-    fn enable_raw_mode(&mut self) -> Result<()>;
-
-    /// Disable RAW mode for the terminal
-    fn disable_raw_mode(&self) -> Result<()>;
-}
-
-/// Enum for Standard Streams 
-///
-/// libc::STDIN_FILENO/STDOUT_FILENO/STDERR_FILENO is not defined for the
-/// Windows platform.  We will use this enum instead when calling isatty
-/// function
-pub enum StandardStream {
-    StdIn,
-    StdOut,
-}
+#[cfg(unix)]mod unix;
+#[cfg(unix)]
+pub use self::unix::*;
diff --git a/src/tty/unix.rs b/src/tty/unix.rs
index 2f3d99409887dfe9715ce1eddc814cd5632ec2d8..fceb3e85ca80cffcfe74d832b0008880aac4b195 100644
--- a/src/tty/unix.rs
+++ b/src/tty/unix.rs
@@ -2,13 +2,18 @@ extern crate nix;
 extern crate libc;
 
 use std;
+use std::io::Read;
 use nix::sys::termios;
-use nix::errno::Errno;
-use super::Terminal;
-use super::StandardStream;
+use char_iter;
+use consts::{self, KeyPress};
 use ::Result;
 use ::error;
 
+pub type Handle = ();
+pub type Mode = termios::Termios;
+pub const STDIN_FILENO: libc::c_int = libc::STDIN_FILENO;
+pub const STDOUT_FILENO: libc::c_int = libc::STDOUT_FILENO;
+
 /// Unsupported Terminals that don't support RAW mode
 static UNSUPPORTED_TERM: [&'static str; 3] = ["dumb", "cons25", "emacs"];
 
@@ -23,7 +28,7 @@ const TIOCGWINSZ: libc::c_int = 0x5413;
 
 /// Try to get the number of columns in the current terminal,
 /// or assume 80 if it fails.
-pub fn get_columns() -> usize {
+pub fn get_columns(_: Handle) -> usize {
     use std::mem::zeroed;
     use libc::c_ushort;
     use libc;
@@ -38,18 +43,13 @@ pub fn get_columns() -> usize {
         }
 
         let mut size: winsize = zeroed();
-        match libc::ioctl(libc::STDOUT_FILENO, TIOCGWINSZ, &mut size) {
+        match libc::ioctl(STDOUT_FILENO, TIOCGWINSZ, &mut size) {
             0 => size.ws_col as usize, // TODO getCursorPosition
             _ => 80,
         }
     }
 }
 
-/// Get UnixTerminal struct
-pub fn get_terminal() -> UnixTerminal {
-    UnixTerminal{ original_termios: None }
-}
-
 /// Check TERM environment variable to see if current term is in our
 /// unsupported list
 pub fn is_unsupported_term() -> bool {
@@ -68,62 +68,136 @@ pub fn is_unsupported_term() -> bool {
 
 
 /// Return whether or not STDIN, STDOUT or STDERR is a TTY
-pub fn is_a_tty(stream: StandardStream) -> bool {
-    extern crate libc;
+pub fn is_a_tty(fd: libc::c_int) -> bool {
+    unsafe { libc::isatty(fd) != 0 }
+}
 
-    let fd = match stream {
-            StandardStream::StdIn => libc::STDIN_FILENO,
-            StandardStream::StdOut => libc::STDOUT_FILENO,
-        };
+/// Enable raw mode for the TERM
+pub fn enable_raw_mode() -> Result<Mode> {
+    use nix::errno::Errno::ENOTTY;
+    use nix::sys::termios::{BRKINT, CS8, ECHO, ICANON, ICRNL, IEXTEN, INPCK, ISIG, ISTRIP, IXON,
+                            /* OPOST, */ VMIN, VTIME};
+    if !is_a_tty(STDIN_FILENO) {
+        try!(Err(nix::Error::from_errno(ENOTTY)));
+    }
+    let original_mode = try!(termios::tcgetattr(STDIN_FILENO));
+    let mut raw = original_mode;
+    // disable BREAK interrupt, CR to NL conversion on input,
+    // input parity check, strip high bit (bit 8), output flow control
+    raw.c_iflag = raw.c_iflag & !(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+    // we don't want raw output, it turns newlines into straight linefeeds
+    // raw.c_oflag = raw.c_oflag & !(OPOST); // disable all output processing
+    raw.c_cflag = raw.c_cflag | (CS8); // character-size mark (8 bits)
+    // disable echoing, canonical mode, extended input processing and signals
+    raw.c_lflag = raw.c_lflag & !(ECHO | ICANON | IEXTEN | ISIG);
+    raw.c_cc[VMIN] = 1; // One character-at-a-time input
+    raw.c_cc[VTIME] = 0; // with blocking read
+    try!(termios::tcsetattr(STDIN_FILENO, termios::TCSAFLUSH, &raw));
+    Ok(original_mode)
+}
 
-    unsafe { libc::isatty(fd) != 0 }
+/// Disable Raw mode for the term
+pub fn disable_raw_mode(original_mode: Mode) -> Result<()> {
+    try!(termios::tcsetattr(STDIN_FILENO, termios::TCSAFLUSH, &original_mode));
+    Ok(())
+}
+
+pub fn stdout_handle() -> Result<Handle> {
+    Ok(())
 }
 
-/// Structure that will contain the original termios before enabling RAW mode
-pub struct UnixTerminal {
-    original_termios: Option<termios::Termios>
+/// Console input reader
+pub struct RawReader<R> {
+    chars: char_iter::Chars<R>,
 }
 
-impl Terminal for UnixTerminal {
-    /// Enable raw mode for the TERM
-    fn enable_raw_mode(&mut self) -> Result<()> {
-        use nix::sys::termios::{BRKINT, CS8, ECHO, ICANON, ICRNL, IEXTEN, INPCK, ISIG, ISTRIP, IXON,
-                                /*OPOST, */VMIN, VTIME};
-        if !is_a_tty(StandardStream::StdIn) {
-            return Err(error::ReadlineError::from_errno(Errno::ENOTTY));
+impl<R: Read> RawReader<R> {
+    pub fn new(stdin: R) -> Result<RawReader<R>> {
+        Ok(RawReader { chars: char_iter::chars(stdin) })
+    }
+
+    // As there is no read timeout to properly handle single ESC key,
+    // we make possible to deactivate escape sequence processing.
+    pub fn next_key(&mut self, esc_seq: bool) -> Result<KeyPress> {
+        let c = try!(self.next_char());
+
+        let mut key = consts::char_to_key_press(c);
+        if esc_seq && key == KeyPress::Esc {
+            // escape sequence
+            key = try!(self.escape_sequence());
         }
-        let original_termios = try!(termios::tcgetattr(libc::STDIN_FILENO));
-        let mut raw = original_termios; 
-        // disable BREAK interrupt, CR to NL conversion on input, 
-        // input parity check, strip high bit (bit 8), output flow control
-        raw.c_iflag = raw.c_iflag & !(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
-        // we don't want raw output, it turns newlines into straight linefeeds
-        //raw.c_oflag = raw.c_oflag & !(OPOST); // disable all output processing    
-        raw.c_cflag = raw.c_cflag | (CS8); // character-size mark (8 bits)
-        // disable echoing, canonical mode, extended input processing and signals
-        raw.c_lflag = raw.c_lflag & !(ECHO | ICANON | IEXTEN | ISIG);
-        raw.c_cc[VMIN] = 1; // One character-at-a-time input
-        raw.c_cc[VTIME] = 0; // with blocking read
-        try!(termios::tcsetattr(libc::STDIN_FILENO, termios::TCSAFLUSH, &raw));
-
-        // Set the original terminal to the struct field
-        self.original_termios = Some(original_termios);
-        Ok(())
+        Ok(key)
     }
 
-    /// Disable Raw mode for the term
-    fn disable_raw_mode(&self) -> Result<()> {
-        try!(termios::tcsetattr(libc::STDIN_FILENO,
-                                termios::TCSAFLUSH,
-                                &self.original_termios.expect("RAW MODE was not enabled previously")));
-        Ok(())
+    pub fn next_char(&mut self) -> Result<char> {
+        match self.chars.next() {
+            Some(c) => {
+                Ok(try!(c)) // TODO SIGWINCH
+            }
+            None => Err(error::ReadlineError::Eof),
+        }
     }
-}
 
-/// Ensure that RAW mode is disabled even in the case of a panic!
-#[allow(unused_must_use)]
-impl Drop for UnixTerminal {
-    fn drop(&mut self) {
-        self.disable_raw_mode();
+    fn escape_sequence(&mut self) -> Result<KeyPress> {
+        // Read the next two bytes representing the escape sequence.
+        let seq1 = try!(self.next_char());
+        if seq1 == '[' {
+            // ESC [ sequences.
+            let seq2 = try!(self.next_char());
+            if seq2.is_digit(10) {
+                // Extended escape, read additional byte.
+                let seq3 = try!(self.next_char());
+                if seq3 == '~' {
+                    match seq2 {
+                        '3' => Ok(KeyPress::Delete),
+                        // TODO '1' // Home
+                        // TODO '4' // End
+                        _ => Ok(KeyPress::UNKNOWN_ESC_SEQ),
+                    }
+                } else {
+                    Ok(KeyPress::UNKNOWN_ESC_SEQ)
+                }
+            } else {
+                match seq2 {
+                    'A' => Ok(KeyPress::Up),
+                    'B' => Ok(KeyPress::Down),
+                    'C' => Ok(KeyPress::Right),
+                    'D' => Ok(KeyPress::Left),
+                    'F' => Ok(KeyPress::End),
+                    'H' => Ok(KeyPress::Home),
+                    _ => Ok(KeyPress::UNKNOWN_ESC_SEQ),
+                }
+            }
+        } else if seq1 == 'O' {
+            // ESC O sequences.
+            let seq2 = try!(self.next_char());
+            match seq2 {
+                'F' => Ok(KeyPress::End),
+                'H' => Ok(KeyPress::Home),
+                _ => Ok(KeyPress::UNKNOWN_ESC_SEQ),
+            }
+        } else {
+            // TODO ESC-N (n): search history forward not interactively
+            // TODO ESC-P (p): search history backward not interactively
+            // TODO ESC-R (r): Undo all changes made to this line.
+            match seq1 {
+                '\x08' => Ok(KeyPress::Meta('\x08')), // Backspace
+                '<' => Ok(KeyPress::Meta('<')),
+                '>' => Ok(KeyPress::Meta('>')),
+                'b' | 'B' => Ok(KeyPress::Meta('B')),
+                'c' | 'C' => Ok(KeyPress::Meta('C')),
+                'd' | 'D' => Ok(KeyPress::Meta('D')),
+                'f' | 'F' => Ok(KeyPress::Meta('F')),
+                'l' | 'L' => Ok(KeyPress::Meta('L')),
+                't' | 'T' => Ok(KeyPress::Meta('T')),
+                'u' | 'U' => Ok(KeyPress::Meta('U')),
+                'y' | 'Y' => Ok(KeyPress::Meta('Y')),
+                '\x7f' => Ok(KeyPress::Meta('\x7f')), // Delete
+                _ => {
+                    // writeln!(io::stderr(), "key: {:?}, seq1: {:?}", KeyPress::Esc, seq1).unwrap();
+                    Ok(KeyPress::UNKNOWN_ESC_SEQ)
+                }
+            }
+        }
     }
 }
diff --git a/src/tty/windows.rs b/src/tty/windows.rs
index c7fc35b911083bac836b614ed7589c38818d2823..2244d8ce12c285793ee57aaf95204c1d024a8ec2 100644
--- a/src/tty/windows.rs
+++ b/src/tty/windows.rs
@@ -2,109 +2,192 @@ extern crate kernel32;
 extern crate winapi;
 
 use std::io;
-use super::StandardStream;
-use super::Terminal;
+use std::marker::PhantomData;
 use ::Result;
 
+pub type Handle = winapi::HANDLE;
+pub type Mode = winapi::DWORD;
+pub const STDIN_FILENO: winapi::DWORD = winapi::STD_INPUT_HANDLE;
+pub const STDOUT_FILENO: winapi::DWORD = winapi::STD_OUTPUT_HANDLE;
+
+fn get_std_handle(fd: winapi::DWORD) -> Result<winapi::HANDLE> {
+    let handle = unsafe { kernel32::GetStdHandle(fd) };
+    if handle == winapi::INVALID_HANDLE_VALUE {
+        try!(Err(io::Error::last_os_error()));
+    } else if handle.is_null() {
+        try!(Err(io::Error::new(io::ErrorKind::Other,
+                                "no stdio handle available for this process")));
+    }
+    Ok(handle)
+}
+
 macro_rules! check {
-    ($funcall:expr) => (
-        if $funcall == 0 {
-            return Err(From::from(io::Error::last_os_error()));
+    ($funcall:expr) => {
+        {
+        let rc = unsafe { $funcall };
+        if rc == 0 {
+            try!(Err(io::Error::last_os_error()));
         }
-    );
+        rc
+        }
+    };
 }
 
 /// Try to get the number of columns in the current terminal, or assume 80 if it fails.
-pub fn get_columns() -> usize {
-    // Get HANDLE to stdout
-    let handle = unsafe { kernel32::GetStdHandle(winapi::STD_OUTPUT_HANDLE) };
-
-    // Create CONSOLE_SCREEN_BUFFER_INFO with some default values
-    let mut csbi = winapi::wincon::CONSOLE_SCREEN_BUFFER_INFO {
-        dwSize: winapi::wincon::COORD { X: 0, Y: 0 },
-        dwCursorPosition: winapi::wincon::COORD { X: 0, Y: 0 },
-        wAttributes: 0,
-        srWindow: winapi::wincon::SMALL_RECT {
-            Left: 0,
-            Top: 0,
-            Right: 0,
-            Bottom: 0,
-        },
-        dwMaximumWindowSize: winapi::wincon::COORD { X: 0, Y: 0 },
-    };
-
-    let success: bool = unsafe { kernel32::GetConsoleScreenBufferInfo(handle, &mut csbi) != 0 };
-
-    // If we were not able to retrieve console info successfully,
-    // we will default to a column size of 80
-    if success && csbi.dwSize.X > 0 {
-        csbi.dwSize.X as usize
-    } else {
-        80
+pub fn get_columns(handle: Handle) -> usize {
+    let mut info = unsafe { mem::zeroed() };
+    match unsafe { kernel32::GetConsoleScreenBufferInfo(handle, &mut info) } {
+        0 => 80,
+        _ => info.dwSize.X as usize,
     }
 }
 
-/// Get WindowsTerminal struct
-pub fn get_terminal() -> WindowsTerminal {
-    WindowsTerminal{ original_mode: None }
-}
-
 /// Checking for an unsupported TERM in windows is a no-op
 pub fn is_unsupported_term() -> bool {
     false
 }
 
-/// Return whether or not STDIN, STDOUT or STDERR is a TTY
-pub fn is_a_tty(stream: StandardStream) -> bool {
-    let handle = match stream {
-            StandardStream::StdIn => winapi::STD_INPUT_HANDLE,
-            StandardStream::StdOut => winapi::STD_OUTPUT_HANDLE,
-    };
+pub fn get_console_mode(handle: winapi::HANDLE) -> Result<Mode> {
+    let mut original_mode = 0;
+    check!(kernel32::GetConsoleMode(handle, &mut original_mode));
+    Ok(original_mode)
+}
 
-    unsafe {
-            let handle = kernel32::GetStdHandle(handle);
-            let mut out = 0;
-            kernel32::GetConsoleMode(handle, &mut out) != 0
+/// Return whether or not STDIN, STDOUT or STDERR is a TTY
+pub fn is_a_tty(fd: winapi::DWORD) -> bool {
+    let handle = get_std_handle(fd);
+    match handle {
+        Ok(handle) => {
+            // If this function doesn't fail then fd is a TTY
+            get_console_mode(handle).is_ok()
+        }
+        Err(_) => false,
     }
 }
 
-pub struct WindowsTerminal {
-    original_mode: Option<winapi::minwindef::DWORD>
+/// Enable raw mode for the TERM
+pub fn enable_raw_mode() -> Result<Mode> {
+    let handle = try!(get_std_handle(STDIN_FILENO));
+    let original_mode = try!(get_console_mode(handle));
+    let raw = original_mode &
+              !(winapi::wincon::ENABLE_LINE_INPUT | winapi::wincon::ENABLE_ECHO_INPUT |
+                winapi::wincon::ENABLE_PROCESSED_INPUT);
+    check!(kernel32::SetConsoleMode(handle, raw));
+    Ok(original_mode)
+}
+
+/// Disable Raw mode for the term
+pub fn disable_raw_mode(original_mode: Mode) -> Result<()> {
+    let handle = try!(get_std_handle(STDIN_FILENO));
+    check!(kernel32::SetConsoleMode(handle, original_mode));
+    Ok(())
 }
 
-impl Terminal for WindowsTerminal {
-    /// Enable raw mode for the TERM
-    fn enable_raw_mode(&mut self) -> Result<()> {
-        let mut original_mode: winapi::minwindef::DWORD = 0;
-        unsafe {
-            let handle = kernel32::GetStdHandle(winapi::STD_INPUT_HANDLE);
-            check!(kernel32::GetConsoleMode(handle, &mut original_mode));
-            check!(kernel32::SetConsoleMode(
-                handle,
-                original_mode & !(winapi::wincon::ENABLE_LINE_INPUT |
-                            winapi::wincon::ENABLE_ECHO_INPUT |
-                            winapi::wincon::ENABLE_PROCESSED_INPUT)
-            ));
-        };
-        self.original_mode = Some(original_mode);
-        Ok(())
+pub fn stdout_handle() -> Result<Handle> {
+    let handle = try!(get_std_handle(STDOUT_FILENO));
+    Ok(handle)
+}
+
+/// Console input reader
+pub struct RawReader<R> {
+    handle: winapi::HANDLE,
+    buf: Option<u16>,
+    phantom: PhantomData<R>,
+}
+
+impl<R: Read> RawReader<R> {
+    pub fn new(_: R) -> Result<RawReader<R>> {
+        let handle = try!(get_std_handle(STDIN_FILENO));
+        Ok(RawReader {
+            handle: handle,
+            buf: None,
+            phantom: PhantomData,
+        })
     }
 
-    /// Disable Raw mode for the term
-    fn disable_raw_mode(&self) -> Result<()> {
-        unsafe {
-            let handle = kernel32::GetStdHandle(winapi::STD_INPUT_HANDLE);
-            check!(kernel32::SetConsoleMode(handle,
-                                    self.original_mode.expect("RAW MODE was not enabled previously")));
+    pub fn next_key(&mut self, _: bool) -> Result<KeyPress> {
+        use std::char::decode_utf16;
+
+        let mut rec: winapi::INPUT_RECORD = unsafe { mem::zeroed() };
+        let mut count = 0;
+        let mut esc_seen = false;
+        loop {
+            // TODO GetNumberOfConsoleInputEvents
+            check!(kernel32::ReadConsoleInputW(self.handle,
+                                               &mut rec,
+                                               1 as winapi::DWORD,
+                                               &mut count));
+
+            // TODO ENABLE_WINDOW_INPUT ???
+            if rec.EventType == winapi::WINDOW_BUFFER_SIZE_EVENT {
+                SIGWINCH.store(true, atomic::Ordering::SeqCst);
+                return Err(error::ReadlineError::WindowResize);
+            } else if rec.EventType != winapi::KEY_EVENT {
+                continue;
+            }
+            let key_event = unsafe { rec.KeyEvent() };
+            // writeln!(io::stderr(), "key_event: {:?}", key_event).unwrap();
+            if key_event.bKeyDown == 0 &&
+               key_event.wVirtualKeyCode != winapi::VK_MENU as winapi::WORD {
+                continue;
+            }
+
+            let ctrl = key_event.dwControlKeyState &
+                       (winapi::LEFT_CTRL_PRESSED | winapi::RIGHT_CTRL_PRESSED) !=
+                       0;
+            let meta = (key_event.dwControlKeyState &
+                        (winapi::LEFT_ALT_PRESSED | winapi::RIGHT_ALT_PRESSED) !=
+                        0) || esc_seen;
+
+            let utf16 = key_event.UnicodeChar;
+            if utf16 == 0 {
+                match key_event.wVirtualKeyCode as i32 {
+                    winapi::VK_LEFT => return Ok(KeyPress::Left),
+                    winapi::VK_RIGHT => return Ok(KeyPress::Right),
+                    winapi::VK_UP => return Ok(KeyPress::Up),
+                    winapi::VK_DOWN => return Ok(KeyPress::Down),
+                    winapi::VK_DELETE => return Ok(KeyPress::Delete),
+                    winapi::VK_HOME => return Ok(KeyPress::Home),
+                    winapi::VK_END => return Ok(KeyPress::End),
+                    _ => continue,
+                };
+            } else if utf16 == 27 {
+                esc_seen = true;
+                continue;
+            } else {
+                // TODO How to support surrogate pair ?
+                self.buf = Some(utf16);
+                let orc = decode_utf16(self).next();
+                if orc.is_none() {
+                    return Err(error::ReadlineError::Eof);
+                }
+                let c = try!(orc.unwrap());
+                if meta {
+                    match c {
+                        'b' | 'B' => return Ok(KeyPress::Meta('B')),
+                        'c' | 'C' => return Ok(KeyPress::Meta('C')),
+                        'd' | 'D' => return Ok(KeyPress::Meta('D')),
+                        'f' | 'F' => return Ok(KeyPress::Meta('F')),
+                        'l' | 'L' => return Ok(KeyPress::Meta('L')),
+                        't' | 'T' => return Ok(KeyPress::Meta('T')),
+                        'u' | 'U' => return Ok(KeyPress::Meta('U')),
+                        'y' | 'Y' => return Ok(KeyPress::Meta('Y')),
+                        _ => return Ok(KeyPress::UNKNOWN_ESC_SEQ),
+                    }
+                } else {
+                    return Ok(consts::char_to_key_press(c));
+                }
+            }
         }
-        Ok(())
     }
 }
 
-/// Ensure that RAW mode is disabled even in the case of a panic!
-#[allow(unused_must_use)]
-impl Drop for WindowsTerminal {
-    fn drop(&mut self) {
-        self.disable_raw_mode();
+impl<R: Read> Iterator for RawReader<R> {
+    type Item = u16;
+
+    fn next(&mut self) -> Option<u16> {
+        let buf = self.buf;
+        self.buf = None;
+        buf
     }
 }