Repository: kkawakam/rustyline
Pull request #235 (2019-06-02 14:02:57)

External print
See #229

```sh
cargo run --example external_print
```

To do:
* ~~Windows platform (only one printer supported, and when the printer is dropped we still wait)~~
* Check interaction with history search, completion, ...


Patch:
diff --git a/Cargo.toml b/Cargo.toml
index 52cb06c..3a0be34 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -48,7 +48,7 @@ skim = { version = "0.9", optional = true }
 buf_redux = { version = "0.8", default-features = false }
 
 [target.'cfg(windows)'.dependencies]
-winapi = { version = "0.3", features = ["consoleapi", "handleapi", "minwindef", "processenv", "std", "winbase", "wincon", "winuser"] }
+winapi = { version = "0.3", features = ["consoleapi", "handleapi", "synchapi", "minwindef", "processenv", "std", "winbase", "wincon", "winuser"] }
 scopeguard = "1.1"
 clipboard-win = "4.2.1"
 
@@ -56,6 +56,7 @@ clipboard-win = "4.2.1"
 doc-comment = "0.3"
 env_logger = { version = "0.9", default-features = false }
 tempfile = "3.1.0"
+rand = "0.8"
 assert_matches = "1.2"
 rustyline-derive = { version = "0.6.0", path = "rustyline-derive" }
 
diff --git a/examples/external_print.rs b/examples/external_print.rs
new file mode 100644
index 0000000..f1cfb64
--- /dev/null
+++ b/examples/external_print.rs
@@ -0,0 +1,29 @@
+use std::thread;
+use std::time::Duration;
+
+use rand::{thread_rng, Rng};
+
+use rustyline::{Editor, ExternalPrinter, Result};
+
+fn main() -> Result<()> {
+    let mut rl = Editor::<()>::new();
+    let mut printer = rl.create_external_printer()?;
+    thread::spawn(move || {
+        let mut rng = thread_rng();
+        let mut i = 0usize;
+        loop {
+            printer
+                .print(format!("External message #{}", i))
+                .expect("External print failure");
+            let wait_ms = rng.gen_range(1000..10000);
+            thread::sleep(Duration::from_millis(wait_ms));
+            i += 1;
+        }
+    });
+
+    loop {
+        let line = rl.readline("> ")?;
+        rl.add_history_entry(line.as_str());
+        println!("Line: {}", line);
+    }
+}
diff --git a/src/edit.rs b/src/edit.rs
index adb6631..1fa0d3e 100644
--- a/src/edit.rs
+++ b/src/edit.rs
@@ -79,9 +79,10 @@ impl<'out, 'prompt, H: Helper> State<'out, 'prompt, H> {
         input_state: &mut InputState,
         rdr: &mut <Terminal as Term>::Reader,
         single_esc_abort: bool,
+        ignore_external_print: bool,
     ) -> Result<Cmd> {
         loop {
-            let rc = input_state.next_cmd(rdr, self, single_esc_abort);
+            let rc = input_state.next_cmd(rdr, self, single_esc_abort, ignore_external_print);
             if rc.is_err() && self.out.sigwinch() {
                 self.out.update_size();
                 self.prompt_size = self
@@ -301,6 +302,15 @@ impl<'out, 'prompt, H: Helper> Refresher for State<'out, 'prompt, H> {
     fn pos(&self) -> usize {
         self.line.pos()
     }
+
+    fn external_print(&mut self, rdr: &mut <Terminal as Term>::Reader, msg: String) -> Result<()> {
+        self.out.clear_rows(&self.layout)?;
+        self.layout.end.row = 0;
+        self.layout.cursor.row = 0;
+        self.out.write_and_flush(msg.as_str())?;
+        self.move_cursor_at_leftmost(rdr)?;
+        self.refresh_line()
+    }
 }
 
 impl<'out, 'prompt, H: Helper> fmt::Debug for State<'out, 'prompt, H> {
@@ -708,7 +718,7 @@ mod test {
 
     #[test]
     fn edit_history_next() {
-        let mut out = Sink::new();
+        let mut out = Sink::default();
         let mut history = History::new();
         history.add("line0");
         history.add("line1");
diff --git a/src/error.rs b/src/error.rs
index 3ef771a..b5c17e6 100644
--- a/src/error.rs
+++ b/src/error.rs
@@ -18,9 +18,6 @@ pub enum ReadlineError {
     Eof,
     /// Interrupt signal (VINTR / VQUIT / Ctrl-C)
     Interrupted,
-    /// Chars Error
-    #[cfg(unix)]
-    Utf8Error,
     /// Unix Error from syscall
     #[cfg(unix)]
     Errno(nix::Error),
@@ -43,8 +40,6 @@ impl fmt::Display for ReadlineError {
             ReadlineError::Eof => write!(f, "EOF"),
             ReadlineError::Interrupted => write!(f, "Interrupted"),
             #[cfg(unix)]
-            ReadlineError::Utf8Error => write!(f, "invalid utf-8: corrupt contents"),
-            #[cfg(unix)]
             ReadlineError::Errno(ref err) => err.fmt(f),
             #[cfg(windows)]
             ReadlineError::WindowResize => write!(f, "WindowResize"),
@@ -80,7 +75,21 @@ impl From<nix::Error> for ReadlineError {
 #[cfg(windows)]
 impl From<char::DecodeUtf16Error> for ReadlineError {
     fn from(err: char::DecodeUtf16Error) -> Self {
-        ReadlineError::Decode(err)
+        ReadlineError::Io(io::Error::new(io::ErrorKind::InvalidData, err))
+    }
+}
+
+#[cfg(windows)]
+impl From<std::string::FromUtf8Error> for ReadlineError {
+    fn from(err: std::string::FromUtf8Error) -> Self {
+        ReadlineError::Io(io::Error::new(io::ErrorKind::InvalidData, err))
+    }
+}
+
+#[cfg(unix)]
+impl From<fmt::Error> for ReadlineError {
+    fn from(err: fmt::Error) -> Self {
+        ReadlineError::Io(io::Error::new(io::ErrorKind::Other, err))
     }
 }
 
diff --git a/src/keymap.rs b/src/keymap.rs
index 1724046..b88aca1 100644
--- a/src/keymap.rs
+++ b/src/keymap.rs
@@ -6,7 +6,7 @@ use radix_trie::Trie;
 
 use super::Result;
 use crate::keys::{KeyCode as K, KeyEvent, KeyEvent as E, Modifiers as M};
-use crate::tty::{RawReader, Term, Terminal};
+use crate::tty::{self, RawReader, Term, Terminal};
 use crate::{Config, EditMode, Event, EventContext, EventHandler};
 
 /// The number of times one command should be repeated.
@@ -392,6 +392,8 @@ pub trait Refresher {
     fn line(&self) -> &str;
     /// Current cursor position (byte position)
     fn pos(&self) -> usize;
+    /// Display `msg` above currently edited line.
+    fn external_print(&mut self, rdr: &mut <Terminal as Term>::Reader, msg: String) -> Result<()>;
 }
 
 impl InputState {
@@ -418,21 +420,38 @@ impl InputState {
         rdr: &mut <Terminal as Term>::Reader,
         wrt: &mut dyn Refresher,
         single_esc_abort: bool,
+        ignore_external_print: bool,
     ) -> Result<Cmd> {
-        match self.mode {
-            EditMode::Emacs => {
-                let key = rdr.next_key(single_esc_abort)?;
-                self.emacs(rdr, wrt, key)
-            }
-            EditMode::Vi if self.input_mode != InputMode::Command => {
-                let key = rdr.next_key(false)?;
-                self.vi_insert(rdr, wrt, key)
-            }
-            EditMode::Vi => {
-                let key = rdr.next_key(false)?;
-                self.vi_command(rdr, wrt, key)
+        let single_esc_abort = self.single_esc_abort(single_esc_abort);
+        let key;
+        if ignore_external_print {
+            key = rdr.next_key(single_esc_abort)?;
+        } else {
+            loop {
+                let event = rdr.wait_for_input(single_esc_abort)?;
+                match event {
+                    tty::Event::KeyPress(k) => {
+                        key = k;
+                        break;
+                    }
+                    tty::Event::ExternalPrint(msg) => {
+                        wrt.external_print(rdr, msg)?;
+                    }
+                }
             }
         }
+        match self.mode {
+            EditMode::Emacs => self.emacs(rdr, wrt, key),
+            EditMode::Vi if self.input_mode != InputMode::Command => self.vi_insert(rdr, wrt, key),
+            EditMode::Vi => self.vi_command(rdr, wrt, key),
+        }
+    }
+
+    fn single_esc_abort(&self, single_esc_abort: bool) -> bool {
+        match self.mode {
+            EditMode::Emacs => single_esc_abort,
+            EditMode::Vi => false,
+        }
     }
 
     /// Application customized binding
diff --git a/src/lib.rs b/src/lib.rs
index 8f85fb7..e55669a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -58,7 +58,7 @@ pub use crate::keymap::{Anchor, At, CharSearch, Cmd, InputMode, Movement, Repeat
 use crate::keymap::{InputState, Refresher};
 pub use crate::keys::{KeyCode, KeyEvent, Modifiers};
 use crate::kill_ring::KillRing;
-
+pub use crate::tty::ExternalPrinter;
 use crate::validate::Validator;
 
 /// The error type for I/O and Linux Syscalls (Errno)
@@ -107,7 +107,7 @@ fn complete_line<H: Helper>(
             }
             s.refresh_line()?;
 
-            cmd = s.next_cmd(input_state, rdr, true)?;
+            cmd = s.next_cmd(input_state, rdr, true, true)?;
             match cmd {
                 Cmd::Complete => {
                     i = (i + 1) % (candidates.len() + 1); // Circular
@@ -154,7 +154,7 @@ fn complete_line<H: Helper>(
             return Ok(None);
         }
         // we can't complete any further, wait for second tab
-        let mut cmd = s.next_cmd(input_state, rdr, true)?;
+        let mut cmd = s.next_cmd(input_state, rdr, true, true)?;
         // if any character other than tab, pass it to the main loop
         if cmd != Cmd::Complete {
             return Ok(Some(cmd));
@@ -174,7 +174,7 @@ fn complete_line<H: Helper>(
                 && cmd != Cmd::SelfInsert(1, 'N')
                 && cmd != Cmd::Kill(Movement::BackwardChar(1))
             {
-                cmd = s.next_cmd(input_state, rdr, false)?;
+                cmd = s.next_cmd(input_state, rdr, false, true)?;
             }
             matches!(cmd, Cmd::SelfInsert(1, 'y' | 'Y'))
         } else {
@@ -306,7 +306,7 @@ fn page_completions<C: Candidate, H: Helper>(
                 && cmd != Cmd::Newline
                 && !matches!(cmd, Cmd::AcceptOrInsertLine { .. })
             {
-                cmd = s.next_cmd(input_state, rdr, false)?;
+                cmd = s.next_cmd(input_state, rdr, false, true)?;
             }
             match cmd {
                 Cmd::SelfInsert(1, 'y' | 'Y' | ' ') => {
@@ -376,7 +376,7 @@ fn reverse_incremental_search<H: Helper>(
         };
         s.refresh_prompt_and_line(&prompt)?;
 
-        cmd = s.next_cmd(input_state, rdr, true)?;
+        cmd = s.next_cmd(input_state, rdr, true, true)?;
         if let Cmd::SelfInsert(_, c) = cmd {
             search_buf.push(c);
         } else {
@@ -469,7 +469,7 @@ fn readline_edit<H: Helper>(
     s.refresh_line()?;
 
     loop {
-        let mut cmd = s.next_cmd(&mut input_state, &mut rdr, false)?;
+        let mut cmd = s.next_cmd(&mut input_state, &mut rdr, false, false)?;
 
         if cmd.should_reset_kill_ring() {
             editor.reset_kill_ring();
@@ -894,6 +894,11 @@ impl<H: Helper> Editor<H> {
             None
         }
     }
+
+    /// Create an external printer
+    pub fn create_external_printer(&mut self) -> Result<<Terminal as Term>::ExternalPrinter> {
+        self.term.create_external_printer()
+    }
 }
 
 impl<H: Helper> config::Configurer for Editor<H> {
diff --git a/src/tty/mod.rs b/src/tty/mod.rs
index 24c4758..2dc583a 100644
--- a/src/tty/mod.rs
+++ b/src/tty/mod.rs
@@ -15,8 +15,16 @@ pub trait RawMode: Sized {
     fn disable_raw_mode(&self) -> Result<()>;
 }
 
+/// Input event
+pub enum Event {
+    KeyPress(KeyEvent),
+    ExternalPrint(String),
+}
+
 /// Translate bytes read from stdin to keys.
 pub trait RawReader {
+    /// Blocking wait for either a key press or an external print
+    fn wait_for_input(&mut self, single_esc_abort: bool) -> Result<Event>; // TODO replace calls to `next_key` by `wait_for_input` where relevant
     /// Blocking read of key pressed.
     fn next_key(&mut self, single_esc_abort: bool) -> Result<KeyEvent>;
     /// For CTRL-V support
@@ -92,6 +100,8 @@ pub trait Renderer {
 
     /// Clear the screen. Used to handle ctrl+l
     fn clear_screen(&mut self) -> Result<()>;
+    /// Clear rows used by prompt and edited line
+    fn clear_rows(&mut self, layout: &Layout) -> Result<()>;
 
     /// Check if a SIGWINCH signal has been received
     fn sigwinch(&self) -> bool;
@@ -143,6 +153,10 @@ impl<'a, R: Renderer + ?Sized> Renderer for &'a mut R {
         (**self).clear_screen()
     }
 
+    fn clear_rows(&mut self, layout: &Layout) -> Result<()> {
+        (**self).clear_rows(layout)
+    }
+
     fn sigwinch(&self) -> bool {
         (**self).sigwinch()
     }
@@ -199,12 +213,19 @@ fn width(s: &str, esc_seq: &mut u8) -> usize {
     }
 }
 
+/// External printer
+pub trait ExternalPrinter {
+    /// Print message to stdout
+    fn print(&mut self, msg: String) -> Result<()>;
+}
+
 /// Terminal contract
 pub trait Term {
     type KeyMap;
     type Reader: RawReader; // rl_instream
     type Writer: Renderer<Reader = Self::Reader>; // rl_outstream
     type Mode: RawMode;
+    type ExternalPrinter: ExternalPrinter;
 
     fn new(
         color_mode: ColorMode,
@@ -227,6 +248,8 @@ pub trait Term {
     /// Create a writer
     fn create_writer(&self) -> Self::Writer;
     fn writeln(&self) -> Result<()>;
+    /// Create an external printer
+    fn create_external_printer(&mut self) -> Result<Self::ExternalPrinter>;
 }
 
 // If on Windows platform import Windows TTY module
diff --git a/src/tty/unix.rs b/src/tty/unix.rs
index 2022d7b..63c1605 100644
--- a/src/tty/unix.rs
+++ b/src/tty/unix.rs
@@ -1,23 +1,25 @@
 //! Unix specific definitions
 use std::cmp;
 use std::collections::HashMap;
-use std::fs::OpenOptions;
-use std::io::{self, ErrorKind, Read};
+use std::fs::{File, OpenOptions};
+use std::io::{self, ErrorKind, Read, Write};
 use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
-use std::sync;
 use std::sync::atomic::{AtomicBool, Ordering};
+use std::sync::mpsc::{self, SyncSender};
+use std::sync::{self, Arc, Mutex};
 
 use buf_redux::BufReader;
 use log::{debug, warn};
 use nix::errno::Errno;
 use nix::poll::{self, PollFlags};
+use nix::sys::select::{self, FdSet};
 use nix::sys::signal;
 use nix::sys::termios::{self, SetArg, SpecialCharacterIndices as SCI, Termios};
 use nix::unistd::{close, isatty, write};
 use unicode_segmentation::UnicodeSegmentation;
 use utf8parse::{Parser, Receiver};
 
-use super::{width, RawMode, RawReader, Renderer, Term};
+use super::{width, Event, RawMode, RawReader, Renderer, Term};
 use crate::config::{Behavior, BellStyle, ColorMode, Config};
 use crate::highlight::Highlighter;
 use crate::keys::{KeyCode as K, KeyEvent, KeyEvent as E, Modifiers as M};
@@ -96,6 +98,7 @@ pub struct PosixMode {
     termios: termios::Termios,
     tty_in: RawFd,
     tty_out: Option<RawFd>,
+    raw_mode: Arc<AtomicBool>,
 }
 
 #[cfg(not(test))]
@@ -109,6 +112,7 @@ impl RawMode for PosixMode {
         if let Some(out) = self.tty_out {
             write_all(out, BRACKETED_PASTE_OFF)?;
         }
+        self.raw_mode.store(false, Ordering::SeqCst);
         Ok(())
     }
 }
@@ -142,6 +146,11 @@ impl Read for TtyIn {
     }
 }
 
+// (native receiver with a selectable file descriptor, actual message receiver)
+type PipeReader = Arc<Mutex<(File, mpsc::Receiver<String>)>>;
+// (native sender, actual message sender)
+type PipeWriter = (Arc<Mutex<File>>, SyncSender<String>);
+
 /// Console input reader
 pub struct PosixRawReader {
     tty_in: BufReader<TtyIn>,
@@ -150,6 +159,9 @@ pub struct PosixRawReader {
     parser: Parser,
     receiver: Utf8,
     key_map: PosixKeyMap,
+    // external print reader
+    pipe_reader: Option<PipeReader>,
+    fds: FdSet,
 }
 
 impl AsRawFd for PosixRawReader {
@@ -190,7 +202,12 @@ const RXVT_CTRL: char = '\x1e';
 const RXVT_CTRL_SHIFT: char = '@';
 
 impl PosixRawReader {
-    fn new(fd: RawFd, config: &Config, key_map: PosixKeyMap) -> Self {
+    fn new(
+        fd: RawFd,
+        config: &Config,
+        key_map: PosixKeyMap,
+        pipe_reader: Option<PipeReader>,
+    ) -> Self {
         Self {
             tty_in: BufReader::with_capacity(1024, TtyIn { fd }),
             timeout_ms: config.keyseq_timeout(),
@@ -201,6 +218,8 @@ impl PosixRawReader {
                 valid: true,
             },
             key_map,
+            pipe_reader,
+            fds: FdSet::new(),
         }
     }
 
@@ -663,9 +682,57 @@ impl PosixRawReader {
             Err(_) => r,
         }
     }
+
+    fn select(&mut self, single_esc_abort: bool) -> Result<Event> {
+        let tty_in = self.as_raw_fd();
+        loop {
+            let mut readfds = self.fds;
+            readfds.clear();
+            readfds.insert(tty_in);
+            readfds.insert(
+                self.pipe_reader
+                    .as_ref()
+                    .unwrap()
+                    .lock()
+                    .unwrap()
+                    .0
+                    .as_raw_fd(),
+            );
+            if let Err(err) = select::select(
+                readfds.highest().map(|h| h + 1),
+                Some(&mut readfds),
+                None,
+                None,
+                None,
+            ) {
+                if err != ::nix::errno::Errno::EINTR || SIGWINCH.load(Ordering::Relaxed) {
+                    return Err(err.into());
+                } else {
+                    continue;
+                }
+            };
+            if readfds.contains(tty_in) {
+                // prefer user input over external print
+                return self.next_key(single_esc_abort).map(Event::KeyPress);
+            } else {
+                let mut guard = self.pipe_reader.as_ref().unwrap().lock().unwrap();
+                guard.0.read_exact(&mut self.buf)?;
+                if let Ok(msg) = guard.1.try_recv() {
+                    return Ok(Event::ExternalPrint(msg));
+                }
+            }
+        }
+    }
 }
 
 impl RawReader for PosixRawReader {
+    fn wait_for_input(&mut self, single_esc_abort: bool) -> Result<Event> {
+        match self.pipe_reader {
+            Some(_) => self.select(single_esc_abort),
+            None => self.next_key(single_esc_abort).map(Event::KeyPress),
+        }
+    }
+
     fn next_key(&mut self, single_esc_abort: bool) -> Result<KeyEvent> {
         let c = self.next_char()?;
 
@@ -704,7 +771,7 @@ impl RawReader for PosixRawReader {
             let b = self.buf[0];
             self.parser.advance(&mut self.receiver, b);
             if !self.receiver.valid {
-                return Err(error::ReadlineError::Utf8Error);
+                return Err(error::ReadlineError::from(io::ErrorKind::InvalidData));
             } else if let Some(c) = self.receiver.c.take() {
                 return Ok(c);
             }
@@ -810,7 +877,7 @@ impl Renderer for PosixRenderer {
             if row_shift == 1 {
                 self.buffer.push_str("\x1b[B");
             } else {
-                write!(self.buffer, "\x1b[{}B", row_shift).unwrap();
+                write!(self.buffer, "\x1b[{}B", row_shift)?;
             }
         } else if row_ordering == cmp::Ordering::Less {
             // move up
@@ -818,7 +885,7 @@ impl Renderer for PosixRenderer {
             if row_shift == 1 {
                 self.buffer.push_str("\x1b[A");
             } else {
-                write!(self.buffer, "\x1b[{}A", row_shift).unwrap();
+                write!(self.buffer, "\x1b[{}A", row_shift)?;
             }
         }
         let col_ordering = new.col.cmp(&old.col);
@@ -828,7 +895,7 @@ impl Renderer for PosixRenderer {
             if col_shift == 1 {
                 self.buffer.push_str("\x1b[C");
             } else {
-                write!(self.buffer, "\x1b[{}C", col_shift).unwrap();
+                write!(self.buffer, "\x1b[{}C", col_shift)?;
             }
         } else if col_ordering == cmp::Ordering::Less {
             // move left
@@ -836,7 +903,7 @@ impl Renderer for PosixRenderer {
             if col_shift == 1 {
                 self.buffer.push_str("\x1b[D");
             } else {
-                write!(self.buffer, "\x1b[{}D", col_shift).unwrap();
+                write!(self.buffer, "\x1b[{}D", col_shift)?;
             }
         }
         write_all(self.out, self.buffer.as_str())?;
@@ -895,7 +962,7 @@ impl Renderer for PosixRenderer {
         let new_cursor_row_movement = end_pos.row - cursor.row;
         // move the cursor up as required
         if new_cursor_row_movement > 0 {
-            write!(self.buffer, "\x1b[{}A", new_cursor_row_movement).unwrap();
+            write!(self.buffer, "\x1b[{}A", new_cursor_row_movement)?;
         }
         // position the cursor within the line
         if cursor.col > 0 {
@@ -954,6 +1021,13 @@ impl Renderer for PosixRenderer {
         self.write_and_flush("\x1b[H\x1b[2J")
     }
 
+    fn clear_rows(&mut self, layout: &Layout) -> Result<()> {
+        self.buffer.clear();
+        self.clear_old_rows(layout);
+        write_all(self.out, self.buffer.as_str())?;
+        Ok(())
+    }
+
     /// Check if a SIGWINCH signal has been received
     fn sigwinch(&self) -> bool {
         SIGWINCH
@@ -1078,6 +1152,11 @@ pub struct PosixTerminal {
     tab_stop: usize,
     bell_style: BellStyle,
     enable_bracketed_paste: bool,
+    raw_mode: Arc<AtomicBool>,
+    // external print reader
+    pipe_reader: Option<PipeReader>,
+    // external print writer
+    pipe_writer: Option<PipeWriter>,
 }
 
 impl PosixTerminal {
@@ -1091,6 +1170,7 @@ impl PosixTerminal {
 }
 
 impl Term for PosixTerminal {
+    type ExternalPrinter = ExternalPrinter;
     type KeyMap = PosixKeyMap;
     type Mode = PosixMode;
     type Reader = PosixRawReader;
@@ -1139,6 +1219,9 @@ impl Term for PosixTerminal {
             tab_stop,
             bell_style,
             enable_bracketed_paste,
+            raw_mode: Arc::new(AtomicBool::new(false)),
+            pipe_reader: None,
+            pipe_writer: None,
         };
         if !term.unsupported && term.is_in_a_tty && term.is_out_a_tty {
             install_sigwinch_handler();
@@ -1199,6 +1282,7 @@ impl Term for PosixTerminal {
 
         termios::tcsetattr(self.tty_in, SetArg::TCSADRAIN, &raw)?;
 
+        self.raw_mode.store(true, Ordering::SeqCst);
         // enable bracketed paste
         let out = if !self.enable_bracketed_paste {
             None
@@ -1208,11 +1292,19 @@ impl Term for PosixTerminal {
         } else {
             Some(self.tty_out)
         };
+
+        // when all ExternalPrinter are dropped there is no need to use `pipe_reader`
+        if Arc::strong_count(&self.raw_mode) == 1 {
+            self.pipe_writer = None;
+            self.pipe_reader = None;
+        }
+
         Ok((
             PosixMode {
                 termios: original_mode,
                 tty_in: self.tty_in,
                 tty_out: out,
+                raw_mode: self.raw_mode.clone(),
             },
             key_map,
         ))
@@ -1220,7 +1312,7 @@ impl Term for PosixTerminal {
 
     /// Create a RAW reader
     fn create_reader(&self, config: &Config, key_map: PosixKeyMap) -> PosixRawReader {
-        PosixRawReader::new(self.tty_in, config, key_map)
+        PosixRawReader::new(self.tty_in, config, key_map, self.pipe_reader.clone())
     }
 
     fn create_writer(&self) -> PosixRenderer {
@@ -1236,6 +1328,35 @@ impl Term for PosixTerminal {
         write_all(self.tty_out, "\n")?;
         Ok(())
     }
+
+    fn create_external_printer(&mut self) -> Result<ExternalPrinter> {
+        if let Some(ref writer) = self.pipe_writer {
+            return Ok(ExternalPrinter {
+                writer: writer.clone(),
+                raw_mode: self.raw_mode.clone(),
+                tty_out: self.tty_out,
+            });
+        }
+        if self.unsupported || !self.is_input_tty() || !self.is_output_tty() {
+            return Err(nix::Error::ENOTTY.into());
+        }
+        use nix::unistd::pipe;
+        use std::os::unix::io::FromRawFd;
+        let (sender, receiver) = mpsc::sync_channel(1); // TODO validate: bound
+        let (r, w) = pipe()?;
+        let reader = Arc::new(Mutex::new((unsafe { File::from_raw_fd(r) }, receiver)));
+        let writer = (
+            Arc::new(Mutex::new(unsafe { File::from_raw_fd(w) })),
+            sender,
+        );
+        self.pipe_reader.replace(reader);
+        self.pipe_writer.replace(writer.clone());
+        Ok(ExternalPrinter {
+            writer,
+            raw_mode: self.raw_mode.clone(),
+            tty_out: self.tty_out,
+        })
+    }
 }
 
 #[allow(unused_must_use)]
@@ -1248,6 +1369,32 @@ impl Drop for PosixTerminal {
     }
 }
 
+#[derive(Debug)]
+pub struct ExternalPrinter {
+    writer: PipeWriter,
+    raw_mode: Arc<AtomicBool>,
+    tty_out: RawFd,
+}
+
+impl super::ExternalPrinter for ExternalPrinter {
+    fn print(&mut self, msg: String) -> Result<()> {
+        // write directly to stdout/stderr while not in raw mode
+        if !self.raw_mode.load(Ordering::SeqCst) {
+            write_all(self.tty_out, msg.as_str())?;
+        } else if let Ok(mut writer) = self.writer.0.lock() {
+            self.writer
+                .1
+                .send(msg)
+                .map_err(|_| io::Error::from(ErrorKind::Other))?; // FIXME
+            writer.write_all(&[b'm'])?;
+            writer.flush()?;
+        } else {
+            return Err(io::Error::from(ErrorKind::Other).into()); // FIXME
+        }
+        Ok(())
+    }
+}
+
 #[cfg(not(test))]
 pub fn suspend() -> Result<()> {
     use nix::unistd::Pid;
diff --git a/src/tty/windows.rs b/src/tty/windows.rs
index 60c6560..c5a237d 100644
--- a/src/tty/windows.rs
+++ b/src/tty/windows.rs
@@ -7,18 +7,21 @@ use std::mem;
 use std::os::windows::io::IntoRawHandle;
 use std::ptr;
 use std::sync::atomic::{AtomicBool, Ordering};
+use std::sync::mpsc::{sync_channel, Receiver, SyncSender};
+use std::sync::Arc;
 
 use log::{debug, warn};
 use unicode_segmentation::UnicodeSegmentation;
 use unicode_width::UnicodeWidthStr;
 use winapi::shared::minwindef::{BOOL, DWORD, FALSE, TRUE, WORD};
 use winapi::shared::winerror;
-use winapi::um::handleapi::{self, INVALID_HANDLE_VALUE};
+use winapi::um::handleapi::{self, CloseHandle, INVALID_HANDLE_VALUE};
+use winapi::um::synchapi::{CreateEventW, ResetEvent, SetEvent};
 use winapi::um::wincon::{self, CONSOLE_SCREEN_BUFFER_INFO, COORD};
 use winapi::um::winnt::{CHAR, HANDLE};
 use winapi::um::{consoleapi, processenv, winbase, winuser};
 
-use super::{width, RawMode, RawReader, Renderer, Term};
+use super::{width, Event, RawMode, RawReader, Renderer, Term};
 use crate::config::{Behavior, BellStyle, ColorMode, Config};
 use crate::highlight::Highlighter;
 use crate::keys::{KeyCode as K, KeyEvent, Modifiers as M};
@@ -28,6 +31,10 @@ use crate::{error, Cmd, Result};
 
 fn get_std_handle(fd: DWORD) -> Result<HANDLE> {
     let handle = unsafe { processenv::GetStdHandle(fd) };
+    check_handle(handle)
+}
+
+fn check_handle(handle: HANDLE) -> Result<HANDLE> {
     if handle == INVALID_HANDLE_VALUE {
         Err(io::Error::last_os_error())?;
     } else if handle.is_null() {
@@ -39,9 +46,9 @@ fn get_std_handle(fd: DWORD) -> Result<HANDLE> {
     Ok(handle)
 }
 
-fn check(rc: BOOL) -> Result<()> {
+fn check(rc: BOOL) -> io::Result<()> {
     if rc == FALSE {
-        Err(io::Error::last_os_error())?
+        Err(io::Error::last_os_error())
     } else {
         Ok(())
     }
@@ -72,12 +79,13 @@ pub type KeyMap = ConsoleKeyMap;
 #[cfg(not(test))]
 pub type Mode = ConsoleMode;
 
-#[derive(Clone, Copy, Debug)]
+#[derive(Clone, Debug)]
 pub struct ConsoleMode {
     original_conin_mode: DWORD,
     conin: HANDLE,
     original_conout_mode: Option<DWORD>,
     conout: HANDLE,
+    raw_mode: Arc<AtomicBool>,
 }
 
 impl RawMode for ConsoleMode {
@@ -87,6 +95,7 @@ impl RawMode for ConsoleMode {
         if let Some(original_stdstream_mode) = self.original_conout_mode {
             check(unsafe { consoleapi::SetConsoleMode(self.conout, original_stdstream_mode) })?;
         }
+        self.raw_mode.store(false, Ordering::SeqCst);
         Ok(())
     }
 }
@@ -94,131 +103,60 @@ impl RawMode for ConsoleMode {
 /// Console input reader
 pub struct ConsoleRawReader {
     conin: HANDLE,
+    // external print reader
+    pipe_reader: Option<Arc<AsyncPipe>>,
 }
 
 impl ConsoleRawReader {
-    pub fn create(conin: HANDLE) -> ConsoleRawReader {
-        ConsoleRawReader { conin }
+    fn create(conin: HANDLE, pipe_reader: Option<Arc<AsyncPipe>>) -> ConsoleRawReader {
+        ConsoleRawReader { conin, pipe_reader }
     }
-}
 
-impl RawReader for ConsoleRawReader {
-    fn next_key(&mut self, _: bool) -> Result<KeyEvent> {
-        use std::char::decode_utf16;
-        use winapi::um::wincon::{
-            LEFT_ALT_PRESSED, LEFT_CTRL_PRESSED, RIGHT_ALT_PRESSED, RIGHT_CTRL_PRESSED,
-            SHIFT_PRESSED,
-        };
+    fn select(&mut self) -> Result<Event> {
+        use std::convert::TryInto;
+        use winapi::um::synchapi::WaitForMultipleObjects;
+        use winapi::um::winbase::{INFINITE, WAIT_OBJECT_0};
 
-        let mut rec: wincon::INPUT_RECORD = unsafe { mem::zeroed() };
-        let mut count = 0;
-        let mut surrogate = 0;
+        let pipe_reader = self.pipe_reader.as_ref().unwrap();
+        let handles = [self.conin, pipe_reader.event.0];
+        let n = handles.len().try_into().unwrap();
         loop {
-            // TODO GetNumberOfConsoleInputEvents
-            check(unsafe { consoleapi::ReadConsoleInputW(self.conin, &mut rec, 1, &mut count) })?;
-
-            if rec.EventType == wincon::WINDOW_BUFFER_SIZE_EVENT {
-                SIGWINCH.store(true, Ordering::SeqCst);
-                debug!(target: "rustyline", "SIGWINCH");
-                return Err(error::ReadlineError::WindowResize); // sigwinch +
-                                                                // err => err
-                                                                // ignored
-            } else if rec.EventType != wincon::KEY_EVENT {
-                continue;
-            }
-            let key_event = unsafe { rec.Event.KeyEvent() };
-            // writeln!(io::stderr(), "key_event: {:?}", key_event).unwrap();
-            if key_event.bKeyDown == 0 && key_event.wVirtualKeyCode != winuser::VK_MENU as WORD {
-                continue;
-            }
-            // key_event.wRepeatCount seems to be always set to 1 (maybe because we only
-            // read one character at a time)
-
-            let alt_gr = key_event.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED)
-                == (LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED);
-            let mut mods = M::NONE;
-            if !alt_gr
-                && key_event.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED) != 0
-            {
-                mods |= M::CTRL;
-            }
-            if !alt_gr && key_event.dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED) != 0
-            {
-                mods |= M::ALT;
-            }
-            if key_event.dwControlKeyState & SHIFT_PRESSED != 0 {
-                mods |= M::SHIFT;
-            }
-
-            let utf16 = unsafe { *key_event.uChar.UnicodeChar() };
-            let key_code = match i32::from(key_event.wVirtualKeyCode) {
-                winuser::VK_LEFT => K::Left,
-                winuser::VK_RIGHT => K::Right,
-                winuser::VK_UP => K::Up,
-                winuser::VK_DOWN => K::Down,
-                winuser::VK_DELETE => K::Delete,
-                winuser::VK_HOME => K::Home,
-                winuser::VK_END => K::End,
-                winuser::VK_PRIOR => K::PageUp,
-                winuser::VK_NEXT => K::PageDown,
-                winuser::VK_INSERT => K::Insert,
-                winuser::VK_F1 => K::F(1),
-                winuser::VK_F2 => K::F(2),
-                winuser::VK_F3 => K::F(3),
-                winuser::VK_F4 => K::F(4),
-                winuser::VK_F5 => K::F(5),
-                winuser::VK_F6 => K::F(6),
-                winuser::VK_F7 => K::F(7),
-                winuser::VK_F8 => K::F(8),
-                winuser::VK_F9 => K::F(9),
-                winuser::VK_F10 => K::F(10),
-                winuser::VK_F11 => K::F(11),
-                winuser::VK_F12 => K::F(12),
-                winuser::VK_BACK => K::Backspace, // vs Ctrl-h
-                winuser::VK_RETURN => K::Enter,   // vs Ctrl-m
-                winuser::VK_ESCAPE => K::Esc,
-                winuser::VK_TAB => {
-                    if mods.contains(M::SHIFT) {
-                        mods.remove(M::SHIFT);
-                        K::BackTab
-                    } else {
-                        K::Tab // vs Ctrl-i
-                    }
-                }
-                _ => {
-                    if utf16 == 0 {
-                        continue;
-                    }
-                    K::UnknownEscSeq
+            let rc = unsafe { WaitForMultipleObjects(n, handles.as_ptr(), FALSE, INFINITE) };
+            if rc == WAIT_OBJECT_0 {
+                let mut count = 0;
+                check(unsafe {
+                    consoleapi::GetNumberOfConsoleInputEvents(self.conin, &mut count)
+                })?;
+                match read_input(self.conin, count)? {
+                    KeyEvent(K::UnknownEscSeq, M::NONE) => continue, // no relevant
+                    key => return Ok(Event::KeyPress(key)),
+                };
+            } else if rc == WAIT_OBJECT_0 + 1 {
+                debug!(target: "rustyline", "ExternalPrinter::receive");
+                check(unsafe { ResetEvent(pipe_reader.event.0) })?;
+                match pipe_reader.receiver.recv() {
+                    Ok(msg) => return Ok(Event::ExternalPrint(msg)),
+                    Err(e) => Err(io::Error::new(io::ErrorKind::InvalidInput, e))?,
                 }
-            };
-            let key = if key_code != K::UnknownEscSeq {
-                KeyEvent(key_code, mods)
-            } else if utf16 == 27 {
-                KeyEvent(K::Esc, mods) // FIXME dead code ?
             } else {
-                if (0xD800..0xDC00).contains(&utf16) {
-                    surrogate = utf16;
-                    continue;
-                }
-                let orc = if surrogate == 0 {
-                    decode_utf16(Some(utf16)).next()
-                } else {
-                    decode_utf16([surrogate, utf16].iter().copied()).next()
-                };
-                let rc = if let Some(rc) = orc {
-                    rc
-                } else {
-                    return Err(error::ReadlineError::Eof);
-                };
-                let c = rc?;
-                KeyEvent::new(c, mods)
-            };
-            debug!(target: "rustyline", "wVirtualKeyCode: {:#x}, utf16: {:#x}, dwControlKeyState: {:#x} => key: {:?}", key_event.wVirtualKeyCode, utf16, key_event.dwControlKeyState, key);
-            return Ok(key);
+                Err(io::Error::last_os_error())?
+            }
+        }
+    }
+}
+
+impl RawReader for ConsoleRawReader {
+    fn wait_for_input(&mut self, single_esc_abort: bool) -> Result<Event> {
+        match self.pipe_reader {
+            Some(_) => self.select(),
+            None => self.next_key(single_esc_abort).map(Event::KeyPress),
         }
     }
 
+    fn next_key(&mut self, _: bool) -> Result<KeyEvent> {
+        read_input(self.conin, std::u32::MAX)
+    }
+
     fn read_pasted_text(&mut self) -> Result<String> {
         Ok(clipboard_win::get_clipboard_string()?)
     }
@@ -228,6 +166,124 @@ impl RawReader for ConsoleRawReader {
     }
 }
 
+fn read_input(handle: HANDLE, max_count: u32) -> Result<KeyEvent> {
+    use std::char::decode_utf16;
+    use winapi::um::wincon::{
+        LEFT_ALT_PRESSED, LEFT_CTRL_PRESSED, RIGHT_ALT_PRESSED, RIGHT_CTRL_PRESSED, SHIFT_PRESSED,
+    };
+
+    let mut rec: wincon::INPUT_RECORD = unsafe { mem::zeroed() };
+    let mut count = 0;
+    let mut total = 0;
+    let mut surrogate = 0;
+    loop {
+        if total >= max_count {
+            return Ok(KeyEvent(K::UnknownEscSeq, M::NONE));
+        }
+        // TODO GetNumberOfConsoleInputEvents
+        check(unsafe { consoleapi::ReadConsoleInputW(handle, &mut rec, 1, &mut count) })?;
+        total += count;
+
+        if rec.EventType == wincon::WINDOW_BUFFER_SIZE_EVENT {
+            SIGWINCH.store(true, Ordering::SeqCst);
+            debug!(target: "rustyline", "SIGWINCH");
+            return Err(error::ReadlineError::WindowResize); // sigwinch +
+                                                            // err => err
+                                                            // ignored
+        } else if rec.EventType != wincon::KEY_EVENT {
+            continue;
+        }
+        let key_event = unsafe { rec.Event.KeyEvent() };
+        // writeln!(io::stderr(), "key_event: {:?}", key_event).unwrap();
+        if key_event.bKeyDown == 0 && key_event.wVirtualKeyCode != winuser::VK_MENU as WORD {
+            continue;
+        }
+        // key_event.wRepeatCount seems to be always set to 1 (maybe because we only
+        // read one character at a time)
+
+        let alt_gr = key_event.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED)
+            == (LEFT_CTRL_PRESSED | RIGHT_ALT_PRESSED);
+        let mut mods = M::NONE;
+        if !alt_gr && key_event.dwControlKeyState & (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED) != 0 {
+            mods |= M::CTRL;
+        }
+        if !alt_gr && key_event.dwControlKeyState & (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED) != 0 {
+            mods |= M::ALT;
+        }
+        if key_event.dwControlKeyState & SHIFT_PRESSED != 0 {
+            mods |= M::SHIFT;
+        }
+
+        let utf16 = unsafe { *key_event.uChar.UnicodeChar() };
+        let key_code = match i32::from(key_event.wVirtualKeyCode) {
+            winuser::VK_LEFT => K::Left,
+            winuser::VK_RIGHT => K::Right,
+            winuser::VK_UP => K::Up,
+            winuser::VK_DOWN => K::Down,
+            winuser::VK_DELETE => K::Delete,
+            winuser::VK_HOME => K::Home,
+            winuser::VK_END => K::End,
+            winuser::VK_PRIOR => K::PageUp,
+            winuser::VK_NEXT => K::PageDown,
+            winuser::VK_INSERT => K::Insert,
+            winuser::VK_F1 => K::F(1),
+            winuser::VK_F2 => K::F(2),
+            winuser::VK_F3 => K::F(3),
+            winuser::VK_F4 => K::F(4),
+            winuser::VK_F5 => K::F(5),
+            winuser::VK_F6 => K::F(6),
+            winuser::VK_F7 => K::F(7),
+            winuser::VK_F8 => K::F(8),
+            winuser::VK_F9 => K::F(9),
+            winuser::VK_F10 => K::F(10),
+            winuser::VK_F11 => K::F(11),
+            winuser::VK_F12 => K::F(12),
+            winuser::VK_BACK => K::Backspace, // vs Ctrl-h
+            winuser::VK_RETURN => K::Enter,   // vs Ctrl-m
+            winuser::VK_ESCAPE => K::Esc,
+            winuser::VK_TAB => {
+                if mods.contains(M::SHIFT) {
+                    mods.remove(M::SHIFT);
+                    K::BackTab
+                } else {
+                    K::Tab // vs Ctrl-i
+                }
+            }
+            _ => {
+                if utf16 == 0 {
+                    continue;
+                }
+                K::UnknownEscSeq
+            }
+        };
+
+        let key = if key_code != K::UnknownEscSeq {
+            KeyEvent(key_code, mods)
+        } else if utf16 == 27 {
+            KeyEvent(K::Esc, mods) // FIXME dead code ?
+        } else {
+            if (0xD800..0xDC00).contains(&utf16) {
+                surrogate = utf16;
+                continue;
+            }
+            let orc = if surrogate == 0 {
+                decode_utf16(Some(utf16)).next()
+            } else {
+                decode_utf16([surrogate, utf16].iter().copied()).next()
+            };
+            let rc = if let Some(rc) = orc {
+                rc
+            } else {
+                return Err(error::ReadlineError::Eof);
+            };
+            let c = rc?;
+            KeyEvent::new(c, mods)
+        };
+        debug!(target: "rustyline", "wVirtualKeyCode: {:#x}, utf16: {:#x}, dwControlKeyState: {:#x} => key: {:?}", key_event.wVirtualKeyCode, utf16, key_event.dwControlKeyState,key);
+        return Ok(key);
+    }
+}
+
 pub struct ConsoleRenderer {
     conout: HANDLE,
     cols: usize, // Number of columns in terminal
@@ -272,9 +328,9 @@ impl ConsoleRenderer {
         check(unsafe {
             wincon::FillConsoleOutputCharacterA(self.conout, ' ' as CHAR, length, pos, &mut _count)
         })?;
-        check(unsafe {
+        Ok(check(unsafe {
             wincon::FillConsoleOutputAttribute(self.conout, attr, length, pos, &mut _count)
-        })
+        })?)
     }
 
     fn set_cursor_visible(&mut self, visible: BOOL) -> Result<()> {
@@ -328,7 +384,9 @@ fn set_cursor_visible(handle: HANDLE, visible: BOOL) -> Result<()> {
         return Ok(());
     }
     info.bVisible = visible;
-    check(unsafe { wincon::SetConsoleCursorInfo(handle, &info) })
+    Ok(check(unsafe {
+        wincon::SetConsoleCursorInfo(handle, &info)
+    })?)
 }
 
 impl Renderer for ConsoleRenderer {
@@ -450,6 +508,11 @@ impl Renderer for ConsoleRenderer {
         self.clear(n, coord, info.wAttributes)
     }
 
+    fn clear_rows(&mut self, layout: &Layout) -> Result<()> {
+        let info = self.get_console_screen_buffer_info()?;
+        self.clear_old_rows(&info, layout)
+    }
+
     fn sigwinch(&self) -> bool {
         SIGWINCH
             .compare_exchange(true, false, Ordering::SeqCst, Ordering::SeqCst)
@@ -550,6 +613,11 @@ pub struct Console {
     pub(crate) color_mode: ColorMode,
     ansi_colors_supported: bool,
     bell_style: BellStyle,
+    raw_mode: Arc<AtomicBool>,
+    // external print reader
+    pipe_reader: Option<Arc<AsyncPipe>>,
+    // external print writer
+    pipe_writer: Option<SyncSender<String>>,
 }
 
 impl Console {
@@ -564,6 +632,7 @@ impl Console {
 }
 
 impl Term for Console {
+    type ExternalPrinter = ExternalPrinter;
     type KeyMap = ConsoleKeyMap;
     type Mode = ConsoleMode;
     type Reader = ConsoleRawReader;
@@ -625,6 +694,9 @@ impl Term for Console {
             color_mode,
             ansi_colors_supported: false,
             bell_style,
+            raw_mode: Arc::new(AtomicBool::new(false)),
+            pipe_reader: None,
+            pipe_writer: None,
         }
     }
 
@@ -701,19 +773,27 @@ impl Term for Console {
             None
         };
 
+        self.raw_mode.store(true, Ordering::SeqCst);
+        // when all ExternalPrinter are dropped there is no need to use `pipe_reader`
+        if Arc::strong_count(&self.raw_mode) == 1 {
+            self.pipe_writer = None;
+            self.pipe_reader = None;
+        }
+
         Ok((
             ConsoleMode {
                 original_conin_mode,
                 conin: self.conin,
                 original_conout_mode,
                 conout: self.conout,
+                raw_mode: self.raw_mode.clone(),
             },
             (),
         ))
     }
 
     fn create_reader(&self, _: &Config, _: ConsoleKeyMap) -> ConsoleRawReader {
-        ConsoleRawReader::create(self.conin)
+        ConsoleRawReader::create(self.conin, self.pipe_reader.clone())
     }
 
     fn create_writer(&self) -> ConsoleRenderer {
@@ -723,6 +803,38 @@ impl Term for Console {
     fn writeln(&self) -> Result<()> {
         write_all(self.conout, &[10; 1])
     }
+
+    fn create_external_printer(&mut self) -> Result<ExternalPrinter> {
+        if let Some(ref sender) = self.pipe_writer {
+            return Ok(ExternalPrinter {
+                event: self.pipe_reader.as_ref().unwrap().event.0,
+                sender: sender.clone(),
+                raw_mode: self.raw_mode.clone(),
+                conout: self.conout,
+            });
+        }
+        if !self.is_input_tty() || !self.is_output_tty() {
+            Err(io::Error::from(io::ErrorKind::Other))?; // FIXME
+        }
+        let event = unsafe { CreateEventW(ptr::null_mut(), TRUE, FALSE, ptr::null()) };
+        if event.is_null() {
+            Err(io::Error::last_os_error())?;
+        }
+        let (sender, receiver) = sync_channel(1);
+
+        let reader = Arc::new(AsyncPipe {
+            event: Handle(event),
+            receiver,
+        });
+        self.pipe_reader.replace(reader);
+        self.pipe_writer.replace(sender.clone());
+        Ok(ExternalPrinter {
+            event,
+            sender,
+            raw_mode: self.raw_mode.clone(),
+            conout: self.conout,
+        })
+    }
 }
 
 impl Drop for Console {
@@ -737,6 +849,50 @@ impl Drop for Console {
 unsafe impl Send for Console {}
 unsafe impl Sync for Console {}
 
+#[derive(Debug)]
+struct AsyncPipe {
+    event: Handle,
+    receiver: Receiver<String>,
+}
+
+#[derive(Debug)]
+pub struct ExternalPrinter {
+    event: HANDLE,
+    sender: SyncSender<String>,
+    raw_mode: Arc<AtomicBool>,
+    conout: HANDLE,
+}
+
+unsafe impl Send for ExternalPrinter {}
+unsafe impl Sync for ExternalPrinter {}
+
+impl super::ExternalPrinter for ExternalPrinter {
+    fn print(&mut self, msg: String) -> Result<()> {
+        // write directly to stdout/stderr while not in raw mode
+        if !self.raw_mode.load(Ordering::SeqCst) {
+            let mut utf16 = Vec::new();
+            write_to_console(self.conout, msg.as_str(), &mut utf16)
+        } else {
+            self.sender
+                .send(msg)
+                .map_err(|_| io::Error::from(io::ErrorKind::Other))?; // FIXME
+            Ok(check(unsafe { SetEvent(self.event) })?)
+        }
+    }
+}
+
+#[derive(Debug)]
+struct Handle(HANDLE);
+
+unsafe impl Send for Handle {}
+unsafe impl Sync for Handle {}
+
+impl Drop for Handle {
+    fn drop(&mut self) {
+        unsafe { CloseHandle(self.0) };
+    }
+}
+
 #[cfg(test)]
 mod test {
     use super::Console;


Test patch:
diff --git a/src/test/mod.rs b/src/test/mod.rs
index 1575d08..72f95e8 100644
--- a/src/test/mod.rs
+++ b/src/test/mod.rs
@@ -54,7 +54,7 @@ impl Validator for SimpleCompleter {}
 
 #[test]
 fn complete_line() {
-    let mut out = Sink::new();
+    let mut out = Sink::default();
     let history = crate::history::History::new();
     let helper = Some(SimpleCompleter);
     let mut s = init_state(&mut out, "rus", 3, helper.as_ref(), &history);
diff --git a/src/tty/test.rs b/src/tty/test.rs
index cf67656..50eecbf 100644
--- a/src/tty/test.rs
+++ b/src/tty/test.rs
@@ -3,7 +3,7 @@ use std::iter::IntoIterator;
 use std::slice::Iter;
 use std::vec::IntoIter;
 
-use super::{RawMode, RawReader, Renderer, Term};
+use super::{Event, ExternalPrinter, RawMode, RawReader, Renderer, Term};
 use crate::config::{Behavior, BellStyle, ColorMode, Config};
 use crate::error::ReadlineError;
 use crate::highlight::Highlighter;
@@ -22,6 +22,10 @@ impl RawMode for Mode {
 }
 
 impl<'a> RawReader for Iter<'a, KeyEvent> {
+    fn wait_for_input(&mut self, single_esc_abort: bool) -> Result<Event> {
+        self.next_key(single_esc_abort).map(Event::KeyPress)
+    }
+
     fn next_key(&mut self, _: bool) -> Result<KeyEvent> {
         match self.next() {
             Some(key) => Ok(*key),
@@ -44,6 +48,10 @@ impl<'a> RawReader for Iter<'a, KeyEvent> {
 }
 
 impl RawReader for IntoIter<KeyEvent> {
+    fn wait_for_input(&mut self, single_esc_abort: bool) -> Result<Event> {
+        self.next_key(single_esc_abort).map(Event::KeyPress)
+    }
+
     fn next_key(&mut self, _: bool) -> Result<KeyEvent> {
         match self.next() {
             Some(key) => Ok(key),
@@ -70,14 +78,9 @@ impl RawReader for IntoIter<KeyEvent> {
     }
 }
 
+#[derive(Default)]
 pub struct Sink {}
 
-impl Sink {
-    pub fn new() -> Sink {
-        Sink {}
-    }
-}
-
 impl Renderer for Sink {
     type Reader = IntoIter<KeyEvent>;
 
@@ -115,6 +118,10 @@ impl Renderer for Sink {
         Ok(())
     }
 
+    fn clear_rows(&mut self, _: &Layout) -> Result<()> {
+        Ok(())
+    }
+
     fn sigwinch(&self) -> bool {
         false
     }
@@ -138,6 +145,14 @@ impl Renderer for Sink {
     }
 }
 
+pub struct DummyExternalPrinter {}
+
+impl ExternalPrinter for DummyExternalPrinter {
+    fn print(&mut self, _msg: String) -> Result<()> {
+        Ok(())
+    }
+}
+
 pub type Terminal = DummyTerminal;
 
 #[derive(Clone, Debug)]
@@ -149,6 +164,7 @@ pub struct DummyTerminal {
 }
 
 impl Term for DummyTerminal {
+    type ExternalPrinter = DummyExternalPrinter;
     type KeyMap = KeyMap;
     type Mode = Mode;
     type Reader = IntoIter<KeyEvent>;
@@ -200,7 +216,11 @@ impl Term for DummyTerminal {
     }
 
     fn create_writer(&self) -> Sink {
-        Sink::new()
+        Sink::default()
+    }
+
+    fn create_external_printer(&mut self) -> Result<DummyExternalPrinter> {
+        Ok(DummyExternalPrinter {})
     }
 
     fn writeln(&self) -> Result<()> {